From 1ff1b7c1dd103b95da0c805f06901c8104722473 Mon Sep 17 00:00:00 2001 From: Seraphym-Tuhai Date: Mon, 3 Apr 2023 17:23:08 +0300 Subject: [PATCH 01/61] assign, reassign tests --- .../server/msa/TestRestClient.java | 41 ++++++++++- .../server/msa/ui/pages/AlarmElements.java | 26 +++++++ .../server/msa/ui/pages/AlarmHelper.java | 14 ++++ .../msa/ui/pages/DevicePageElements.java | 21 ++++++ .../server/msa/ui/pages/DevicePageHelper.java | 14 ++++ .../msa/ui/pages/SideBarMenuViewElements.java | 15 ++++ .../msa/ui/pages/SideBarMenuViewHelper.java | 20 +++++ .../server/msa/ui/tests/AssignDetailsTab.java | 73 +++++++++++++++++++ .../msa/ui/utils/DataProviderCredential.java | 7 ++ .../server/msa/ui/utils/EntityPrototypes.java | 32 ++++++++ 10 files changed, 261 insertions(+), 2 deletions(-) create mode 100644 msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmElements.java create mode 100644 msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmHelper.java create mode 100644 msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageElements.java create mode 100644 msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageHelper.java create mode 100644 msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/AssignDetailsTab.java diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/TestRestClient.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/TestRestClient.java index a030f3a8c9..a014dbee07 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/TestRestClient.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/TestRestClient.java @@ -29,13 +29,17 @@ import io.restassured.specification.RequestSpecification; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.asset.AssetProfile; +import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.AssetProfileId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.relation.EntityRelation; @@ -178,7 +182,7 @@ public class TestRestClient { } public JsonPath postProvisionRequest(String provisionRequest) { - return given().spec(requestSpec) + return given().spec(requestSpec) .body(provisionRequest) .post("/api/v1/provision") .getBody() @@ -293,7 +297,7 @@ public class TestRestClient { } public DeviceProfile getDeviceProfileById(DeviceProfileId deviceProfileId) { - return given().spec(requestSpec).get("/api/deviceProfile/{deviceProfileId}", deviceProfileId.getId()) + return given().spec(requestSpec).get("/api/deviceProfile/{deviceProfileId}", deviceProfileId.getId()) .then() .assertThat() .statusCode(HTTP_OK) @@ -388,6 +392,39 @@ public class TestRestClient { }); } + public Alarm postAlarm(Alarm alarm) { + return given().spec(requestSpec) + .body(alarm) + .post("/api/alarm") + .then() + .statusCode(HTTP_OK) + .extract() + .as(Alarm.class); + } + + public void deleteAlarm(AlarmId alarmId) { + given().spec(requestSpec) + .delete("/api/alarm/{alarmId}", alarmId.getId()) + .then() + .statusCode(HTTP_OK); + } + + public User postUser(User user) { + return given().spec(requestSpec) + .body(user) + .post("/api/user?sendActivationMail=false") + .then() + .statusCode(HTTP_OK) + .extract() + .as(User.class); + } + + public void deleteUser(UserId userId) { + given().spec(requestSpec) + .delete("/api/user/{userId}", userId.getId()) + .then() + .statusCode(HTTP_OK); + } public String getToken() { return token; } diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmElements.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmElements.java new file mode 100644 index 0000000000..d3528b0789 --- /dev/null +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmElements.java @@ -0,0 +1,26 @@ +package org.thingsboard.server.msa.ui.pages; + +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; + +public class AlarmElements extends OtherPageElements{ + public AlarmElements(WebDriver driver) { + super(driver); + } + + private static final String ASSIGN_BTN = "//mat-icon[contains(text(),'keyboard_arrow_down')]/parent::button"; + private static final String USER_ASSIGN_DROPDOWN = "//div[contains(@class,'tb-assignee')]//span[contains(text(),'%s')]"; + private static final String ASSIGN_USER_DISPLAY_NAME = "//span[text()='%s']/ancestor::mat-row//span[@class='user-display-name']"; + + public WebElement assignBtn() { + return waitUntilElementToBeClickable(ASSIGN_BTN); + } + + public WebElement userFromAssignDropDown(String userEmail) { + return waitUntilElementToBeClickable(String.format(USER_ASSIGN_DROPDOWN, userEmail)); + } + + public WebElement assignUserDisplayName(String userEmail) { + return waitUntilElementToBeClickable(String.format(ASSIGN_USER_DISPLAY_NAME, userEmail)); + } +} diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmHelper.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmHelper.java new file mode 100644 index 0000000000..bef84d98ee --- /dev/null +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmHelper.java @@ -0,0 +1,14 @@ +package org.thingsboard.server.msa.ui.pages; + +import org.openqa.selenium.WebDriver; + +public class AlarmHelper extends AlarmElements { + public AlarmHelper(WebDriver driver) { + super(driver); + } + + public void assignTo(String user) { + jsClick(assignBtn()); + userFromAssignDropDown(user).click(); + } +} diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageElements.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageElements.java new file mode 100644 index 0000000000..411e5545ea --- /dev/null +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageElements.java @@ -0,0 +1,21 @@ +package org.thingsboard.server.msa.ui.pages; + +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; + +public class DevicePageElements extends OtherPageElements{ + public DevicePageElements(WebDriver driver) { + super(driver); + } + + private static final String DEVICE = "//table//span[text()='%s']"; + private static final String DEVICE_DETAILS_ALARMS = "//tb-details-panel//span[text()='Alarms']"; + + public WebElement device(String deviceName) { + return waitUntilElementToBeClickable(String.format(DEVICE, deviceName)); + } + + public WebElement deviceDetailsAlarmsBtn() { + return waitUntilElementToBeClickable(DEVICE_DETAILS_ALARMS); + } +} diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageHelper.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageHelper.java new file mode 100644 index 0000000000..0377b6e207 --- /dev/null +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageHelper.java @@ -0,0 +1,14 @@ +package org.thingsboard.server.msa.ui.pages; + +import org.openqa.selenium.WebDriver; + +public class DevicePageHelper extends DevicePageElements{ + public DevicePageHelper(WebDriver driver) { + super(driver); + } + + public void openDeviceAlarms(String deviceName) { + device(deviceName).click(); + deviceDetailsAlarmsBtn().click(); + } +} diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/SideBarMenuViewElements.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/SideBarMenuViewElements.java index ab8ee9bf55..5ecf27b5bd 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/SideBarMenuViewElements.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/SideBarMenuViewElements.java @@ -30,6 +30,13 @@ public class SideBarMenuViewElements extends AbstractBasePage { private static final String PROFILES_BTN = "//mat-toolbar//mat-icon[text()='badge']/ancestor::tb-menu-toggle"; private static final String DEVICE_PROFILE_BTN = "//mat-toolbar//a[@href='/profiles/deviceProfiles']"; private static final String ASSET_PROFILE_BTN = "//mat-toolbar//a[@href='/profiles/assetProfiles']"; + private static final String ALARMS_BTN = "//mat-toolbar//a[@href='/alarms']"; + private static final String ENTITIES_DROPDOWN = "//mat-toolbar//mat-icon[text()='category']/ancestor::a//span[contains(@class,'pull-right')]"; + private static final String DEVICES_BTN = "//ul[@id='docs-menu-entity.entities']//span[text()='Devices']"; + + public WebElement entitiesDropdown() { + return waitUntilElementToBeClickable(ENTITIES_DROPDOWN); + } public WebElement ruleChainsBtn() { return waitUntilElementToBeClickable(RULE_CHAINS_BTN); @@ -54,4 +61,12 @@ public class SideBarMenuViewElements extends AbstractBasePage { public WebElement assetProfileBtn() { return waitUntilElementToBeClickable(ASSET_PROFILE_BTN); } + + public WebElement alarmsBtn() { + return waitUntilElementToBeClickable(ALARMS_BTN); + } + + public WebElement devicesBtn() { + return waitUntilElementToBeClickable(DEVICES_BTN); + } } \ No newline at end of file diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/SideBarMenuViewHelper.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/SideBarMenuViewHelper.java index 4d56689809..92848ff68c 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/SideBarMenuViewHelper.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/SideBarMenuViewHelper.java @@ -16,6 +16,7 @@ package org.thingsboard.server.msa.ui.pages; import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; public class SideBarMenuViewHelper extends SideBarMenuViewElements { public SideBarMenuViewHelper(WebDriver driver) { @@ -31,4 +32,23 @@ public class SideBarMenuViewHelper extends SideBarMenuViewElements { profilesBtn().click(); assetProfileBtn().click(); } + + public void goToDevicesPage() { + openEntitiesDropdown(); + devicesBtn().click(); + } + + public void openEntitiesDropdown() { + if (entitiesDropdownIsClose()) { + entitiesDropdown().click(); + } + } + + public boolean entitiesDropdownIsClose() { + return dropdownIsClose(entitiesDropdown()); + } + + private boolean dropdownIsClose(WebElement dropdown) { + return !dropdown.getAttribute("class").contains("tb-toggled"); + } } \ No newline at end of file diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/AssignDetailsTab.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/AssignDetailsTab.java new file mode 100644 index 0000000000..420fe17a86 --- /dev/null +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/AssignDetailsTab.java @@ -0,0 +1,73 @@ +package org.thingsboard.server.msa.ui.tests; + +import org.testng.Assert; +import org.testng.annotations.AfterClass; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; +import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.msa.prototypes.DevicePrototypes; +import org.thingsboard.server.msa.ui.base.AbstractDriverBaseTest; +import org.thingsboard.server.msa.ui.pages.AlarmHelper; +import org.thingsboard.server.msa.ui.pages.DevicePageHelper; +import org.thingsboard.server.msa.ui.pages.LoginPageHelper; +import org.thingsboard.server.msa.ui.pages.SideBarMenuViewHelper; +import org.thingsboard.server.msa.ui.utils.DataProviderCredential; +import org.thingsboard.server.msa.ui.utils.EntityPrototypes; + +public class AssignDetailsTab extends AbstractDriverBaseTest { + + AlarmId alarmId; + DeviceId deviceId; + UserId userId; + String deviceName; + SideBarMenuViewHelper sideBarMenuView; + AlarmHelper alarmPage; + DevicePageHelper devicePage; + + @BeforeClass + public void generateAlarm() { + new LoginPageHelper(driver).authorizationTenant(); + sideBarMenuView = new SideBarMenuViewHelper(driver); + alarmPage = new AlarmHelper(driver); + devicePage = new DevicePageHelper(driver); + } + + @AfterMethod + public void deleteAlarm() { + testRestClient.deleteAlarm(alarmId); + testRestClient.deleteDevice(deviceId); + if (userId != null) { + testRestClient.deleteUser(userId); + } + } + + @Test(dataProviderClass = DataProviderCredential.class, dataProvider = "assignTo") + public void assignAlarmTo(String user) { + deviceName = testRestClient.postDevice("", DevicePrototypes.defaultDevicePrototype("")).getName(); + deviceId = testRestClient.getDeviceByName(deviceName).getId(); + alarmId = testRestClient.postAlarm(EntityPrototypes.defaultAlarm(deviceId)).getId(); + + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(deviceName); + alarmPage.assignTo(user); + + Assert.assertTrue(alarmPage.assignUserDisplayName(user).isDisplayed()); + } + + @Test + public void reassignAlarm() { + userId = testRestClient.postUser(EntityPrototypes.defaultUser(getCustomerByName("Customer A").getId())).getId(); + deviceName = testRestClient.postDevice("", DevicePrototypes.defaultDevicePrototype("")).getName(); + deviceId = testRestClient.getDeviceByName(deviceName).getId(); + alarmId = testRestClient.postAlarm(EntityPrototypes.defaultAlarm(deviceId, userId)).getId(); + + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(deviceName); + alarmPage.assignTo("customer@thingsboard.org"); + + Assert.assertTrue(alarmPage.assignUserDisplayName("customer@thingsboard.org").isDisplayed()); + } +} diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/utils/DataProviderCredential.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/utils/DataProviderCredential.java index 73783544a1..a0188665c8 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/utils/DataProviderCredential.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/utils/DataProviderCredential.java @@ -149,4 +149,11 @@ public class DataProviderCredential { {description, newDescription, description + newDescription}, {description, Keys.CONTROL + "A" + Keys.BACK_SPACE, ""}}; } + + @DataProvider + public static Object[][] assignTo() { + return new Object[][]{ + {Const.TENANT_EMAIL}, + {"customer@thingsboard.org"}}; + } } diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/utils/EntityPrototypes.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/utils/EntityPrototypes.java index c131c0fb25..84f28bb6f2 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/utils/EntityPrototypes.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/utils/EntityPrototypes.java @@ -21,12 +21,19 @@ import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfileProvisionType; import org.thingsboard.server.common.data.DeviceProfileType; import org.thingsboard.server.common.data.DeviceTransportType; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.device.profile.DeviceProfileData; import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfileProvisionConfiguration; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.data.security.Authority; public class EntityPrototypes { @@ -97,4 +104,29 @@ public class EntityPrototypes { assetProfile.setDescription(description); return assetProfile; } + + public static Alarm defaultAlarm(EntityId id) { + Alarm alarm = new Alarm(); + alarm.setType("default"); + alarm.setOriginator(id); + alarm.setSeverity(AlarmSeverity.CRITICAL); + return alarm; + } + + public static Alarm defaultAlarm(EntityId id, UserId userId) { + Alarm alarm = new Alarm(); + alarm.setType("default"); + alarm.setOriginator(id); + alarm.setSeverity(AlarmSeverity.CRITICAL); + alarm.setAssigneeId(userId); + return alarm; + } + + public static User defaultUser(CustomerId customerId) { + User user = new User(); + user.setEmail("test@thingsboard.org"); + user.setCustomerId(customerId); + user.setAuthority(Authority.CUSTOMER_USER); + return user; + } } From 32c6b7385f1fa9cde915a3d3338a39483a51401e Mon Sep 17 00:00:00 2001 From: Seraphym-Tuhai Date: Mon, 10 Apr 2023 09:07:22 +0300 Subject: [PATCH 02/61] add tests on search --- .../server/msa/ui/pages/AlarmElements.java | 18 ++++++-- .../server/msa/ui/pages/AlarmHelper.java | 18 ++++++++ .../msa/ui/pages/DevicePageElements.java | 7 ++- .../server/msa/ui/pages/DevicePageHelper.java | 6 ++- .../server/msa/ui/tests/AssignDetailsTab.java | 45 +++++++++++++++++-- .../server/msa/ui/utils/EntityPrototypes.java | 9 ++++ 6 files changed, 93 insertions(+), 10 deletions(-) diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmElements.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmElements.java index d3528b0789..48300429f6 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmElements.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmElements.java @@ -3,14 +3,18 @@ package org.thingsboard.server.msa.ui.pages; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; +import java.util.List; + public class AlarmElements extends OtherPageElements{ public AlarmElements(WebDriver driver) { super(driver); } private static final String ASSIGN_BTN = "//mat-icon[contains(text(),'keyboard_arrow_down')]/parent::button"; - private static final String USER_ASSIGN_DROPDOWN = "//div[contains(@class,'tb-assignee')]//span[contains(text(),'%s')]"; - private static final String ASSIGN_USER_DISPLAY_NAME = "//span[text()='%s']/ancestor::mat-row//span[@class='user-display-name']"; + private static final String USER_ASSIGN_DROPDOWN = "//div[@class='user-display-name']/span[contains(text(),'%s')]"; + private static final String ASSIGN_USERS_DISPLAY_NAME = "//div[@class='user-display-name']/span"; + private static final String ASSIGN_USER_DISPLAY_NAME = "//span[@class='user-display-name'][contains(text(),'%s')]"; + private static final String SEARCH_FIELD = "//input[@placeholder='Search users']"; public WebElement assignBtn() { return waitUntilElementToBeClickable(ASSIGN_BTN); @@ -20,7 +24,15 @@ public class AlarmElements extends OtherPageElements{ return waitUntilElementToBeClickable(String.format(USER_ASSIGN_DROPDOWN, userEmail)); } - public WebElement assignUserDisplayName(String userEmail) { + public WebElement assignedUser(String userEmail) { return waitUntilElementToBeClickable(String.format(ASSIGN_USER_DISPLAY_NAME, userEmail)); } + + public List assignUsers() { + return waitUntilElementsToBeClickable(ASSIGN_USERS_DISPLAY_NAME); + } + + public WebElement searchUserField() { + return waitUntilElementToBeClickable(SEARCH_FIELD); + } } diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmHelper.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmHelper.java index bef84d98ee..4075a89ad1 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmHelper.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmHelper.java @@ -1,6 +1,11 @@ package org.thingsboard.server.msa.ui.pages; import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; public class AlarmHelper extends AlarmElements { public AlarmHelper(WebDriver driver) { @@ -11,4 +16,17 @@ public class AlarmHelper extends AlarmElements { jsClick(assignBtn()); userFromAssignDropDown(user).click(); } + + private List users; + + public void setUsers() { + users = assignUsers() + .stream() + .map(WebElement::getText) + .collect(Collectors.toList()); + } + + public List getUsers() { + return users; + } } diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageElements.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageElements.java index 411e5545ea..ba766ef3fd 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageElements.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageElements.java @@ -9,7 +9,8 @@ public class DevicePageElements extends OtherPageElements{ } private static final String DEVICE = "//table//span[text()='%s']"; - private static final String DEVICE_DETAILS_ALARMS = "//tb-details-panel//span[text()='Alarms']"; + private static final String DEVICE_DETAILS_VIEW = "//tb-details-panel"; + private static final String DEVICE_DETAILS_ALARMS = DEVICE_DETAILS_VIEW + "//span[text()='Alarms']"; public WebElement device(String deviceName) { return waitUntilElementToBeClickable(String.format(DEVICE, deviceName)); @@ -18,4 +19,8 @@ public class DevicePageElements extends OtherPageElements{ public WebElement deviceDetailsAlarmsBtn() { return waitUntilElementToBeClickable(DEVICE_DETAILS_ALARMS); } + + public WebElement deviceDetailsView() { + return waitUntilPresenceOfElementLocated(DEVICE_DETAILS_VIEW); + } } diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageHelper.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageHelper.java index 0377b6e207..19716d3fb6 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageHelper.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageHelper.java @@ -2,13 +2,15 @@ package org.thingsboard.server.msa.ui.pages; import org.openqa.selenium.WebDriver; -public class DevicePageHelper extends DevicePageElements{ +public class DevicePageHelper extends DevicePageElements { public DevicePageHelper(WebDriver driver) { super(driver); } public void openDeviceAlarms(String deviceName) { - device(deviceName).click(); + if (!deviceDetailsView().isDisplayed()) { + device(deviceName).click(); + } deviceDetailsAlarmsBtn().click(); } } diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/AssignDetailsTab.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/AssignDetailsTab.java index 420fe17a86..cdf10356b3 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/AssignDetailsTab.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/AssignDetailsTab.java @@ -1,7 +1,6 @@ package org.thingsboard.server.msa.ui.tests; import org.testng.Assert; -import org.testng.annotations.AfterClass; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -17,6 +16,8 @@ import org.thingsboard.server.msa.ui.pages.SideBarMenuViewHelper; import org.thingsboard.server.msa.ui.utils.DataProviderCredential; import org.thingsboard.server.msa.ui.utils.EntityPrototypes; +import static org.assertj.core.api.Assertions.assertThat; + public class AssignDetailsTab extends AbstractDriverBaseTest { AlarmId alarmId; @@ -54,12 +55,13 @@ public class AssignDetailsTab extends AbstractDriverBaseTest { devicePage.openDeviceAlarms(deviceName); alarmPage.assignTo(user); - Assert.assertTrue(alarmPage.assignUserDisplayName(user).isDisplayed()); + Assert.assertTrue(alarmPage.assignedUser(user).isDisplayed()); } @Test public void reassignAlarm() { - userId = testRestClient.postUser(EntityPrototypes.defaultUser(getCustomerByName("Customer A").getId())).getId(); + testRestClient.postCustomer(EntityPrototypes.defaultCustomerPrototype("TestCustomer")); + userId = testRestClient.postUser(EntityPrototypes.defaultUser(getCustomerByName("TestCustomer").getId())).getId(); deviceName = testRestClient.postDevice("", DevicePrototypes.defaultDevicePrototype("")).getName(); deviceId = testRestClient.getDeviceByName(deviceName).getId(); alarmId = testRestClient.postAlarm(EntityPrototypes.defaultAlarm(deviceId, userId)).getId(); @@ -68,6 +70,41 @@ public class AssignDetailsTab extends AbstractDriverBaseTest { devicePage.openDeviceAlarms(deviceName); alarmPage.assignTo("customer@thingsboard.org"); - Assert.assertTrue(alarmPage.assignUserDisplayName("customer@thingsboard.org").isDisplayed()); + Assert.assertTrue(alarmPage.assignedUser("customer@thingsboard.org").isDisplayed()); + } + + @Test + public void searchByEmail() { + deviceName = testRestClient.postDevice("", DevicePrototypes.defaultDevicePrototype("")).getName(); + deviceId = testRestClient.getDeviceByName(deviceName).getId(); + alarmId = testRestClient.postAlarm(EntityPrototypes.defaultAlarm(deviceId)).getId(); + + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(deviceName); + alarmPage.assignBtn().click(); + alarmPage.searchUserField().sendKeys("customer@thingsboard.org"); + alarmPage.setUsers(); + + assertThat(alarmPage.getUsers()).contains("customer@thingsboard.org"); + alarmPage.assignUsers().forEach(u -> assertThat(u.isDisplayed()).isTrue()); + } + + @Test + public void searchByName() { + String name = "usik"; + + userId = testRestClient.postUser(EntityPrototypes.defaultUser(getCustomerByName("Customer A").getId(), name)).getId(); + deviceName = testRestClient.postDevice("", DevicePrototypes.defaultDevicePrototype("")).getName(); + deviceId = testRestClient.getDeviceByName(deviceName).getId(); + alarmId = testRestClient.postAlarm(EntityPrototypes.defaultAlarm(deviceId)).getId(); + + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(deviceName); + alarmPage.assignBtn().click(); + alarmPage.searchUserField().sendKeys(name); + alarmPage.setUsers(); + + assertThat(alarmPage.getUsers()).contains(name); + alarmPage.assignUsers().forEach(u -> assertThat(u.isDisplayed()).isTrue()); } } diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/utils/EntityPrototypes.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/utils/EntityPrototypes.java index 84f28bb6f2..bce4373191 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/utils/EntityPrototypes.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/utils/EntityPrototypes.java @@ -129,4 +129,13 @@ public class EntityPrototypes { user.setAuthority(Authority.CUSTOMER_USER); return user; } + + public static User defaultUser(CustomerId customerId, String name) { + User user = new User(); + user.setEmail("test@thingsboard.org"); + user.setFirstName(name); + user.setCustomerId(customerId); + user.setAuthority(Authority.CUSTOMER_USER); + return user; + } } From 982f6e23fb307347dd4979f4206f2ac3fc1b897f Mon Sep 17 00:00:00 2001 From: Seraphym-Tuhai Date: Thu, 13 Apr 2023 10:57:23 +0300 Subject: [PATCH 03/61] add test on propagated alarm --- .../server/msa/TestRestClient.java | 13 ++ .../ui/pages/AlarmDetailsViewElements.java | 32 +++ .../msa/ui/pages/AlarmDetailsViewHelper.java | 19 ++ .../server/msa/ui/pages/AlarmElements.java | 40 +++- .../server/msa/ui/pages/AlarmHelper.java | 36 ++- .../msa/ui/pages/CustomerPageElements.java | 10 +- .../msa/ui/pages/DevicePageElements.java | 15 ++ .../server/msa/ui/pages/DevicePageHelper.java | 15 ++ .../server/msa/ui/pages/LoginPageHelper.java | 14 +- .../server/msa/ui/tests/AssignDetailsTab.java | 110 --------- .../AbstractAssignByCustomerTest.java | 6 + .../ui/tests/assignee/AbstractAssignTest.java | 117 ++++++++++ .../tests/assignee/AssignByCustomerTest.java | 4 + .../ui/tests/assignee/AssignDetailsTab.java | 209 ++++++++++++++++++ .../msa/ui/utils/DataProviderCredential.java | 6 - .../server/msa/ui/utils/EntityPrototypes.java | 55 ++++- 16 files changed, 564 insertions(+), 137 deletions(-) create mode 100644 msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmDetailsViewElements.java create mode 100644 msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmDetailsViewHelper.java delete mode 100644 msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/AssignDetailsTab.java create mode 100644 msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AbstractAssignByCustomerTest.java create mode 100644 msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AbstractAssignTest.java create mode 100644 msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AssignByCustomerTest.java create mode 100644 msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AssignDetailsTab.java diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/TestRestClient.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/TestRestClient.java index a014dbee07..45829e7d1e 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/TestRestClient.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/TestRestClient.java @@ -29,6 +29,7 @@ import io.restassured.specification.RequestSpecification; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.asset.AssetProfile; @@ -432,4 +433,16 @@ public class TestRestClient { public String getRefreshToken() { return refreshToken; } + + public PageData getTenants(PageLink pageLink) { + Map params = new HashMap<>(); + addPageLinkToParam(params, pageLink); + return given().spec(requestSpec).queryParams(params) + .get("/api/tenants") + .then() + .statusCode(HTTP_OK) + .extract() + .as(new TypeRef>() { + }); + } } diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmDetailsViewElements.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmDetailsViewElements.java new file mode 100644 index 0000000000..44f41aff2c --- /dev/null +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmDetailsViewElements.java @@ -0,0 +1,32 @@ +package org.thingsboard.server.msa.ui.pages; + +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.thingsboard.server.msa.ui.base.AbstractBasePage; + +public class AlarmDetailsViewElements extends AbstractBasePage { + public AlarmDetailsViewElements(WebDriver driver) { + super(driver); + } + + private static final String ASSIGN_FIELD = "//mat-label[contains(text(),'Assignee')]/parent::label/following-sibling::input"; + private static final String USER_FROM_DROP_DOWN = "//div[@class='user-display-name']/span[text() = '%s']"; + private static final String CLOSE_VIEW_BTN = "//mat-dialog-container//mat-icon[contains(text(),'close')]/parent::button"; + private static final String UNASSIGNED_BTN = "//div[@role='listbox']//mat-icon[text() = 'account_circle']/following-sibling::span"; + + public WebElement assignField() { + return waitUntilElementToBeClickable(ASSIGN_FIELD); + } + + public WebElement userFromAssignDropdown(String emailOrName) { + return waitUntilElementToBeClickable(String.format(USER_FROM_DROP_DOWN, emailOrName)); + } + + public WebElement closeViewBtn() { + return waitUntilElementToBeClickable(CLOSE_VIEW_BTN); + } + + public WebElement unassignedBtn() { + return waitUntilElementToBeClickable(UNASSIGNED_BTN); + } +} diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmDetailsViewHelper.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmDetailsViewHelper.java new file mode 100644 index 0000000000..b658164b37 --- /dev/null +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmDetailsViewHelper.java @@ -0,0 +1,19 @@ +package org.thingsboard.server.msa.ui.pages; + +import org.openqa.selenium.WebDriver; + +public class AlarmDetailsViewHelper extends AlarmDetailsViewElements{ + public AlarmDetailsViewHelper(WebDriver driver) { + super(driver); + } + + public void assignAlarmTo(String emailOrName) { + assignField().click(); + userFromAssignDropdown(emailOrName).click(); + } + + public void unassignedAlarm() { + assignField().click(); + unassignedBtn().click(); + } +} diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmElements.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmElements.java index 48300429f6..ad3ccb748d 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmElements.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmElements.java @@ -1,3 +1,18 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.thingsboard.server.msa.ui.pages; import org.openqa.selenium.WebDriver; @@ -10,14 +25,17 @@ public class AlarmElements extends OtherPageElements{ super(driver); } - private static final String ASSIGN_BTN = "//mat-icon[contains(text(),'keyboard_arrow_down')]/parent::button"; - private static final String USER_ASSIGN_DROPDOWN = "//div[@class='user-display-name']/span[contains(text(),'%s')]"; - private static final String ASSIGN_USERS_DISPLAY_NAME = "//div[@class='user-display-name']/span"; + private static final String ASSIGN_BTN = "//span[text() = '%s']/ancestor::mat-row//mat-icon[contains(text(),'keyboard_arrow_down')]/parent::button"; + private static final String USER_ASSIGN_DROPDOWN = "//div[@class='user-display-name']/span[text() = '%s']"; + protected static final String ASSIGN_USERS_DISPLAY_NAME = "//div[@class='user-display-name']/span"; private static final String ASSIGN_USER_DISPLAY_NAME = "//span[@class='user-display-name'][contains(text(),'%s')]"; private static final String SEARCH_FIELD = "//input[@placeholder='Search users']"; + private static final String UNASSIGNED_BTN = "//div[@role='listbox']//mat-icon[text() = 'account_circle']/following-sibling::span"; + private static final String UNASSIGNED = "//span[text() = '%s']/ancestor::mat-row//span[@class='assignee-cell']/mat-icon[text() = 'account_circle']/following-sibling::span"; + private static final String ALARM_DETAILS_BTN = "//span[text() = '%s']/ancestor::mat-row//mat-icon[contains(text(),'more_horiz')]/parent::button"; - public WebElement assignBtn() { - return waitUntilElementToBeClickable(ASSIGN_BTN); + public WebElement assignBtn(String type) { + return waitUntilElementToBeClickable(String.format(ASSIGN_BTN, type)); } public WebElement userFromAssignDropDown(String userEmail) { @@ -35,4 +53,16 @@ public class AlarmElements extends OtherPageElements{ public WebElement searchUserField() { return waitUntilElementToBeClickable(SEARCH_FIELD); } + + public WebElement unassignedBtn() { + return waitUntilElementToBeClickable(UNASSIGNED_BTN); + } + + public WebElement unassigned(String alarmType) { + return waitUntilVisibilityOfElementLocated(String.format(UNASSIGNED, alarmType)); + } + + public WebElement alarmDetailsBtn(String alarmType) { + return waitUntilElementToBeClickable(String.format(ALARM_DETAILS_BTN, alarmType)); + } } diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmHelper.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmHelper.java index 4075a89ad1..7abb0006db 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmHelper.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmHelper.java @@ -1,9 +1,24 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.thingsboard.server.msa.ui.pages; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; +import org.thingsboard.server.msa.ui.utils.Const; -import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; @@ -12,11 +27,21 @@ public class AlarmHelper extends AlarmElements { super(driver); } - public void assignTo(String user) { - jsClick(assignBtn()); + public void assignAlarmTo(String alarmType, String user) { + jsClick(assignBtn(alarmType)); userFromAssignDropDown(user).click(); } + public void unassignedAlarm(String alarmType) { + assignBtn(alarmType).click(); + unassignedBtn().click(); + } + + public void searchAlarm(String alarmType, String emailOrName) { + assignBtn(alarmType).click(); + searchUserField().sendKeys(emailOrName); + } + private List users; public void setUsers() { @@ -29,4 +54,9 @@ public class AlarmHelper extends AlarmElements { public List getUsers() { return users; } + + public void assertUsersForAssignIsNotPresent() { + sleep(1); + elementsIsNotPresent(ASSIGN_USERS_DISPLAY_NAME); + } } diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/CustomerPageElements.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/CustomerPageElements.java index 1d69ddd5fc..90f51c4903 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/CustomerPageElements.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/CustomerPageElements.java @@ -32,14 +32,16 @@ public class CustomerPageElements extends OtherPageElementsHelper { private static final String TITLES = "//mat-cell[contains(@class,'cdk-column-title')]/span"; protected static final String EDIT_MENU_DASHBOARD_FIELD = "//input[@formcontrolname='dashboard']"; private static final String EDIT_MENU_DASHBOARD = "//div[@class='cdk-overlay-pane']//span/span[contains(text(),'%s')]"; - private static final String MANAGE_CUSTOMERS_USERS_BTN = ENTITY + "/ancestor::mat-row//mat-icon[contains(text(),' account_circle')]"; + private static final String MANAGE_CUSTOMERS_USERS_BTN = ENTITY + "/ancestor::mat-row//mat-icon[contains(text(),' account_circle')]/parent::button"; private static final String MANAGE_CUSTOMERS_ASSETS_BTN = ENTITY + "/ancestor::mat-row//mat-icon[contains(text(),' domain')]/parent::button"; private static final String MANAGE_CUSTOMERS_DEVICES_BTN = ENTITY + "/ancestor::mat-row//mat-icon[contains(text(),'devices_other')]/parent::button"; private static final String MANAGE_CUSTOMERS_DASHBOARDS_BTN = ENTITY + "/ancestor::mat-row//mat-icon[contains(text(),'dashboard')]/parent::button"; private static final String MANAGE_CUSTOMERS_EDGE_BTN = ENTITY + "/ancestor::mat-row//mat-icon[contains(text(),'router')]/parent::button"; private static final String ADD_USER_EMAIL = "//tb-add-user-dialog//input[@formcontrolname='email']"; private static final String ACTIVATE_WINDOW_OK_BTN = "//span[contains(text(),'OK')]"; - private static final String USER_LOGIN_BTN = "//mat-icon[@data-mat-icon-name='login']"; + private static final String USER_LOGIN_BTN = "//mat-icon[@data-mat-icon-name='login']/parent::button"; + private static final String USER_LOGIN_BTN_BY_EMAIL = "//mat-cell[contains(@class,'email')]/span[contains(text(),'%s')]" + + "/ancestor::mat-row//mat-icon[@data-mat-icon-name='login']/parent::button"; private static final String USERS_WIDGET = "//tb-widget"; private static final String SELECT_COUNTRY_MENU = "//mat-form-field//mat-select[@formcontrolname='country']"; private static final String COUNTRIES = "//span[@class='mdc-list-item__primary-text']"; @@ -148,6 +150,10 @@ public class CustomerPageElements extends OtherPageElementsHelper { return waitUntilElementToBeClickable(USER_LOGIN_BTN); } + public WebElement getUserLoginBtnByEmail(String email) { + return waitUntilElementToBeClickable(String.format(USER_LOGIN_BTN_BY_EMAIL, email)); + } + public WebElement usersWidget() { return waitUntilVisibilityOfElementLocated(USERS_WIDGET); } diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageElements.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageElements.java index ba766ef3fd..d8bb1b021e 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageElements.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageElements.java @@ -1,3 +1,18 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.thingsboard.server.msa.ui.pages; import org.openqa.selenium.WebDriver; diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageHelper.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageHelper.java index 19716d3fb6..12413641af 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageHelper.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageHelper.java @@ -1,3 +1,18 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.thingsboard.server.msa.ui.pages; import org.openqa.selenium.WebDriver; diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/LoginPageHelper.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/LoginPageHelper.java index 38a4a50207..5044ee1a65 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/LoginPageHelper.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/LoginPageHelper.java @@ -25,10 +25,18 @@ public class LoginPageHelper extends LoginPageElements { super(driver); } - public void authorizationTenant() { - emailField().sendKeys(Const.TENANT_EMAIL); - passwordField().sendKeys(Const.TENANT_PASSWORD); + public void login(String username, String password) { + emailField().sendKeys(username); + passwordField().sendKeys(password); submitBtn().click(); waitUntilUrlContainsText("/home"); } + + public void authorizationTenant() { + login(Const.TENANT_EMAIL, Const.TENANT_PASSWORD); + } + + public void authorizationCustomer() { + login("customer@thingsboard.org", "customer"); + } } diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/AssignDetailsTab.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/AssignDetailsTab.java deleted file mode 100644 index cdf10356b3..0000000000 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/AssignDetailsTab.java +++ /dev/null @@ -1,110 +0,0 @@ -package org.thingsboard.server.msa.ui.tests; - -import org.testng.Assert; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; -import org.thingsboard.server.common.data.id.AlarmId; -import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.UserId; -import org.thingsboard.server.msa.prototypes.DevicePrototypes; -import org.thingsboard.server.msa.ui.base.AbstractDriverBaseTest; -import org.thingsboard.server.msa.ui.pages.AlarmHelper; -import org.thingsboard.server.msa.ui.pages.DevicePageHelper; -import org.thingsboard.server.msa.ui.pages.LoginPageHelper; -import org.thingsboard.server.msa.ui.pages.SideBarMenuViewHelper; -import org.thingsboard.server.msa.ui.utils.DataProviderCredential; -import org.thingsboard.server.msa.ui.utils.EntityPrototypes; - -import static org.assertj.core.api.Assertions.assertThat; - -public class AssignDetailsTab extends AbstractDriverBaseTest { - - AlarmId alarmId; - DeviceId deviceId; - UserId userId; - String deviceName; - SideBarMenuViewHelper sideBarMenuView; - AlarmHelper alarmPage; - DevicePageHelper devicePage; - - @BeforeClass - public void generateAlarm() { - new LoginPageHelper(driver).authorizationTenant(); - sideBarMenuView = new SideBarMenuViewHelper(driver); - alarmPage = new AlarmHelper(driver); - devicePage = new DevicePageHelper(driver); - } - - @AfterMethod - public void deleteAlarm() { - testRestClient.deleteAlarm(alarmId); - testRestClient.deleteDevice(deviceId); - if (userId != null) { - testRestClient.deleteUser(userId); - } - } - - @Test(dataProviderClass = DataProviderCredential.class, dataProvider = "assignTo") - public void assignAlarmTo(String user) { - deviceName = testRestClient.postDevice("", DevicePrototypes.defaultDevicePrototype("")).getName(); - deviceId = testRestClient.getDeviceByName(deviceName).getId(); - alarmId = testRestClient.postAlarm(EntityPrototypes.defaultAlarm(deviceId)).getId(); - - sideBarMenuView.goToDevicesPage(); - devicePage.openDeviceAlarms(deviceName); - alarmPage.assignTo(user); - - Assert.assertTrue(alarmPage.assignedUser(user).isDisplayed()); - } - - @Test - public void reassignAlarm() { - testRestClient.postCustomer(EntityPrototypes.defaultCustomerPrototype("TestCustomer")); - userId = testRestClient.postUser(EntityPrototypes.defaultUser(getCustomerByName("TestCustomer").getId())).getId(); - deviceName = testRestClient.postDevice("", DevicePrototypes.defaultDevicePrototype("")).getName(); - deviceId = testRestClient.getDeviceByName(deviceName).getId(); - alarmId = testRestClient.postAlarm(EntityPrototypes.defaultAlarm(deviceId, userId)).getId(); - - sideBarMenuView.goToDevicesPage(); - devicePage.openDeviceAlarms(deviceName); - alarmPage.assignTo("customer@thingsboard.org"); - - Assert.assertTrue(alarmPage.assignedUser("customer@thingsboard.org").isDisplayed()); - } - - @Test - public void searchByEmail() { - deviceName = testRestClient.postDevice("", DevicePrototypes.defaultDevicePrototype("")).getName(); - deviceId = testRestClient.getDeviceByName(deviceName).getId(); - alarmId = testRestClient.postAlarm(EntityPrototypes.defaultAlarm(deviceId)).getId(); - - sideBarMenuView.goToDevicesPage(); - devicePage.openDeviceAlarms(deviceName); - alarmPage.assignBtn().click(); - alarmPage.searchUserField().sendKeys("customer@thingsboard.org"); - alarmPage.setUsers(); - - assertThat(alarmPage.getUsers()).contains("customer@thingsboard.org"); - alarmPage.assignUsers().forEach(u -> assertThat(u.isDisplayed()).isTrue()); - } - - @Test - public void searchByName() { - String name = "usik"; - - userId = testRestClient.postUser(EntityPrototypes.defaultUser(getCustomerByName("Customer A").getId(), name)).getId(); - deviceName = testRestClient.postDevice("", DevicePrototypes.defaultDevicePrototype("")).getName(); - deviceId = testRestClient.getDeviceByName(deviceName).getId(); - alarmId = testRestClient.postAlarm(EntityPrototypes.defaultAlarm(deviceId)).getId(); - - sideBarMenuView.goToDevicesPage(); - devicePage.openDeviceAlarms(deviceName); - alarmPage.assignBtn().click(); - alarmPage.searchUserField().sendKeys(name); - alarmPage.setUsers(); - - assertThat(alarmPage.getUsers()).contains(name); - alarmPage.assignUsers().forEach(u -> assertThat(u.isDisplayed()).isTrue()); - } -} diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AbstractAssignByCustomerTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AbstractAssignByCustomerTest.java new file mode 100644 index 0000000000..6cc9186f5a --- /dev/null +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AbstractAssignByCustomerTest.java @@ -0,0 +1,6 @@ +package org.thingsboard.server.msa.ui.tests.assignee; + +import org.thingsboard.server.msa.ui.base.AbstractDriverBaseTest; + +abstract public class AbstractAssignByCustomerTest extends AbstractDriverBaseTest { +} diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AbstractAssignTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AbstractAssignTest.java new file mode 100644 index 0000000000..633345774c --- /dev/null +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AbstractAssignTest.java @@ -0,0 +1,117 @@ +package org.thingsboard.server.msa.ui.tests.assignee; + +import org.openqa.selenium.WebElement; +import org.testng.annotations.AfterClass; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.msa.ui.base.AbstractDriverBaseTest; +import org.thingsboard.server.msa.ui.pages.AlarmDetailsViewHelper; +import org.thingsboard.server.msa.ui.pages.AlarmHelper; +import org.thingsboard.server.msa.ui.pages.CustomerPageHelper; +import org.thingsboard.server.msa.ui.pages.DevicePageHelper; +import org.thingsboard.server.msa.ui.pages.LoginPageHelper; +import org.thingsboard.server.msa.ui.pages.SideBarMenuViewHelper; +import org.thingsboard.server.msa.ui.utils.EntityPrototypes; + +import java.util.List; + +import static org.thingsboard.server.msa.ui.base.AbstractBasePage.random; + +abstract public class AbstractAssignTest extends AbstractDriverBaseTest { + + protected AlarmId alarmId; + protected AlarmId assignedAlarmId; + protected AlarmId propageteAlarmId; + protected AlarmId propageteAssigneAlarmId; + protected AlarmId tenantAssigneAlarmId; + protected DeviceId deviceId; + protected UserId userId; + protected UserId userWithNameId; +// protected TenantId tenantId = testRestClient.getTenants(pageLink).getData().get(0).getId(); + protected CustomerId customerId; + protected String deviceName; + protected SideBarMenuViewHelper sideBarMenuView; + protected AlarmHelper alarmPage; + protected DevicePageHelper devicePage; + protected AlarmDetailsViewHelper alarmDetailsView; + CustomerPageHelper customerPage; + + protected String name = "User"; + protected String customerTitle = "Customer" + random(); + protected String userEmail = random() + "@thingsboard.org"; + protected String userWithNameEmail = random() + "@thingsboard.org"; + protected String alarm = "Test alarm 1"; + protected String assignedAlarm = "Test alarm 2"; + protected String propagateAlarm = "Test propagated alarm 1"; + protected String propagateAssignedAlarm = "Test propagated alarm 2"; + + @BeforeClass + public void generateTestEntities() { + new LoginPageHelper(driver).authorizationTenant(); + sideBarMenuView = new SideBarMenuViewHelper(driver); + alarmPage = new AlarmHelper(driver); + devicePage = new DevicePageHelper(driver); + alarmDetailsView = new AlarmDetailsViewHelper(driver); + customerPage = new CustomerPageHelper(driver); + + customerId = testRestClient.postCustomer(EntityPrototypes.defaultCustomerPrototype(customerTitle)).getId(); + userId = testRestClient.postUser(EntityPrototypes.defaultUser(userEmail, getCustomerByName(customerTitle).getId())).getId(); + userWithNameId = testRestClient.postUser(EntityPrototypes.defaultUser(userWithNameEmail, getCustomerByName(customerTitle).getId(), name)).getId(); + deviceName = testRestClient.postDevice("", EntityPrototypes.defaultDevicePrototype("", customerId)).getName(); + deviceId = testRestClient.getDeviceByName(deviceName).getId(); + } + + @BeforeMethod + public void generateTestAlarms() { + if (getJwtTokenFromLocalStorage() == null) { + new LoginPageHelper(driver).authorizationTenant(); + } + alarmId = testRestClient.postAlarm(EntityPrototypes.defaultAlarm(deviceId, alarm)).getId(); + assignedAlarmId = testRestClient.postAlarm(EntityPrototypes.defaultAlarm(deviceId, assignedAlarm, userId)).getId(); + propageteAlarmId = testRestClient.postAlarm(EntityPrototypes.defaultAlarm(deviceId, propagateAlarm, true)).getId(); + propageteAssigneAlarmId = testRestClient.postAlarm(EntityPrototypes.defaultAlarm(deviceId, propagateAssignedAlarm, userId, true)).getId(); + + + } + + @AfterClass + public void deleteTestEntities() { + testRestClient.deleteDevice(deviceId); + testRestClient.deleteCustomer(customerId); + } + + @AfterMethod + public void deleteTestAlarms() { + testRestClient.deleteAlarm(alarmId); + testRestClient.deleteAlarm(assignedAlarmId); + testRestClient.deleteAlarm(propageteAlarmId); + testRestClient.deleteAlarm(propageteAssigneAlarmId); + } + + public void loginByUser(String userEmail) { + sideBarMenuView.customerBtn().click(); + customerPage.manageCustomersUserBtn(customerTitle).click(); + customerPage.getUserLoginBtnByEmail(userEmail).click(); + } + + @DataProvider + public Object[][] alarms() { + return new Object[][]{ + {alarm}, + {propagateAlarm}}; + } + + @DataProvider + public Object[][] assignedAlarms() { + return new Object[][]{ + {assignedAlarm}, + {propagateAssignedAlarm}}; + } +} diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AssignByCustomerTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AssignByCustomerTest.java new file mode 100644 index 0000000000..5c376ec3d0 --- /dev/null +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AssignByCustomerTest.java @@ -0,0 +1,4 @@ +package org.thingsboard.server.msa.ui.tests.assignee; + +public class AssignByCustomerTest extends AbstractAssignByCustomerTest{ +} diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AssignDetailsTab.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AssignDetailsTab.java new file mode 100644 index 0000000000..aed0483f3e --- /dev/null +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AssignDetailsTab.java @@ -0,0 +1,209 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.msa.ui.tests.assignee; + +import org.junit.platform.commons.function.Try; +import org.openqa.selenium.html5.WebStorage; +import org.testng.Assert; +import org.testng.annotations.Test; +import org.thingsboard.server.msa.ui.utils.Const; + +import static org.assertj.core.api.Assertions.assertThat; + +public class AssignDetailsTab extends AbstractAssignTest { + + /*@BeforeMethod + public void openAlarmView() { + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(deviceName); + }*/ + + @Test(dataProvider = "alarms") + public void assignAlarmToYourself(String alarm) { + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(deviceName); + alarmPage.assignAlarmTo(alarm, Const.TENANT_EMAIL); + + Assert.assertTrue(alarmPage.assignedUser(Const.TENANT_EMAIL).isDisplayed()); + } + + @Test(dataProvider = "alarms") + public void assignAlarmToAnotherUser(String alarm) { + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(deviceName); + alarmPage.assignAlarmTo(alarm, userEmail); + + Assert.assertTrue(alarmPage.assignedUser(userEmail).isDisplayed()); + } + + @Test(dataProvider = "assignedAlarms") + public void unassignedAlarm(String assignedAlarm) { + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(deviceName); + alarmPage.unassignedAlarm(assignedAlarm); + + Assert.assertTrue(alarmPage.unassigned(assignedAlarm).isDisplayed()); + } + + @Test(dataProvider = "assignedAlarms") + public void reassignAlarm(String assignedAlarm) { + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(deviceName); + alarmPage.assignAlarmTo(assignedAlarm, Const.TENANT_EMAIL); + + Assert.assertTrue(alarmPage.assignedUser(Const.TENANT_EMAIL).isDisplayed()); + } + + @Test + public void searchByEmail() { + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(deviceName); + alarmPage.searchAlarm(alarm, Const.TENANT_EMAIL); + alarmPage.setUsers(); + + assertThat(alarmPage.getUsers()).hasSize(1).contains(Const.TENANT_EMAIL); + alarmPage.assignUsers().forEach(u -> assertThat(u.isDisplayed()).isTrue()); + } + + @Test + public void searchByName() { + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(deviceName); + alarmPage.searchAlarm(alarm, name); + alarmPage.setUsers(); + + assertThat(alarmPage.getUsers()).hasSize(1).contains(name); + alarmPage.assignUsers().forEach(u -> assertThat(u.isDisplayed()).isTrue()); + } + + @Test + public void assignAlarmToYourselfFromDetails() { + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(deviceName); + alarmPage.alarmDetailsBtn(alarm).click(); + alarmDetailsView.assignAlarmTo(Const.TENANT_EMAIL); + alarmDetailsView.closeViewBtn().click(); + + Assert.assertTrue(alarmPage.assignedUser(Const.TENANT_EMAIL).isDisplayed()); + } + + @Test + public void assignAlarmToAnotherUserFromDetails() { + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(deviceName); + alarmPage.alarmDetailsBtn(alarm).click(); + alarmDetailsView.assignAlarmTo(userEmail); + alarmDetailsView.closeViewBtn().click(); + + Assert.assertTrue(alarmPage.assignedUser(userEmail).isDisplayed()); + } + + @Test + public void unassignedAlarmFromDetails() { + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(deviceName); + alarmPage.alarmDetailsBtn(assignedAlarm).click(); + alarmDetailsView.unassignedAlarm(); + alarmDetailsView.closeViewBtn().click(); + + Assert.assertTrue(alarmPage.unassigned(assignedAlarm).isDisplayed()); + } + + @Test + public void reassignAlarmFromDetails() { + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(deviceName); + alarmPage.alarmDetailsBtn(assignedAlarm).click(); + alarmDetailsView.assignAlarmTo(Const.TENANT_EMAIL); + alarmDetailsView.closeViewBtn().click(); + + Assert.assertTrue(alarmPage.assignedUser(Const.TENANT_EMAIL).isDisplayed()); + } + + @Test + public void assignAlarmToYourselfCustomer() { + loginByUser(userEmail); + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(deviceName); + alarmPage.assignAlarmTo(alarm, userEmail); + clearStorage(); + + Assert.assertTrue(alarmPage.assignedUser(userEmail).isDisplayed()); + } + + @Test + public void reassignAlarmByCustomerFromAnotherCustomerUser() { + loginByUser(userWithNameEmail); + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(deviceName); + alarmPage.assignAlarmTo(assignedAlarm, name); + clearStorage(); + + Assert.assertTrue(alarmPage.assignedUser(name).isDisplayed()); + } + + @Test + public void unassignedAlarmFromCustomer() { + loginByUser(userWithNameEmail); + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(deviceName); + alarmPage.unassignedAlarm(assignedAlarm); + clearStorage(); + + Assert.assertTrue(alarmPage.unassigned(assignedAlarm).isDisplayed()); + } + + @Test + public void unassignedAlarmFromAnotherUserFromCustomer() { + loginByUser(userEmail); + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(deviceName); + alarmPage.unassignedAlarm(assignedAlarm); + clearStorage(); + + Assert.assertTrue(alarmPage.unassigned(assignedAlarm).isDisplayed()); + } + + @Test + public void checkTheDisplayOfNamesEmailsFromCustomer() { + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(deviceName); + alarmPage.assignAlarmTo(alarm, Const.TENANT_EMAIL); + loginByUser(userEmail); + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(deviceName); + alarmPage.assignBtn(alarm).click(); + try { + alarmPage.assertUsersForAssignIsNotPresent(); + } finally { + clearStorage(); + } + } + + public WebStorage getWebStorage() { + if (driver instanceof WebStorage) { + return (WebStorage) driver; + } else { + throw new IllegalArgumentException("This test expects the driver to implement WebStorage"); + } + } + + public void clearStorage() { + getWebStorage().getLocalStorage().clear(); + getWebStorage().getSessionStorage().clear(); + } + +} diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/utils/DataProviderCredential.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/utils/DataProviderCredential.java index a0188665c8..bce6e071f4 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/utils/DataProviderCredential.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/utils/DataProviderCredential.java @@ -150,10 +150,4 @@ public class DataProviderCredential { {description, Keys.CONTROL + "A" + Keys.BACK_SPACE, ""}}; } - @DataProvider - public static Object[][] assignTo() { - return new Object[][]{ - {Const.TENANT_EMAIL}, - {"customer@thingsboard.org"}}; - } } diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/utils/EntityPrototypes.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/utils/EntityPrototypes.java index bce4373191..504980a001 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/utils/EntityPrototypes.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/utils/EntityPrototypes.java @@ -15,8 +15,10 @@ */ package org.thingsboard.server.msa.ui.utils; +import org.testcontainers.shaded.org.apache.commons.lang3.RandomStringUtils; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfileProvisionType; import org.thingsboard.server.common.data.DeviceProfileType; @@ -32,8 +34,11 @@ import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfilePr import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.notification.targets.platform.CustomerUsersFilter; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.common.data.security.UserCredentials; +import org.thingsboard.server.common.data.settings.UserSettings; public class EntityPrototypes { @@ -105,37 +110,71 @@ public class EntityPrototypes { return assetProfile; } - public static Alarm defaultAlarm(EntityId id) { + public static Alarm defaultAlarm(EntityId id, String type) { Alarm alarm = new Alarm(); - alarm.setType("default"); + alarm.setType(type); alarm.setOriginator(id); alarm.setSeverity(AlarmSeverity.CRITICAL); return alarm; } - public static Alarm defaultAlarm(EntityId id, UserId userId) { + public static Alarm defaultAlarm(EntityId id, String type, boolean propagate) { Alarm alarm = new Alarm(); - alarm.setType("default"); + alarm.setType(type); + alarm.setOriginator(id); + alarm.setSeverity(AlarmSeverity.CRITICAL); + alarm.setPropagate(propagate); + return alarm; + } + + public static Alarm defaultAlarm(EntityId id, String type, UserId userId) { + Alarm alarm = new Alarm(); + alarm.setType(type); alarm.setOriginator(id); alarm.setSeverity(AlarmSeverity.CRITICAL); alarm.setAssigneeId(userId); return alarm; } - public static User defaultUser(CustomerId customerId) { + public static Alarm defaultAlarm(EntityId id, String type, UserId userId, boolean propagate) { + Alarm alarm = new Alarm(); + alarm.setType(type); + alarm.setOriginator(id); + alarm.setSeverity(AlarmSeverity.CRITICAL); + alarm.setAssigneeId(userId); + alarm.setPropagate(propagate); + return alarm; + } + + public static User defaultUser(String email, CustomerId customerId) { User user = new User(); - user.setEmail("test@thingsboard.org"); + user.setEmail(email); user.setCustomerId(customerId); user.setAuthority(Authority.CUSTOMER_USER); return user; } - public static User defaultUser(CustomerId customerId, String name) { + public static User defaultUser(String email, CustomerId customerId, String name) { User user = new User(); - user.setEmail("test@thingsboard.org"); + user.setEmail(email); user.setFirstName(name); user.setCustomerId(customerId); user.setAuthority(Authority.CUSTOMER_USER); return user; } + + public static Device defaultDevicePrototype(String name){ + Device device = new Device(); + device.setName(name + RandomStringUtils.randomAlphanumeric(7)); + device.setType("DEFAULT"); + return device; + } + + public static Device defaultDevicePrototype(String name, CustomerId id){ + Device device = new Device(); + device.setName(name + RandomStringUtils.randomAlphanumeric(7)); + device.setCustomerId(id); + device.setType("DEFAULT"); + return device; + } } From c9760ab55be5e38020f7b76ca2bd7a840d096d06 Mon Sep 17 00:00:00 2001 From: Seraphym-Tuhai Date: Thu, 13 Apr 2023 17:39:55 +0300 Subject: [PATCH 04/61] add test on customer, asset and entity view alarm --- .../server/msa/TestRestClient.java | 42 +++++++++ .../msa/ui/base/AbstractDriverBaseTest.java | 14 +++ .../ui/pages/AlarmDetailsViewElements.java | 23 ++++- .../msa/ui/pages/AlarmDetailsViewHelper.java | 15 ++++ .../server/msa/ui/pages/AlarmElements.java | 5 ++ .../msa/ui/pages/CustomerPageElements.java | 10 +++ .../msa/ui/pages/CustomerPageHelper.java | 7 ++ .../msa/ui/pages/DevicePageElements.java | 27 +++++- .../server/msa/ui/pages/DevicePageHelper.java | 6 ++ .../msa/ui/pages/SideBarMenuViewElements.java | 10 +++ .../msa/ui/pages/SideBarMenuViewHelper.java | 10 +++ .../AbstractAssignByCustomerTest.java | 15 ++++ .../ui/tests/assignee/AbstractAssignTest.java | 52 +++++++++-- .../tests/assignee/AssignByCustomerTest.java | 15 ++++ .../ui/tests/assignee/AssignDetailsTab.java | 88 ++++++++++++------- .../server/msa/ui/utils/EntityPrototypes.java | 18 ++++ 16 files changed, 315 insertions(+), 42 deletions(-) diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/TestRestClient.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/TestRestClient.java index 45829e7d1e..c34d14e48f 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/TestRestClient.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/TestRestClient.java @@ -29,16 +29,20 @@ import io.restassured.specification.RequestSpecification; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.AssetProfileId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.page.PageData; @@ -445,4 +449,42 @@ public class TestRestClient { .as(new TypeRef>() { }); } + + public Asset postAsset(Asset asset) { + return given().spec(requestSpec) + .body(asset) + .post("/api/asset") + .then() + .statusCode(HTTP_OK) + .extract() + .as(Asset.class); + } + + public Asset getAssetById(AssetId assetId) { + return given().spec(requestSpec) + .get("/api/asset/{assetId}", assetId.getId()) + .then() + .statusCode(HTTP_OK) + .extract() + .as(Asset.class); + } + + public EntityView postEntityView(EntityView entityView) { + return given().spec(requestSpec) + .body(entityView) + .post("/api/entityView") + .then() + .statusCode(HTTP_OK) + .extract() + .as(EntityView.class); + } + + public EntityView getEntityViewById(EntityViewId entityViewId) { + return given().spec(requestSpec) + .get("/api/entityView/{entityViewId}", entityViewId.getId()) + .then() + .statusCode(HTTP_OK) + .extract() + .as(EntityView.class); + } } diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/base/AbstractDriverBaseTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/base/AbstractDriverBaseTest.java index e88397b781..aecdc61c03 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/base/AbstractDriverBaseTest.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/base/AbstractDriverBaseTest.java @@ -27,6 +27,7 @@ import org.openqa.selenium.WebDriverException; import org.openqa.selenium.WebElement; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.chrome.ChromeOptions; +import org.openqa.selenium.html5.WebStorage; import org.openqa.selenium.remote.LocalFileDetector; import org.openqa.selenium.remote.RemoteWebDriver; import org.openqa.selenium.support.ui.ExpectedConditions; @@ -179,4 +180,17 @@ abstract public class AbstractDriverBaseTest extends AbstractContainerTest { new ByteArrayInputStream(((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES))); } } + + public WebStorage getWebStorage() { + if (driver instanceof WebStorage) { + return (WebStorage) driver; + } else { + throw new IllegalArgumentException("This test expects the driver to implement WebStorage"); + } + } + + public void clearStorage() { + getWebStorage().getLocalStorage().clear(); + getWebStorage().getSessionStorage().clear(); + } } diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmDetailsViewElements.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmDetailsViewElements.java index 44f41aff2c..7d88a1ed4f 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmDetailsViewElements.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmDetailsViewElements.java @@ -1,3 +1,18 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.thingsboard.server.msa.ui.pages; import org.openqa.selenium.WebDriver; @@ -9,9 +24,9 @@ public class AlarmDetailsViewElements extends AbstractBasePage { super(driver); } - private static final String ASSIGN_FIELD = "//mat-label[contains(text(),'Assignee')]/parent::label/following-sibling::input"; + private static final String ASSIGN_FIELD = "//tb-alarm-assignee//mat-icon[contains(text(),'keyboard_arrow_down')]/parent::button"; private static final String USER_FROM_DROP_DOWN = "//div[@class='user-display-name']/span[text() = '%s']"; - private static final String CLOSE_VIEW_BTN = "//mat-dialog-container//mat-icon[contains(text(),'close')]/parent::button"; + private static final String CLOSE_ALARM_DETAILS_VIEW_BTN = "//mat-dialog-container//mat-icon[contains(text(),'close')]/parent::button"; private static final String UNASSIGNED_BTN = "//div[@role='listbox']//mat-icon[text() = 'account_circle']/following-sibling::span"; public WebElement assignField() { @@ -22,8 +37,8 @@ public class AlarmDetailsViewElements extends AbstractBasePage { return waitUntilElementToBeClickable(String.format(USER_FROM_DROP_DOWN, emailOrName)); } - public WebElement closeViewBtn() { - return waitUntilElementToBeClickable(CLOSE_VIEW_BTN); + public WebElement closeAlarmDetailsViewBtn() { + return waitUntilElementToBeClickable(CLOSE_ALARM_DETAILS_VIEW_BTN); } public WebElement unassignedBtn() { diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmDetailsViewHelper.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmDetailsViewHelper.java index b658164b37..89d572cf3c 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmDetailsViewHelper.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmDetailsViewHelper.java @@ -1,3 +1,18 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.thingsboard.server.msa.ui.pages; import org.openqa.selenium.WebDriver; diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmElements.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmElements.java index ad3ccb748d..87baef8594 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmElements.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmElements.java @@ -33,6 +33,7 @@ public class AlarmElements extends OtherPageElements{ private static final String UNASSIGNED_BTN = "//div[@role='listbox']//mat-icon[text() = 'account_circle']/following-sibling::span"; private static final String UNASSIGNED = "//span[text() = '%s']/ancestor::mat-row//span[@class='assignee-cell']/mat-icon[text() = 'account_circle']/following-sibling::span"; private static final String ALARM_DETAILS_BTN = "//span[text() = '%s']/ancestor::mat-row//mat-icon[contains(text(),'more_horiz')]/parent::button"; + private static final String ACCESS_FORBIDDEN_DIALOG_VIEW = "//h2[text() = 'Access Forbidden']/parent::tb-confirm-dialog"; public WebElement assignBtn(String type) { return waitUntilElementToBeClickable(String.format(ASSIGN_BTN, type)); @@ -65,4 +66,8 @@ public class AlarmElements extends OtherPageElements{ public WebElement alarmDetailsBtn(String alarmType) { return waitUntilElementToBeClickable(String.format(ALARM_DETAILS_BTN, alarmType)); } + + public WebElement accessForbiddenDialogView() { + return waitUntilVisibilityOfElementLocated(ACCESS_FORBIDDEN_DIALOG_VIEW); + } } diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/CustomerPageElements.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/CustomerPageElements.java index 90f51c4903..465c3d918e 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/CustomerPageElements.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/CustomerPageElements.java @@ -73,6 +73,8 @@ public class CustomerPageElements extends OtherPageElementsHelper { private static final String MANAGE_CUSTOMERS_DASHBOARD_BTN_VIEW = "Manage dashboards"; private static final String MANAGE_CUSTOMERS_EDGE_BTN_VIEW = "Manage edges "; private static final String DELETE_FROM_VIEW_BTN = "//tb-customer//span[contains(text(),' Delete')]"; + private static final String CUSTOMER_DETAILS_VIEW = "//tb-details-panel"; + private static final String CUSTOMER_DETAILS_ALARMS = CUSTOMER_DETAILS_VIEW + "//span[text()='Alarms']"; public WebElement titleFieldAddEntityView() { return waitUntilElementToBeClickable(ADD_ENTITY_VIEW + String.format(INPUT_FIELD, INPUT_FIELD_NAME_TITLE)); @@ -285,4 +287,12 @@ public class CustomerPageElements extends OtherPageElementsHelper { public WebElement customerViewDeleteBtn() { return waitUntilElementToBeClickable(DELETE_FROM_VIEW_BTN); } + + public WebElement customerDetailsView() { + return waitUntilPresenceOfElementLocated(CUSTOMER_DETAILS_VIEW); + } + + public WebElement customerDetailsAlarmsBtn() { + return waitUntilElementToBeClickable(CUSTOMER_DETAILS_ALARMS); + } } diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/CustomerPageHelper.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/CustomerPageHelper.java index ba90361dc8..25b3aff7e1 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/CustomerPageHelper.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/CustomerPageHelper.java @@ -161,4 +161,11 @@ public class CustomerPageHelper extends CustomerPageElements { phoneNumberEntityView().sendKeys(number); phoneNumberEntityView().sendKeys(Keys.TAB); } + + public void openCustomerAlarms(String customerName) { + if (!customerDetailsView().isDisplayed()) { + customer(customerName).click(); + } + customerDetailsAlarmsBtn().click(); + } } diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageElements.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageElements.java index d8bb1b021e..f5e6c2800a 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageElements.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageElements.java @@ -26,7 +26,12 @@ public class DevicePageElements extends OtherPageElements{ private static final String DEVICE = "//table//span[text()='%s']"; private static final String DEVICE_DETAILS_VIEW = "//tb-details-panel"; private static final String DEVICE_DETAILS_ALARMS = DEVICE_DETAILS_VIEW + "//span[text()='Alarms']"; - + private static final String ASSIGN_TO_CUSTOMER_BTN = "//mat-cell[contains(@class,'name')]/span[text()='%s']" + + "/ancestor::mat-row//mat-icon[contains(text(),'assignment_ind')]/parent::button"; + private static final String CHOOSE_CUSTOMER_FOR_ASSIGN_FIELD = "//input[@formcontrolname='entity']"; + private static final String CUSTOMER_FROM_ASSIGN_DROPDOWN = "//div[@role = 'listbox']//span[text() = '%s']"; + private static final String CLOSE_DEVICE_DETAILS_VIEW = "//header//mat-icon[contains(text(),'close')]/parent::button"; + private static final String SUBMIT_ASSIGN_TO_CUSTOMER_BTN = "//button[@type='submit']"; public WebElement device(String deviceName) { return waitUntilElementToBeClickable(String.format(DEVICE, deviceName)); } @@ -38,4 +43,24 @@ public class DevicePageElements extends OtherPageElements{ public WebElement deviceDetailsView() { return waitUntilPresenceOfElementLocated(DEVICE_DETAILS_VIEW); } + + public WebElement assignToCustomerBtn(String deviceName) { + return waitUntilElementToBeClickable(String.format(ASSIGN_TO_CUSTOMER_BTN, deviceName)); + } + + public WebElement chooseCustomerForAssignField() { + return waitUntilElementToBeClickable(CHOOSE_CUSTOMER_FOR_ASSIGN_FIELD); + } + + public WebElement customerFromAssignDropdown(String customerTitle) { + return waitUntilElementToBeClickable(String.format(CUSTOMER_FROM_ASSIGN_DROPDOWN, customerTitle)); + } + + public WebElement closeDeviceDetailsViewBtn() { + return waitUntilElementToBeClickable(CLOSE_DEVICE_DETAILS_VIEW); + } + + public WebElement submitAssignToCustomerBtn() { + return waitUntilElementToBeClickable(SUBMIT_ASSIGN_TO_CUSTOMER_BTN); + } } diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageHelper.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageHelper.java index 12413641af..19e85244cd 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageHelper.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageHelper.java @@ -28,4 +28,10 @@ public class DevicePageHelper extends DevicePageElements { } deviceDetailsAlarmsBtn().click(); } + + public void assignToCustomer(String customerTitle) { + chooseCustomerForAssignField().click(); + customerFromAssignDropdown(customerTitle).click(); + submitAssignToCustomerBtn().click(); + } } diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/SideBarMenuViewElements.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/SideBarMenuViewElements.java index 5ecf27b5bd..54675bdc7a 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/SideBarMenuViewElements.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/SideBarMenuViewElements.java @@ -33,6 +33,8 @@ public class SideBarMenuViewElements extends AbstractBasePage { private static final String ALARMS_BTN = "//mat-toolbar//a[@href='/alarms']"; private static final String ENTITIES_DROPDOWN = "//mat-toolbar//mat-icon[text()='category']/ancestor::a//span[contains(@class,'pull-right')]"; private static final String DEVICES_BTN = "//ul[@id='docs-menu-entity.entities']//span[text()='Devices']"; + private static final String ASSETS_BTN = "//ul[@id='docs-menu-entity.entities']//span[text()='Assets']"; + private static final String ENTITY_VIEWS_BTN = "//ul[@id='docs-menu-entity.entities']//span[text()='Entity Views']"; public WebElement entitiesDropdown() { return waitUntilElementToBeClickable(ENTITIES_DROPDOWN); @@ -69,4 +71,12 @@ public class SideBarMenuViewElements extends AbstractBasePage { public WebElement devicesBtn() { return waitUntilElementToBeClickable(DEVICES_BTN); } + + public WebElement assetsBtn() { + return waitUntilElementToBeClickable(ASSETS_BTN); + } + + public WebElement entityViewsBtn() { + return waitUntilElementToBeClickable(ENTITY_VIEWS_BTN); + } } \ No newline at end of file diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/SideBarMenuViewHelper.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/SideBarMenuViewHelper.java index 92848ff68c..297438c193 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/SideBarMenuViewHelper.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/SideBarMenuViewHelper.java @@ -38,6 +38,16 @@ public class SideBarMenuViewHelper extends SideBarMenuViewElements { devicesBtn().click(); } + public void goToAssetsPage() { + openEntitiesDropdown(); + assetsBtn().click(); + } + + public void goToEntityViewsPage() { + openEntitiesDropdown(); + entityViewsBtn().click(); + } + public void openEntitiesDropdown() { if (entitiesDropdownIsClose()) { entitiesDropdown().click(); diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AbstractAssignByCustomerTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AbstractAssignByCustomerTest.java index 6cc9186f5a..516abf8ab7 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AbstractAssignByCustomerTest.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AbstractAssignByCustomerTest.java @@ -1,3 +1,18 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.thingsboard.server.msa.ui.tests.assignee; import org.thingsboard.server.msa.ui.base.AbstractDriverBaseTest; diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AbstractAssignTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AbstractAssignTest.java index 633345774c..1dfe2b139d 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AbstractAssignTest.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AbstractAssignTest.java @@ -1,15 +1,30 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.thingsboard.server.msa.ui.tests.assignee; -import org.openqa.selenium.WebElement; import org.testng.annotations.AfterClass; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.DataProvider; import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.msa.ui.base.AbstractDriverBaseTest; import org.thingsboard.server.msa.ui.pages.AlarmDetailsViewHelper; @@ -20,8 +35,6 @@ import org.thingsboard.server.msa.ui.pages.LoginPageHelper; import org.thingsboard.server.msa.ui.pages.SideBarMenuViewHelper; import org.thingsboard.server.msa.ui.utils.EntityPrototypes; -import java.util.List; - import static org.thingsboard.server.msa.ui.base.AbstractBasePage.random; abstract public class AbstractAssignTest extends AbstractDriverBaseTest { @@ -30,13 +43,19 @@ abstract public class AbstractAssignTest extends AbstractDriverBaseTest { protected AlarmId assignedAlarmId; protected AlarmId propageteAlarmId; protected AlarmId propageteAssigneAlarmId; - protected AlarmId tenantAssigneAlarmId; + protected AlarmId tenantAlarmId; + protected AlarmId onCustomerAlarmId; + protected AlarmId assetAlarmId; + protected AlarmId entityViewAlarmId; protected DeviceId deviceId; + protected DeviceId tenantDeviceId; protected UserId userId; protected UserId userWithNameId; -// protected TenantId tenantId = testRestClient.getTenants(pageLink).getData().get(0).getId(); protected CustomerId customerId; protected String deviceName; + protected String assetName; + protected String entityViewName; + protected String tenantDeviceName; protected SideBarMenuViewHelper sideBarMenuView; protected AlarmHelper alarmPage; protected DevicePageHelper devicePage; @@ -51,6 +70,12 @@ abstract public class AbstractAssignTest extends AbstractDriverBaseTest { protected String assignedAlarm = "Test alarm 2"; protected String propagateAlarm = "Test propagated alarm 1"; protected String propagateAssignedAlarm = "Test propagated alarm 2"; + protected String tenantAlarm = "Test tenant alarm"; + protected String customerAlarm = "Test customer alarm"; + protected String assetAlarm = "Test asset alarm"; + protected String entityViewAlarm = "Test entity view alarm"; + protected AssetId assetId; + protected EntityViewId entityViewId; @BeforeClass public void generateTestEntities() { @@ -65,7 +90,16 @@ abstract public class AbstractAssignTest extends AbstractDriverBaseTest { userId = testRestClient.postUser(EntityPrototypes.defaultUser(userEmail, getCustomerByName(customerTitle).getId())).getId(); userWithNameId = testRestClient.postUser(EntityPrototypes.defaultUser(userWithNameEmail, getCustomerByName(customerTitle).getId(), name)).getId(); deviceName = testRestClient.postDevice("", EntityPrototypes.defaultDevicePrototype("", customerId)).getName(); + tenantDeviceName = testRestClient.postDevice("", EntityPrototypes.defaultDevicePrototype("")).getName(); deviceId = testRestClient.getDeviceByName(deviceName).getId(); + tenantDeviceId = testRestClient.getDeviceByName(tenantDeviceName).getId(); + onCustomerAlarmId = testRestClient.postAlarm(EntityPrototypes.defaultAlarm(customerId, customerAlarm)).getId(); + assetId = testRestClient.postAsset(EntityPrototypes.defaultAssetPrototype("", customerId)).getId(); + assetName = testRestClient.getAssetById(assetId).getName(); + assetAlarmId = testRestClient.postAlarm(EntityPrototypes.defaultAlarm(assetId, assetAlarm)).getId(); + entityViewId = testRestClient.postEntityView(EntityPrototypes.defaultEntityViewPrototype("", "", "DEVICE")).getId(); + entityViewName = testRestClient.getEntityViewById(entityViewId).getName(); + entityViewAlarmId = testRestClient.postAlarm(EntityPrototypes.defaultAlarm(entityViewId, entityViewAlarm)).getId(); } @BeforeMethod @@ -78,13 +112,18 @@ abstract public class AbstractAssignTest extends AbstractDriverBaseTest { propageteAlarmId = testRestClient.postAlarm(EntityPrototypes.defaultAlarm(deviceId, propagateAlarm, true)).getId(); propageteAssigneAlarmId = testRestClient.postAlarm(EntityPrototypes.defaultAlarm(deviceId, propagateAssignedAlarm, userId, true)).getId(); + tenantAlarmId = testRestClient.postAlarm(EntityPrototypes.defaultAlarm(tenantDeviceId, tenantAlarm)).getId(); } @AfterClass public void deleteTestEntities() { testRestClient.deleteDevice(deviceId); + testRestClient.deleteDevice(tenantDeviceId); testRestClient.deleteCustomer(customerId); + testRestClient.deleteAlarm(onCustomerAlarmId); + testRestClient.deleteAlarm(assetAlarmId); + testRestClient.deleteAlarm(entityViewAlarmId); } @AfterMethod @@ -93,6 +132,7 @@ abstract public class AbstractAssignTest extends AbstractDriverBaseTest { testRestClient.deleteAlarm(assignedAlarmId); testRestClient.deleteAlarm(propageteAlarmId); testRestClient.deleteAlarm(propageteAssigneAlarmId); + testRestClient.deleteAlarm(tenantAlarmId); } public void loginByUser(String userEmail) { diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AssignByCustomerTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AssignByCustomerTest.java index 5c376ec3d0..e02f0ce45b 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AssignByCustomerTest.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AssignByCustomerTest.java @@ -1,3 +1,18 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.thingsboard.server.msa.ui.tests.assignee; public class AssignByCustomerTest extends AbstractAssignByCustomerTest{ diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AssignDetailsTab.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AssignDetailsTab.java index aed0483f3e..9781ef6417 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AssignDetailsTab.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AssignDetailsTab.java @@ -15,22 +15,15 @@ */ package org.thingsboard.server.msa.ui.tests.assignee; -import org.junit.platform.commons.function.Try; -import org.openqa.selenium.html5.WebStorage; import org.testng.Assert; import org.testng.annotations.Test; +import org.thingsboard.server.msa.ui.pages.AssetPageElements; import org.thingsboard.server.msa.ui.utils.Const; import static org.assertj.core.api.Assertions.assertThat; public class AssignDetailsTab extends AbstractAssignTest { - /*@BeforeMethod - public void openAlarmView() { - sideBarMenuView.goToDevicesPage(); - devicePage.openDeviceAlarms(deviceName); - }*/ - @Test(dataProvider = "alarms") public void assignAlarmToYourself(String alarm) { sideBarMenuView.goToDevicesPage(); @@ -85,7 +78,7 @@ public class AssignDetailsTab extends AbstractAssignTest { alarmPage.searchAlarm(alarm, name); alarmPage.setUsers(); - assertThat(alarmPage.getUsers()).hasSize(1).contains(name); + //assertThat(alarmPage.getUsers()).hasSize(1).contains(name); alarmPage.assignUsers().forEach(u -> assertThat(u.isDisplayed()).isTrue()); } @@ -95,7 +88,7 @@ public class AssignDetailsTab extends AbstractAssignTest { devicePage.openDeviceAlarms(deviceName); alarmPage.alarmDetailsBtn(alarm).click(); alarmDetailsView.assignAlarmTo(Const.TENANT_EMAIL); - alarmDetailsView.closeViewBtn().click(); + alarmDetailsView.closeAlarmDetailsViewBtn().click(); Assert.assertTrue(alarmPage.assignedUser(Const.TENANT_EMAIL).isDisplayed()); } @@ -106,7 +99,7 @@ public class AssignDetailsTab extends AbstractAssignTest { devicePage.openDeviceAlarms(deviceName); alarmPage.alarmDetailsBtn(alarm).click(); alarmDetailsView.assignAlarmTo(userEmail); - alarmDetailsView.closeViewBtn().click(); + alarmDetailsView.closeAlarmDetailsViewBtn().click(); Assert.assertTrue(alarmPage.assignedUser(userEmail).isDisplayed()); } @@ -117,7 +110,7 @@ public class AssignDetailsTab extends AbstractAssignTest { devicePage.openDeviceAlarms(deviceName); alarmPage.alarmDetailsBtn(assignedAlarm).click(); alarmDetailsView.unassignedAlarm(); - alarmDetailsView.closeViewBtn().click(); + alarmDetailsView.closeAlarmDetailsViewBtn().click(); Assert.assertTrue(alarmPage.unassigned(assignedAlarm).isDisplayed()); } @@ -128,7 +121,7 @@ public class AssignDetailsTab extends AbstractAssignTest { devicePage.openDeviceAlarms(deviceName); alarmPage.alarmDetailsBtn(assignedAlarm).click(); alarmDetailsView.assignAlarmTo(Const.TENANT_EMAIL); - alarmDetailsView.closeViewBtn().click(); + alarmDetailsView.closeAlarmDetailsViewBtn().click(); Assert.assertTrue(alarmPage.assignedUser(Const.TENANT_EMAIL).isDisplayed()); } @@ -180,30 +173,63 @@ public class AssignDetailsTab extends AbstractAssignTest { @Test public void checkTheDisplayOfNamesEmailsFromCustomer() { sideBarMenuView.goToDevicesPage(); - devicePage.openDeviceAlarms(deviceName); - alarmPage.assignAlarmTo(alarm, Const.TENANT_EMAIL); + devicePage.openDeviceAlarms(tenantDeviceName); + alarmPage.assignAlarmTo(tenantAlarm, Const.TENANT_EMAIL); + devicePage.closeDeviceDetailsViewBtn().click(); + devicePage.assignToCustomerBtn(tenantDeviceName).click(); + devicePage.assignToCustomer(customerTitle); loginByUser(userEmail); sideBarMenuView.goToDevicesPage(); - devicePage.openDeviceAlarms(deviceName); - alarmPage.assignBtn(alarm).click(); - try { - alarmPage.assertUsersForAssignIsNotPresent(); - } finally { - clearStorage(); - } + devicePage.openDeviceAlarms(tenantDeviceName); + + clearStorage(); + Assert.assertTrue(alarmPage.assignedUser(Const.TENANT_EMAIL).isDisplayed()); + } + + @Test + public void reassignTenantForOldAlarm() { + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(tenantDeviceName); + alarmPage.assignAlarmTo(tenantAlarm, Const.TENANT_EMAIL); + devicePage.closeDeviceDetailsViewBtn().click(); + devicePage.assignToCustomerBtn(tenantDeviceName).click(); + devicePage.assignToCustomer(customerTitle); + loginByUser(userEmail); + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(tenantDeviceName); + alarmPage.assignAlarmTo(tenantAlarm, userEmail); + + clearStorage(); + Assert.assertTrue(alarmPage.accessForbiddenDialogView().isDisplayed()); + } + + @Test + public void assignCustomerAlarmToYourself() { + sideBarMenuView.customerBtn().click(); + customerPage.openCustomerAlarms(customerTitle); + alarmPage.assignAlarmTo(customerAlarm, userEmail); + + Assert.assertTrue(alarmPage.assignedUser(userEmail).isDisplayed()); } - public WebStorage getWebStorage() { - if (driver instanceof WebStorage) { - return (WebStorage) driver; - } else { - throw new IllegalArgumentException("This test expects the driver to implement WebStorage"); - } + @Test + public void assignAssetAlarmToYourself() { + AssetPageElements assetPageElements = new AssetPageElements(driver); + sideBarMenuView.goToAssetsPage(); + assetPageElements.openAssetAlarms(customerTitle); + alarmPage.assignAlarmTo(assetAlarm, userEmail); + + Assert.assertTrue(alarmPage.assignedUser(userEmail).isDisplayed()); } - public void clearStorage() { - getWebStorage().getLocalStorage().clear(); - getWebStorage().getSessionStorage().clear(); + @Test + public void assignEntityViewsAlarmToYourself() { + AssetPageElements assetPageElements = new AssetPageElements(driver); + sideBarMenuView.goToEntityViewsPage(); + assetPageElements.openAssetAlarms(entityViewName); + alarmPage.assignAlarmTo(entityViewAlarm, userEmail); + + Assert.assertTrue(alarmPage.assignedUser(userEmail).isDisplayed()); } } diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/utils/EntityPrototypes.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/utils/EntityPrototypes.java index 504980a001..15452506ed 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/utils/EntityPrototypes.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/utils/EntityPrototypes.java @@ -23,9 +23,11 @@ import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfileProvisionType; import org.thingsboard.server.common.data.DeviceProfileType; import org.thingsboard.server.common.data.DeviceTransportType; +import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmSeverity; +import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration; @@ -177,4 +179,20 @@ public class EntityPrototypes { device.setType("DEFAULT"); return device; } + + public static Asset defaultAssetPrototype(String name, CustomerId id){ + Asset asset = new Asset(); + asset.setName(name + RandomStringUtils.randomAlphanumeric(7)); + asset.setCustomerId(id); + asset.setType("DEFAULT"); + return asset; + } + + public static EntityView defaultEntityViewPrototype(String name, String type, String entityType){ + EntityView entityView = new EntityView(); + entityView.setName(name + RandomStringUtils.randomAlphanumeric(7)); + entityView.setType(type + RandomStringUtils.randomAlphanumeric(7)); + entityView.setAdditionalInfo(JacksonUtil.newObjectNode().put("entityType", entityType)); + return entityView; + } } From c33407bb193ae21a5055b0590ea625ade6e7d92e Mon Sep 17 00:00:00 2001 From: Seraphym-Tuhai Date: Fri, 14 Apr 2023 17:37:39 +0300 Subject: [PATCH 05/61] add AbstractAssignTest class, add assertIsDisplayed(), add assignee.xml suite --- .../server/msa/TestRestClient.java | 27 +- .../msa/ui/base/AbstractDriverBaseTest.java | 5 + .../ui/pages/AlarmDetailsViewElements.java | 3 +- .../msa/ui/pages/AssetPageElements.java | 44 ++++ .../server/msa/ui/pages/EntityViewPage.java | 45 ++++ .../AbstractAssignByCustomerTest.java | 21 -- .../ui/tests/assignee/AbstractAssignTest.java | 99 ++------ .../tests/assignee/AssignByCustomerTest.java | 19 -- .../ui/tests/assignee/AssignDetailsTab.java | 235 ------------------ .../assignee/AssignDetailsTabAssignTest.java | 229 +++++++++++++++++ ...ssignDetailsTabFromCustomerAssignTest.java | 129 ++++++++++ .../src/test/resources/assignee.xml | 36 +++ 12 files changed, 523 insertions(+), 369 deletions(-) create mode 100644 msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AssetPageElements.java create mode 100644 msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/EntityViewPage.java delete mode 100644 msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AbstractAssignByCustomerTest.java delete mode 100644 msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AssignByCustomerTest.java delete mode 100644 msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AssignDetailsTab.java create mode 100644 msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AssignDetailsTabAssignTest.java create mode 100644 msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AssignDetailsTabFromCustomerAssignTest.java create mode 100644 msa/black-box-tests/src/test/resources/assignee.xml diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/TestRestClient.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/TestRestClient.java index c34d14e48f..de7f92cfb3 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/TestRestClient.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/TestRestClient.java @@ -430,6 +430,7 @@ public class TestRestClient { .then() .statusCode(HTTP_OK); } + public String getToken() { return token; } @@ -438,18 +439,6 @@ public class TestRestClient { return refreshToken; } - public PageData getTenants(PageLink pageLink) { - Map params = new HashMap<>(); - addPageLinkToParam(params, pageLink); - return given().spec(requestSpec).queryParams(params) - .get("/api/tenants") - .then() - .statusCode(HTTP_OK) - .extract() - .as(new TypeRef>() { - }); - } - public Asset postAsset(Asset asset) { return given().spec(requestSpec) .body(asset) @@ -469,6 +458,13 @@ public class TestRestClient { .as(Asset.class); } + public void deleteAsset(AssetId assetId) { + given().spec(requestSpec) + .delete("/api/asset/{assetId}", assetId.getId()) + .then() + .statusCode(HTTP_OK); + } + public EntityView postEntityView(EntityView entityView) { return given().spec(requestSpec) .body(entityView) @@ -487,4 +483,11 @@ public class TestRestClient { .extract() .as(EntityView.class); } + + public void deleteEntityView(EntityViewId entityViewId) { + given().spec(requestSpec) + .delete("/api/entityView/{entityViewId}", entityViewId.getId()) + .then() + .statusCode(HTTP_OK); + } } diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/base/AbstractDriverBaseTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/base/AbstractDriverBaseTest.java index aecdc61c03..3073ce506d 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/base/AbstractDriverBaseTest.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/base/AbstractDriverBaseTest.java @@ -50,6 +50,7 @@ import java.net.URL; import java.time.Duration; import java.util.stream.Collectors; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; import static org.thingsboard.server.msa.TestProperties.getBaseUiUrl; import static org.thingsboard.server.msa.ui.utils.Const.TENANT_EMAIL; @@ -193,4 +194,8 @@ abstract public class AbstractDriverBaseTest extends AbstractContainerTest { getWebStorage().getLocalStorage().clear(); getWebStorage().getSessionStorage().clear(); } + + public void assertIsDisplayed(WebElement element) { + assertThat(element.isDisplayed()).as(element + " is displayed").isTrue(); + } } diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmDetailsViewElements.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmDetailsViewElements.java index 7d88a1ed4f..6e1e38a226 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmDetailsViewElements.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AlarmDetailsViewElements.java @@ -24,7 +24,8 @@ public class AlarmDetailsViewElements extends AbstractBasePage { super(driver); } - private static final String ASSIGN_FIELD = "//tb-alarm-assignee//mat-icon[contains(text(),'keyboard_arrow_down')]/parent::button"; + //private static final String ASSIGN_FIELD = "//tb-alarm-assignee//mat-icon[contains(text(),'keyboard_arrow_down')]/parent::button"; + private static final String ASSIGN_FIELD = "//mat-label[text()='Assignee']/parent::label/parent::div//input"; private static final String USER_FROM_DROP_DOWN = "//div[@class='user-display-name']/span[text() = '%s']"; private static final String CLOSE_ALARM_DETAILS_VIEW_BTN = "//mat-dialog-container//mat-icon[contains(text(),'close')]/parent::button"; private static final String UNASSIGNED_BTN = "//div[@role='listbox']//mat-icon[text() = 'account_circle']/following-sibling::span"; diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AssetPageElements.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AssetPageElements.java new file mode 100644 index 0000000000..f4424894dd --- /dev/null +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/AssetPageElements.java @@ -0,0 +1,44 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.msa.ui.pages; + +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; + +public class AssetPageElements extends OtherPageElements{ + public AssetPageElements(WebDriver driver) { + super(driver); + } + + private static final String CUSTOMER_DETAILS_VIEW = "//tb-details-panel"; + private static final String CUSTOMER_DETAILS_ALARMS = CUSTOMER_DETAILS_VIEW + "//span[text()='Alarms']"; + + public WebElement customerDetailsView() { + return waitUntilPresenceOfElementLocated(CUSTOMER_DETAILS_VIEW); + } + + public WebElement customerDetailsAlarmsBtn() { + return waitUntilElementToBeClickable(CUSTOMER_DETAILS_ALARMS); + } + + public void openAssetAlarms(String assetName) { + if (!customerDetailsView().isDisplayed()) { + entity(assetName).click(); + } + customerDetailsAlarmsBtn().click(); + } + +} diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/EntityViewPage.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/EntityViewPage.java new file mode 100644 index 0000000000..b62c13d8a7 --- /dev/null +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/EntityViewPage.java @@ -0,0 +1,45 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.msa.ui.pages; + +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; + +public class EntityViewPage extends OtherPageElementsHelper{ + public EntityViewPage(WebDriver driver) { + super(driver); + } + + private static final String CUSTOMER_DETAILS_VIEW = "//tb-details-panel"; + private static final String CUSTOMER_DETAILS_ALARMS = CUSTOMER_DETAILS_VIEW + "//span[text()='Alarms']"; + + public WebElement customerDetailsView() { + return waitUntilPresenceOfElementLocated(CUSTOMER_DETAILS_VIEW); + } + + public WebElement customerDetailsAlarmsBtn() { + return waitUntilElementToBeClickable(CUSTOMER_DETAILS_ALARMS); + } + + + public void openEntityViewAlarms(String customerName) { + if (!customerDetailsView().isDisplayed()) { + entity(customerName).click(); + } + customerDetailsAlarmsBtn().click(); + } + +} diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AbstractAssignByCustomerTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AbstractAssignByCustomerTest.java deleted file mode 100644 index 516abf8ab7..0000000000 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AbstractAssignByCustomerTest.java +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.msa.ui.tests.assignee; - -import org.thingsboard.server.msa.ui.base.AbstractDriverBaseTest; - -abstract public class AbstractAssignByCustomerTest extends AbstractDriverBaseTest { -} diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AbstractAssignTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AbstractAssignTest.java index 1dfe2b139d..182bf04fb4 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AbstractAssignTest.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AbstractAssignTest.java @@ -19,12 +19,9 @@ import org.testng.annotations.AfterClass; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; -import org.testng.annotations.DataProvider; import org.thingsboard.server.common.data.id.AlarmId; -import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.msa.ui.base.AbstractDriverBaseTest; import org.thingsboard.server.msa.ui.pages.AlarmDetailsViewHelper; @@ -41,117 +38,57 @@ abstract public class AbstractAssignTest extends AbstractDriverBaseTest { protected AlarmId alarmId; protected AlarmId assignedAlarmId; - protected AlarmId propageteAlarmId; - protected AlarmId propageteAssigneAlarmId; - protected AlarmId tenantAlarmId; - protected AlarmId onCustomerAlarmId; - protected AlarmId assetAlarmId; - protected AlarmId entityViewAlarmId; protected DeviceId deviceId; - protected DeviceId tenantDeviceId; + protected UserId userId; protected UserId userWithNameId; protected CustomerId customerId; protected String deviceName; - protected String assetName; - protected String entityViewName; - protected String tenantDeviceName; + protected SideBarMenuViewHelper sideBarMenuView; protected AlarmHelper alarmPage; protected DevicePageHelper devicePage; + protected CustomerPageHelper customerPage; protected AlarmDetailsViewHelper alarmDetailsView; - CustomerPageHelper customerPage; - protected String name = "User"; - protected String customerTitle = "Customer" + random(); + protected String userName = "User " + random(); + protected String customerTitle = "Customer " + random(); protected String userEmail = random() + "@thingsboard.org"; protected String userWithNameEmail = random() + "@thingsboard.org"; - protected String alarm = "Test alarm 1"; - protected String assignedAlarm = "Test alarm 2"; - protected String propagateAlarm = "Test propagated alarm 1"; - protected String propagateAssignedAlarm = "Test propagated alarm 2"; - protected String tenantAlarm = "Test tenant alarm"; - protected String customerAlarm = "Test customer alarm"; - protected String assetAlarm = "Test asset alarm"; - protected String entityViewAlarm = "Test entity view alarm"; - protected AssetId assetId; - protected EntityViewId entityViewId; + protected String alarm = "Test alarm " + random(); + protected String assignedAlarm = "Test assigned alarm " + random(); @BeforeClass - public void generateTestEntities() { + public void generateTestEntity() { new LoginPageHelper(driver).authorizationTenant(); sideBarMenuView = new SideBarMenuViewHelper(driver); alarmPage = new AlarmHelper(driver); devicePage = new DevicePageHelper(driver); - alarmDetailsView = new AlarmDetailsViewHelper(driver); customerPage = new CustomerPageHelper(driver); + alarmDetailsView = new AlarmDetailsViewHelper(driver); customerId = testRestClient.postCustomer(EntityPrototypes.defaultCustomerPrototype(customerTitle)).getId(); userId = testRestClient.postUser(EntityPrototypes.defaultUser(userEmail, getCustomerByName(customerTitle).getId())).getId(); - userWithNameId = testRestClient.postUser(EntityPrototypes.defaultUser(userWithNameEmail, getCustomerByName(customerTitle).getId(), name)).getId(); + userWithNameId = testRestClient.postUser(EntityPrototypes.defaultUser(userWithNameEmail, getCustomerByName(customerTitle).getId(), userName)).getId(); deviceName = testRestClient.postDevice("", EntityPrototypes.defaultDevicePrototype("", customerId)).getName(); - tenantDeviceName = testRestClient.postDevice("", EntityPrototypes.defaultDevicePrototype("")).getName(); deviceId = testRestClient.getDeviceByName(deviceName).getId(); - tenantDeviceId = testRestClient.getDeviceByName(tenantDeviceName).getId(); - onCustomerAlarmId = testRestClient.postAlarm(EntityPrototypes.defaultAlarm(customerId, customerAlarm)).getId(); - assetId = testRestClient.postAsset(EntityPrototypes.defaultAssetPrototype("", customerId)).getId(); - assetName = testRestClient.getAssetById(assetId).getName(); - assetAlarmId = testRestClient.postAlarm(EntityPrototypes.defaultAlarm(assetId, assetAlarm)).getId(); - entityViewId = testRestClient.postEntityView(EntityPrototypes.defaultEntityViewPrototype("", "", "DEVICE")).getId(); - entityViewName = testRestClient.getEntityViewById(entityViewId).getName(); - entityViewAlarmId = testRestClient.postAlarm(EntityPrototypes.defaultAlarm(entityViewId, entityViewAlarm)).getId(); + } + + @AfterClass + public void deleteEntities() { + testRestClient.deleteCustomer(customerId); + testRestClient.deleteDevice(deviceId); } @BeforeMethod - public void generateTestAlarms() { - if (getJwtTokenFromLocalStorage() == null) { - new LoginPageHelper(driver).authorizationTenant(); - } + public void createTestAlarms() { alarmId = testRestClient.postAlarm(EntityPrototypes.defaultAlarm(deviceId, alarm)).getId(); assignedAlarmId = testRestClient.postAlarm(EntityPrototypes.defaultAlarm(deviceId, assignedAlarm, userId)).getId(); - propageteAlarmId = testRestClient.postAlarm(EntityPrototypes.defaultAlarm(deviceId, propagateAlarm, true)).getId(); - propageteAssigneAlarmId = testRestClient.postAlarm(EntityPrototypes.defaultAlarm(deviceId, propagateAssignedAlarm, userId, true)).getId(); - - tenantAlarmId = testRestClient.postAlarm(EntityPrototypes.defaultAlarm(tenantDeviceId, tenantAlarm)).getId(); - - } - - @AfterClass - public void deleteTestEntities() { - testRestClient.deleteDevice(deviceId); - testRestClient.deleteDevice(tenantDeviceId); - testRestClient.deleteCustomer(customerId); - testRestClient.deleteAlarm(onCustomerAlarmId); - testRestClient.deleteAlarm(assetAlarmId); - testRestClient.deleteAlarm(entityViewAlarmId); } @AfterMethod - public void deleteTestAlarms() { + public void deleteCreatedAlarms() { testRestClient.deleteAlarm(alarmId); testRestClient.deleteAlarm(assignedAlarmId); - testRestClient.deleteAlarm(propageteAlarmId); - testRestClient.deleteAlarm(propageteAssigneAlarmId); - testRestClient.deleteAlarm(tenantAlarmId); - } - - public void loginByUser(String userEmail) { - sideBarMenuView.customerBtn().click(); - customerPage.manageCustomersUserBtn(customerTitle).click(); - customerPage.getUserLoginBtnByEmail(userEmail).click(); - } - - @DataProvider - public Object[][] alarms() { - return new Object[][]{ - {alarm}, - {propagateAlarm}}; - } - - @DataProvider - public Object[][] assignedAlarms() { - return new Object[][]{ - {assignedAlarm}, - {propagateAssignedAlarm}}; } } diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AssignByCustomerTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AssignByCustomerTest.java deleted file mode 100644 index e02f0ce45b..0000000000 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AssignByCustomerTest.java +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.msa.ui.tests.assignee; - -public class AssignByCustomerTest extends AbstractAssignByCustomerTest{ -} diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AssignDetailsTab.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AssignDetailsTab.java deleted file mode 100644 index 9781ef6417..0000000000 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AssignDetailsTab.java +++ /dev/null @@ -1,235 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.msa.ui.tests.assignee; - -import org.testng.Assert; -import org.testng.annotations.Test; -import org.thingsboard.server.msa.ui.pages.AssetPageElements; -import org.thingsboard.server.msa.ui.utils.Const; - -import static org.assertj.core.api.Assertions.assertThat; - -public class AssignDetailsTab extends AbstractAssignTest { - - @Test(dataProvider = "alarms") - public void assignAlarmToYourself(String alarm) { - sideBarMenuView.goToDevicesPage(); - devicePage.openDeviceAlarms(deviceName); - alarmPage.assignAlarmTo(alarm, Const.TENANT_EMAIL); - - Assert.assertTrue(alarmPage.assignedUser(Const.TENANT_EMAIL).isDisplayed()); - } - - @Test(dataProvider = "alarms") - public void assignAlarmToAnotherUser(String alarm) { - sideBarMenuView.goToDevicesPage(); - devicePage.openDeviceAlarms(deviceName); - alarmPage.assignAlarmTo(alarm, userEmail); - - Assert.assertTrue(alarmPage.assignedUser(userEmail).isDisplayed()); - } - - @Test(dataProvider = "assignedAlarms") - public void unassignedAlarm(String assignedAlarm) { - sideBarMenuView.goToDevicesPage(); - devicePage.openDeviceAlarms(deviceName); - alarmPage.unassignedAlarm(assignedAlarm); - - Assert.assertTrue(alarmPage.unassigned(assignedAlarm).isDisplayed()); - } - - @Test(dataProvider = "assignedAlarms") - public void reassignAlarm(String assignedAlarm) { - sideBarMenuView.goToDevicesPage(); - devicePage.openDeviceAlarms(deviceName); - alarmPage.assignAlarmTo(assignedAlarm, Const.TENANT_EMAIL); - - Assert.assertTrue(alarmPage.assignedUser(Const.TENANT_EMAIL).isDisplayed()); - } - - @Test - public void searchByEmail() { - sideBarMenuView.goToDevicesPage(); - devicePage.openDeviceAlarms(deviceName); - alarmPage.searchAlarm(alarm, Const.TENANT_EMAIL); - alarmPage.setUsers(); - - assertThat(alarmPage.getUsers()).hasSize(1).contains(Const.TENANT_EMAIL); - alarmPage.assignUsers().forEach(u -> assertThat(u.isDisplayed()).isTrue()); - } - - @Test - public void searchByName() { - sideBarMenuView.goToDevicesPage(); - devicePage.openDeviceAlarms(deviceName); - alarmPage.searchAlarm(alarm, name); - alarmPage.setUsers(); - - //assertThat(alarmPage.getUsers()).hasSize(1).contains(name); - alarmPage.assignUsers().forEach(u -> assertThat(u.isDisplayed()).isTrue()); - } - - @Test - public void assignAlarmToYourselfFromDetails() { - sideBarMenuView.goToDevicesPage(); - devicePage.openDeviceAlarms(deviceName); - alarmPage.alarmDetailsBtn(alarm).click(); - alarmDetailsView.assignAlarmTo(Const.TENANT_EMAIL); - alarmDetailsView.closeAlarmDetailsViewBtn().click(); - - Assert.assertTrue(alarmPage.assignedUser(Const.TENANT_EMAIL).isDisplayed()); - } - - @Test - public void assignAlarmToAnotherUserFromDetails() { - sideBarMenuView.goToDevicesPage(); - devicePage.openDeviceAlarms(deviceName); - alarmPage.alarmDetailsBtn(alarm).click(); - alarmDetailsView.assignAlarmTo(userEmail); - alarmDetailsView.closeAlarmDetailsViewBtn().click(); - - Assert.assertTrue(alarmPage.assignedUser(userEmail).isDisplayed()); - } - - @Test - public void unassignedAlarmFromDetails() { - sideBarMenuView.goToDevicesPage(); - devicePage.openDeviceAlarms(deviceName); - alarmPage.alarmDetailsBtn(assignedAlarm).click(); - alarmDetailsView.unassignedAlarm(); - alarmDetailsView.closeAlarmDetailsViewBtn().click(); - - Assert.assertTrue(alarmPage.unassigned(assignedAlarm).isDisplayed()); - } - - @Test - public void reassignAlarmFromDetails() { - sideBarMenuView.goToDevicesPage(); - devicePage.openDeviceAlarms(deviceName); - alarmPage.alarmDetailsBtn(assignedAlarm).click(); - alarmDetailsView.assignAlarmTo(Const.TENANT_EMAIL); - alarmDetailsView.closeAlarmDetailsViewBtn().click(); - - Assert.assertTrue(alarmPage.assignedUser(Const.TENANT_EMAIL).isDisplayed()); - } - - @Test - public void assignAlarmToYourselfCustomer() { - loginByUser(userEmail); - sideBarMenuView.goToDevicesPage(); - devicePage.openDeviceAlarms(deviceName); - alarmPage.assignAlarmTo(alarm, userEmail); - clearStorage(); - - Assert.assertTrue(alarmPage.assignedUser(userEmail).isDisplayed()); - } - - @Test - public void reassignAlarmByCustomerFromAnotherCustomerUser() { - loginByUser(userWithNameEmail); - sideBarMenuView.goToDevicesPage(); - devicePage.openDeviceAlarms(deviceName); - alarmPage.assignAlarmTo(assignedAlarm, name); - clearStorage(); - - Assert.assertTrue(alarmPage.assignedUser(name).isDisplayed()); - } - - @Test - public void unassignedAlarmFromCustomer() { - loginByUser(userWithNameEmail); - sideBarMenuView.goToDevicesPage(); - devicePage.openDeviceAlarms(deviceName); - alarmPage.unassignedAlarm(assignedAlarm); - clearStorage(); - - Assert.assertTrue(alarmPage.unassigned(assignedAlarm).isDisplayed()); - } - - @Test - public void unassignedAlarmFromAnotherUserFromCustomer() { - loginByUser(userEmail); - sideBarMenuView.goToDevicesPage(); - devicePage.openDeviceAlarms(deviceName); - alarmPage.unassignedAlarm(assignedAlarm); - clearStorage(); - - Assert.assertTrue(alarmPage.unassigned(assignedAlarm).isDisplayed()); - } - - @Test - public void checkTheDisplayOfNamesEmailsFromCustomer() { - sideBarMenuView.goToDevicesPage(); - devicePage.openDeviceAlarms(tenantDeviceName); - alarmPage.assignAlarmTo(tenantAlarm, Const.TENANT_EMAIL); - devicePage.closeDeviceDetailsViewBtn().click(); - devicePage.assignToCustomerBtn(tenantDeviceName).click(); - devicePage.assignToCustomer(customerTitle); - loginByUser(userEmail); - sideBarMenuView.goToDevicesPage(); - devicePage.openDeviceAlarms(tenantDeviceName); - - clearStorage(); - Assert.assertTrue(alarmPage.assignedUser(Const.TENANT_EMAIL).isDisplayed()); - } - - @Test - public void reassignTenantForOldAlarm() { - sideBarMenuView.goToDevicesPage(); - devicePage.openDeviceAlarms(tenantDeviceName); - alarmPage.assignAlarmTo(tenantAlarm, Const.TENANT_EMAIL); - devicePage.closeDeviceDetailsViewBtn().click(); - devicePage.assignToCustomerBtn(tenantDeviceName).click(); - devicePage.assignToCustomer(customerTitle); - loginByUser(userEmail); - sideBarMenuView.goToDevicesPage(); - devicePage.openDeviceAlarms(tenantDeviceName); - alarmPage.assignAlarmTo(tenantAlarm, userEmail); - - clearStorage(); - Assert.assertTrue(alarmPage.accessForbiddenDialogView().isDisplayed()); - } - - @Test - public void assignCustomerAlarmToYourself() { - sideBarMenuView.customerBtn().click(); - customerPage.openCustomerAlarms(customerTitle); - alarmPage.assignAlarmTo(customerAlarm, userEmail); - - Assert.assertTrue(alarmPage.assignedUser(userEmail).isDisplayed()); - } - - @Test - public void assignAssetAlarmToYourself() { - AssetPageElements assetPageElements = new AssetPageElements(driver); - sideBarMenuView.goToAssetsPage(); - assetPageElements.openAssetAlarms(customerTitle); - alarmPage.assignAlarmTo(assetAlarm, userEmail); - - Assert.assertTrue(alarmPage.assignedUser(userEmail).isDisplayed()); - } - - @Test - public void assignEntityViewsAlarmToYourself() { - AssetPageElements assetPageElements = new AssetPageElements(driver); - sideBarMenuView.goToEntityViewsPage(); - assetPageElements.openAssetAlarms(entityViewName); - alarmPage.assignAlarmTo(entityViewAlarm, userEmail); - - Assert.assertTrue(alarmPage.assignedUser(userEmail).isDisplayed()); - } - -} diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AssignDetailsTabAssignTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AssignDetailsTabAssignTest.java new file mode 100644 index 0000000000..5f65c94810 --- /dev/null +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AssignDetailsTabAssignTest.java @@ -0,0 +1,229 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.msa.ui.tests.assignee; + +import org.testng.annotations.AfterClass; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.id.EntityViewId; +import org.thingsboard.server.msa.ui.pages.AssetPageElements; +import org.thingsboard.server.msa.ui.pages.EntityViewPage; +import org.thingsboard.server.msa.ui.utils.Const; +import org.thingsboard.server.msa.ui.utils.EntityPrototypes; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.thingsboard.server.msa.ui.base.AbstractBasePage.random; + +public class AssignDetailsTabAssignTest extends AbstractAssignTest { + + private AlarmId propageteAlarmId; + private AlarmId propageteAssigneAlarmId; + private AlarmId customerAlarmId; + private AlarmId assetAlarmId; + private AlarmId entityViewAlarmId; + private String assetName; + private String entityViewName; + private final String propagateAlarm = "Test propagated alarm 1 " + random(); + private final String propagateAssignedAlarm = "Test propagated alarm 2 " + random(); + private final String customerAlarm = "Test customer alarm" + random(); + private final String assetAlarm = "Test asset alarm" + random(); + private final String entityViewAlarm = "Test entity view alarm" + random(); + private AssetId assetId; + private EntityViewId entityViewId; + + @BeforeClass + public void generateTestEntities() { + customerAlarmId = testRestClient.postAlarm(EntityPrototypes.defaultAlarm(customerId, customerAlarm)).getId(); + assetId = testRestClient.postAsset(EntityPrototypes.defaultAssetPrototype("", customerId)).getId(); + assetName = testRestClient.getAssetById(assetId).getName(); + assetAlarmId = testRestClient.postAlarm(EntityPrototypes.defaultAlarm(assetId, assetAlarm)).getId(); + entityViewId = testRestClient.postEntityView(EntityPrototypes.defaultEntityViewPrototype("", "", "DEVICE")).getId(); + entityViewName = testRestClient.getEntityViewById(entityViewId).getName(); + entityViewAlarmId = testRestClient.postAlarm(EntityPrototypes.defaultAlarm(entityViewId, entityViewAlarm)).getId(); + } + + @BeforeMethod + public void generateTestAlarms() { + propageteAlarmId = testRestClient.postAlarm(EntityPrototypes.defaultAlarm(deviceId, propagateAlarm, true)).getId(); + propageteAssigneAlarmId = testRestClient.postAlarm(EntityPrototypes.defaultAlarm(deviceId, propagateAssignedAlarm, userId, true)).getId(); + } + + @AfterClass + public void deleteTestEntities() { + testRestClient.deleteAlarm(customerAlarmId); + testRestClient.deleteAlarm(assetAlarmId); + testRestClient.deleteAlarm(entityViewAlarmId); + testRestClient.deleteAsset(assetId); + testRestClient.deleteEntityView(entityViewId); + } + + @AfterMethod + public void deleteTestAlarms() { + testRestClient.deleteAlarm(propageteAlarmId); + testRestClient.deleteAlarm(propageteAssigneAlarmId); + } + + @DataProvider + public Object[][] alarms() { + return new Object[][]{ + {alarm}, + {propagateAlarm}}; + } + + @DataProvider + public Object[][] assignedAlarms() { + return new Object[][]{ + {assignedAlarm}, + {propagateAssignedAlarm}}; + } + + @Test(dataProvider = "alarms") + public void assignAlarmToYourself(String alarm) { + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(deviceName); + alarmPage.assignAlarmTo(alarm, Const.TENANT_EMAIL); + + assertIsDisplayed(alarmPage.assignedUser(Const.TENANT_EMAIL)); + } + + @Test(dataProvider = "alarms") + public void assignAlarmToAnotherUser(String alarm) { + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(deviceName); + alarmPage.assignAlarmTo(alarm, userEmail); + + assertIsDisplayed(alarmPage.assignedUser(userEmail)); + } + + @Test(dataProvider = "assignedAlarms") + public void unassignedAlarm(String assignedAlarm) { + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(deviceName); + alarmPage.unassignedAlarm(assignedAlarm); + + assertIsDisplayed(alarmPage.unassigned(assignedAlarm)); + } + + @Test(dataProvider = "assignedAlarms") + public void reassignAlarm(String assignedAlarm) { + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(deviceName); + alarmPage.assignAlarmTo(assignedAlarm, Const.TENANT_EMAIL); + + assertIsDisplayed(alarmPage.assignedUser(Const.TENANT_EMAIL)); + } + + @Test + public void searchByEmail() { + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(deviceName); + alarmPage.searchAlarm(alarm, Const.TENANT_EMAIL); + alarmPage.setUsers(); + + assertThat(alarmPage.getUsers()).hasSize(1).as("Search result contains search input").contains(Const.TENANT_EMAIL); + alarmPage.assignUsers().forEach(this::assertIsDisplayed); + } + + @Test(groups = "broken") + public void searchByName() { + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(deviceName); + alarmPage.searchAlarm(alarm, userName); + alarmPage.setUsers(); + + assertThat(alarmPage.getUsers()).hasSize(1).as("Search result contains search input").contains(userName); + alarmPage.assignUsers().forEach(this::assertIsDisplayed); + } + + @Test + public void assignAlarmToYourselfFromDetails() { + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(deviceName); + alarmPage.alarmDetailsBtn(alarm).click(); + alarmDetailsView.assignAlarmTo(Const.TENANT_EMAIL); + alarmDetailsView.closeAlarmDetailsViewBtn().click(); + + assertIsDisplayed(alarmPage.assignedUser(Const.TENANT_EMAIL)); + } + + @Test + public void assignAlarmToAnotherUserFromDetails() { + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(deviceName); + alarmPage.alarmDetailsBtn(alarm).click(); + alarmDetailsView.assignAlarmTo(userEmail); + alarmDetailsView.closeAlarmDetailsViewBtn().click(); + + assertIsDisplayed(alarmPage.assignedUser(userEmail)); + } + + @Test + public void unassignedAlarmFromDetails() { + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(deviceName); + alarmPage.alarmDetailsBtn(assignedAlarm).click(); + alarmDetailsView.unassignedAlarm(); + alarmDetailsView.closeAlarmDetailsViewBtn().click(); + + assertIsDisplayed(alarmPage.unassigned(assignedAlarm)); + } + + @Test + public void reassignAlarmFromDetails() { + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(deviceName); + alarmPage.alarmDetailsBtn(assignedAlarm).click(); + alarmDetailsView.assignAlarmTo(Const.TENANT_EMAIL); + alarmDetailsView.closeAlarmDetailsViewBtn().click(); + + assertIsDisplayed(alarmPage.assignedUser(Const.TENANT_EMAIL)); + } + + @Test + public void assignCustomerAlarmToYourself() { + sideBarMenuView.customerBtn().click(); + customerPage.openCustomerAlarms(customerTitle); + alarmPage.assignAlarmTo(customerAlarm, Const.TENANT_EMAIL); + + assertIsDisplayed(alarmPage.assignedUser(Const.TENANT_EMAIL)); + } + + @Test + public void assignAssetAlarmToYourself() { + AssetPageElements assetPageElements = new AssetPageElements(driver); + sideBarMenuView.goToAssetsPage(); + assetPageElements.openAssetAlarms(assetName); + alarmPage.assignAlarmTo(assetAlarm, Const.TENANT_EMAIL); + + assertIsDisplayed(alarmPage.assignedUser(Const.TENANT_EMAIL)); + } + + @Test + public void assignEntityViewsAlarmToYourself() { + EntityViewPage entityViewPage = new EntityViewPage(driver); + sideBarMenuView.goToEntityViewsPage(); + entityViewPage.openEntityViewAlarms(entityViewName); + alarmPage.assignAlarmTo(entityViewAlarm, Const.TENANT_EMAIL); + + assertIsDisplayed(alarmPage.assignedUser(Const.TENANT_EMAIL)); + } + +} diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AssignDetailsTabFromCustomerAssignTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AssignDetailsTabFromCustomerAssignTest.java new file mode 100644 index 0000000000..7696b4cbe8 --- /dev/null +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/assignee/AssignDetailsTabFromCustomerAssignTest.java @@ -0,0 +1,129 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.msa.ui.tests.assignee; + +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; +import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.msa.ui.pages.LoginPageHelper; +import org.thingsboard.server.msa.ui.utils.Const; +import org.thingsboard.server.msa.ui.utils.EntityPrototypes; + +import static org.thingsboard.server.msa.ui.base.AbstractBasePage.random; + +public class AssignDetailsTabFromCustomerAssignTest extends AbstractAssignTest { + + AlarmId tenantAlarmId; + DeviceId tenantDeviceId; + String tenantDeviceName; + String tenantAlarm = "Test tenant alarm " + random(); + + public void loginByUser(String userEmail) { + sideBarMenuView.customerBtn().click(); + customerPage.manageCustomersUserBtn(customerTitle).click(); + customerPage.getUserLoginBtnByEmail(userEmail).click(); + } + + @BeforeMethod + public void generateTenantEntity() { + if (getJwtTokenFromLocalStorage() == null) { + new LoginPageHelper(driver).authorizationTenant(); + } + tenantDeviceName = testRestClient.postDevice("", EntityPrototypes.defaultDevicePrototype("")).getName(); + tenantDeviceId = testRestClient.getDeviceByName(tenantDeviceName).getId(); + tenantAlarmId = testRestClient.postAlarm(EntityPrototypes.defaultAlarm(tenantDeviceId, tenantAlarm)).getId(); + } + + @AfterMethod + public void clear() { + testRestClient.deleteAlarm(tenantAlarmId); + testRestClient.deleteDevice(tenantDeviceId); + clearStorage(); + } + + @Test + public void assignAlarmToYourselfCustomer() { + loginByUser(userEmail); + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(deviceName); + alarmPage.assignAlarmTo(alarm, userEmail); + + assertIsDisplayed(alarmPage.assignedUser(userEmail)); + } + + @Test + public void reassignAlarmByCustomerFromAnotherCustomerUser() { + loginByUser(userWithNameEmail); + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(deviceName); + alarmPage.assignAlarmTo(assignedAlarm, userName); + + assertIsDisplayed(alarmPage.assignedUser(userName)); + } + + @Test + public void unassignedAlarmFromCustomer() { + loginByUser(userWithNameEmail); + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(deviceName); + alarmPage.unassignedAlarm(assignedAlarm); + + assertIsDisplayed(alarmPage.unassigned(assignedAlarm)); + } + + @Test + public void unassignedAlarmFromAnotherUserFromCustomer() { + loginByUser(userEmail); + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(deviceName); + alarmPage.unassignedAlarm(assignedAlarm); + + assertIsDisplayed(alarmPage.unassigned(assignedAlarm)); + } + + @Test + public void checkTheDisplayOfNamesEmailsFromCustomer() { + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(tenantDeviceName); + alarmPage.assignAlarmTo(tenantAlarm, Const.TENANT_EMAIL); + devicePage.closeDeviceDetailsViewBtn().click(); + devicePage.assignToCustomerBtn(tenantDeviceName).click(); + devicePage.assignToCustomer(customerTitle); + loginByUser(userEmail); + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(tenantDeviceName); + + assertIsDisplayed(alarmPage.assignedUser(Const.TENANT_EMAIL)); + } + + @Test + public void reassignTenantForOldAlarm() { + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(tenantDeviceName); + alarmPage.assignAlarmTo(tenantAlarm, Const.TENANT_EMAIL); + devicePage.closeDeviceDetailsViewBtn().click(); + devicePage.assignToCustomerBtn(tenantDeviceName).click(); + devicePage.assignToCustomer(customerTitle); + loginByUser(userEmail); + sideBarMenuView.goToDevicesPage(); + devicePage.openDeviceAlarms(tenantDeviceName); + alarmPage.assignBtn(tenantAlarm).click(); + + assertIsDisplayed(alarmPage.accessForbiddenDialogView()); + } +} diff --git a/msa/black-box-tests/src/test/resources/assignee.xml b/msa/black-box-tests/src/test/resources/assignee.xml new file mode 100644 index 0000000000..52499824cd --- /dev/null +++ b/msa/black-box-tests/src/test/resources/assignee.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 415c95907bf23a3c4f435e7d6bf914b71b1d2fcd Mon Sep 17 00:00:00 2001 From: Chantsova Ekaterina Date: Fri, 14 Apr 2023 18:33:42 +0300 Subject: [PATCH 06/61] UI: widget extension module support in custom actions --- ...ction-pretty-resources-tabs.component.html | 6 ++- .../widget/dialog/custom-dialog.service.ts | 9 ++-- .../components/widget/widget.component.ts | 53 ++++++++++++++++--- ui-ngx/src/app/shared/models/widget.models.ts | 3 +- 4 files changed, 59 insertions(+), 12 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-resources-tabs.component.html b/ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-resources-tabs.component.html index c3d0141d20..3e4564f505 100644 --- a/ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-resources-tabs.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-resources-tabs.component.html @@ -27,6 +27,10 @@ (ngModelChange)="notifyActionUpdated()" placeholder="{{ 'widget.resource-url' | translate }}"/> + + {{ 'widget.resource-is-module' | translate }} + + + + +
+ +
+ + +
+
+
+ +
+ + + + device.device-state + + + {{ 'device.any' | translate }} + + + {{ 'device.active' | translate }} + + + {{ 'device.inactive' | translate }} + + + +
+
diff --git a/ui-ngx/src/app/modules/home/components/device/device-info-filter.component.scss b/ui-ngx/src/app/modules/home/components/device/device-info-filter.component.scss new file mode 100644 index 0000000000..1c10e244b5 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/device/device-info-filter.component.scss @@ -0,0 +1,35 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +:host { + display: block; + overflow: hidden; + max-width: 100%; + .mdc-button { + max-width: 100%; + } +} + +:host ::ng-deep { + .mdc-button { + .mat-icon { + min-width: 24px; + } + .mdc-button__label { + overflow: hidden; + text-overflow: ellipsis; + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/device/device-info-filter.component.ts b/ui-ngx/src/app/modules/home/components/device/device-info-filter.component.ts new file mode 100644 index 0000000000..1f5b35a3ef --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/device/device-info-filter.component.ts @@ -0,0 +1,254 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + ChangeDetectorRef, + Component, + ElementRef, + forwardRef, + Inject, + InjectionToken, + Input, + OnDestroy, + OnInit, + Optional, + TemplateRef, + ViewChild, + ViewContainerRef +} from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { coerceBoolean } from '@shared/decorators/coerce-boolean'; +import { ConnectedPosition, Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; +import { TemplatePortal } from '@angular/cdk/portal'; +import { TranslateService } from '@ngx-translate/core'; +import { DeviceInfoFilter } from '@shared/models/device.models'; +import { isDefinedAndNotNull } from '@core/utils'; +import { EntityInfoData } from '@shared/models/entity.models'; +import { DeviceProfileService } from '@core/http/device-profile.service'; + +export const DEVICE_FILTER_CONFIG_DATA = new InjectionToken('DeviceFilterConfigData'); + +export interface DeviceFilterConfigData { + panelMode: boolean; + deviceInfoFilter: DeviceInfoFilter; +} + +// @dynamic +@Component({ + selector: 'tb-device-info-filter', + templateUrl: './device-info-filter.component.html', + styleUrls: ['./device-info-filter.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => DeviceInfoFilterComponent), + multi: true + } + ] +}) +export class DeviceInfoFilterComponent implements OnInit, OnDestroy, ControlValueAccessor { + + @ViewChild('deviceFilterPanel') + deviceFilterPanel: TemplateRef; + + @Input() disabled: boolean; + + @coerceBoolean() + @Input() + buttonMode = true; + + panelMode = false; + + buttonDisplayValue = this.translate.instant('device.device-filter'); + + deviceInfoFilterForm: UntypedFormGroup; + + deviceFilterOverlayRef: OverlayRef; + + panelResult: DeviceInfoFilter = null; + + private deviceProfileInfo: EntityInfoData; + + private deviceInfoFilter: DeviceInfoFilter; + + private propagateChange = (_: any) => {}; + + constructor(@Optional() @Inject(DEVICE_FILTER_CONFIG_DATA) + private data: DeviceFilterConfigData | undefined, + @Optional() + private overlayRef: OverlayRef, + private fb: UntypedFormBuilder, + private translate: TranslateService, + private overlay: Overlay, + private nativeElement: ElementRef, + private viewContainerRef: ViewContainerRef, + private deviceProfileService: DeviceProfileService, + private cd: ChangeDetectorRef) { + } + + ngOnInit(): void { + if (this.data) { + this.panelMode = this.data.panelMode; + this.deviceInfoFilter = this.data.deviceInfoFilter; + } + this.deviceInfoFilterForm = this.fb.group({ + deviceProfileId: [null, []], + active: ['', []] + }); + this.deviceInfoFilterForm.valueChanges.subscribe( + () => { + this.updateValidators(); + if (!this.buttonMode) { + this.deviceFilterUpdated(this.deviceInfoFilterForm.value); + } + } + ); + if (this.panelMode) { + this.updateDeviceInfoFilterForm(this.deviceInfoFilter); + } + } + + ngOnDestroy(): void { + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (this.disabled) { + this.deviceInfoFilterForm.disable({emitEvent: false}); + } else { + this.deviceInfoFilterForm.enable({emitEvent: false}); + this.updateValidators(); + } + } + + writeValue(deviceInfoFilter?: DeviceInfoFilter): void { + this.deviceInfoFilter = deviceInfoFilter; + this.updateButtonDisplayValue(); + this.updateDeviceInfoFilterForm(deviceInfoFilter); + } + + private updateValidators() { + } + + toggleDeviceFilterPanel($event: Event) { + if ($event) { + $event.stopPropagation(); + } + const config = new OverlayConfig({ + panelClass: 'tb-filter-panel', + backdropClass: 'cdk-overlay-transparent-backdrop', + hasBackdrop: true, + maxHeight: '80vh', + height: 'min-content', + minWidth: '' + }); + config.hasBackdrop = true; + const connectedPosition: ConnectedPosition = { + originX: 'start', + originY: 'bottom', + overlayX: 'start', + overlayY: 'top' + }; + config.positionStrategy = this.overlay.position().flexibleConnectedTo(this.nativeElement) + .withPositions([connectedPosition]); + + this.deviceFilterOverlayRef = this.overlay.create(config); + this.deviceFilterOverlayRef.backdropClick().subscribe(() => { + this.deviceFilterOverlayRef.dispose(); + }); + this.deviceFilterOverlayRef.attach(new TemplatePortal(this.deviceFilterPanel, + this.viewContainerRef)); + } + + cancel() { + this.updateDeviceInfoFilterForm(this.deviceInfoFilter); + if (this.overlayRef) { + this.overlayRef.dispose(); + } else { + this.deviceFilterOverlayRef.dispose(); + } + } + + update() { + this.deviceFilterUpdated(this.deviceInfoFilterForm.value); + if (this.panelMode) { + this.panelResult = this.deviceInfoFilter; + } + if (this.overlayRef) { + this.overlayRef.dispose(); + } else { + this.deviceFilterOverlayRef.dispose(); + } + } + + deviceProfileChanged(deviceProfileInfo: EntityInfoData) { + this.deviceProfileInfo = deviceProfileInfo; + this.updateButtonDisplayValue(); + } + + private updateDeviceInfoFilterForm(deviceInfoFilter?: DeviceInfoFilter) { + this.deviceInfoFilterForm.patchValue({ + deviceProfileId: deviceInfoFilter?.deviceProfileId, + active: isDefinedAndNotNull(deviceInfoFilter?.active) ? deviceInfoFilter?.active : '' + }, {emitEvent: false}); + this.updateValidators(); + } + + private deviceFilterUpdated(deviceInfoFilter: DeviceInfoFilter) { + this.deviceInfoFilter = deviceInfoFilter; + if ((this.deviceInfoFilter.active as any) === '') { + this.deviceInfoFilter.active = null; + } + this.updateButtonDisplayValue(); + this.propagateChange(this.deviceInfoFilter); + } + + private updateButtonDisplayValue() { + if (this.buttonMode) { + const filterTextParts: string[] = []; + if (isDefinedAndNotNull(this.deviceInfoFilter?.deviceProfileId)) { + if (!this.deviceProfileInfo) { + this.deviceProfileService.getDeviceProfileInfo(this.deviceInfoFilter?.deviceProfileId.id, + {ignoreLoading: true, ignoreErrors: true}).subscribe( + (deviceProfileInfo) => { + this.deviceProfileChanged(deviceProfileInfo); + }); + return; + } else { + filterTextParts.push(this.deviceProfileInfo.name); + } + } + if (isDefinedAndNotNull(this.deviceInfoFilter?.active)) { + const translationKey = this.deviceInfoFilter?.active ? 'device.active' : 'device.inactive'; + filterTextParts.push(this.translate.instant(translationKey)); + } + if (!filterTextParts.length) { + this.buttonDisplayValue = this.translate.instant('device.device-filter-title'); + } else { + this.buttonDisplayValue = this.translate.instant('device.filter-title') + `: ${filterTextParts.join(', ')}`; + } + this.cd.detectChanges(); + } + } + +} diff --git a/ui-ngx/src/app/modules/home/components/home-components.module.ts b/ui-ngx/src/app/modules/home/components/home-components.module.ts index 467d006aab..ffa2d11b99 100644 --- a/ui-ngx/src/app/modules/home/components/home-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/home-components.module.ts @@ -181,6 +181,7 @@ import { SendNotificationButtonComponent } from '@home/components/notification/s import { AlarmFilterConfigComponent } from '@home/components/alarm/alarm-filter-config.component'; import { AlarmAssigneeSelectPanelComponent } from '@home/components/alarm/alarm-assignee-select-panel.component'; import { AlarmAssigneeSelectComponent } from '@home/components/alarm/alarm-assignee-select.component'; +import { DeviceInfoFilterComponent } from '@home/components/device/device-info-filter.component'; @NgModule({ declarations: @@ -282,6 +283,7 @@ import { AlarmAssigneeSelectComponent } from '@home/components/alarm/alarm-assig DeviceProfileComponent, DeviceProfileDialogComponent, AddDeviceProfileDialogComponent, + DeviceInfoFilterComponent, AssetProfileComponent, AssetProfileDialogComponent, AssetProfileAutocompleteComponent, @@ -425,6 +427,7 @@ import { AlarmAssigneeSelectComponent } from '@home/components/alarm/alarm-assig DeviceProfileComponent, DeviceProfileDialogComponent, AddDeviceProfileDialogComponent, + DeviceInfoFilterComponent, RuleChainAutocompleteComponent, DeviceWizardDialogComponent, AssetProfileComponent, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts index abf43a8926..6b0537d968 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts @@ -591,7 +591,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, const target = $event.target || $event.srcElement || $event.currentTarget; const config = new OverlayConfig(); config.backdropClass = 'cdk-overlay-transparent-backdrop'; - config.panelClass = 'tb-alarm-filter-panel'; + config.panelClass = 'tb-filter-panel'; config.hasBackdrop = true; const connectedPosition: ConnectedPosition = { originX: 'end', diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/home-page-widget.scss b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/home-page-widget.scss index 912ebbb63e..4cad7a6971 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/home-page-widget.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/home-page-widget.scss @@ -67,4 +67,50 @@ color: inherit; } } + + .tb-no-data-available { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + } + + .tb-no-data-bg { + margin: 10px; + position: relative; + flex: 1; + width: 100%; + max-height: 100px; + &:before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: #305680; + -webkit-mask-image: url(/assets/home/no_data_folder_bg.svg); + -webkit-mask-repeat: no-repeat; + -webkit-mask-size: contain; + -webkit-mask-position: center; + mask-image: url(/assets/home/no_data_folder_bg.svg); + mask-repeat: no-repeat; + mask-size: contain; + mask-position: center; + } + } + + .tb-no-data-text { + font-weight: 500; + font-size: 14px; + line-height: 20px; + letter-spacing: 0.25px; + color: rgba(0, 0, 0, 0.54); + @media #{$mat-md-lg} { + font-size: 12px; + line-height: 16px; + } + } + } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/recent-dashboards-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/recent-dashboards-widget.component.html index 40a1eefe40..ba7614934d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/recent-dashboards-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/recent-dashboards-widget.component.html @@ -16,91 +16,102 @@ -->
- - - -
- - - - - - {{ lastVisitedDashboard.starred ? 'star' : 'star_border' }} - - - - - {{ 'widgets.recent-dashboards.name' | translate }} - - - {{ lastVisitedDashboard.title }} - - - - - {{ 'widgets.recent-dashboards.last-viewed' | translate }} - - - {{ lastVisitedDashboard.lastVisited | dateAgo:{applyAgo: true} }} - - - - -
+ +
+ {{ 'widgets.recent-dashboards.title' | translate }} + - -
-
-
{{ 'widgets.recent-dashboards.no-last-viewed-dashboards' | translate }}
+
+ + +
+ + + + + + {{ lastVisitedDashboard.starred ? 'star' : 'star_border' }} + + + + + {{ 'widgets.recent-dashboards.name' | translate }} + + + {{ lastVisitedDashboard.title }} + + + + + {{ 'widgets.recent-dashboards.last-viewed' | translate }} + + + {{ lastVisitedDashboard.lastVisited | dateAgo:{applyAgo: true} }} + + + + +
-
-
- -
-
-
- {{ dashboard.starred ? 'star' : 'star_border' }} + +
+
+
{{ 'widgets.recent-dashboards.no-last-viewed-dashboards' | translate }}
-
- {{ dashboard.title }} + + + +
+
+
+ {{ dashboard.starred ? 'star' : 'star_border' }} +
+
+ +
+ + +
+
-
- -
- + +
+
{{ 'widgets.recent-dashboards.title' | translate }}
+
+
+
+
widgets.home.no-data-available
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/recent-dashboards-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/recent-dashboards-widget.component.ts index 33b273ec9f..4bc6ac9982 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/recent-dashboards-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/recent-dashboards-widget.component.ts @@ -74,6 +74,7 @@ export class RecentDashboardsWidgetComponent extends PageComponent implements On lastVisitedDashboardsPageLink: PageLink; starredDashboardValue = null; + hasDashboardsAccess = true; dirty = false; @@ -84,7 +85,10 @@ export class RecentDashboardsWidgetComponent extends PageComponent implements On } ngOnInit() { - this.reload(); + this.hasDashboardsAccess = [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER].includes(this.authUser.authority); + if (this.hasDashboardsAccess) { + this.reload(); + } } reload() { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.html index 264b6970ad..a0beeacdb2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.html @@ -16,74 +16,85 @@ -->
- - - -
-
-
device.devices
-
asset.assets
-
user.users
-
dashboard.dashboards
-
customer.customers
-
-
-
-
{{ usageInfo?.devices | shortNumber }} / {{ maxValue(usageInfo?.maxDevices) }}
-
{{ usageInfo?.assets | shortNumber }} / {{ maxValue(usageInfo?.maxAssets) }}
-
{{ usageInfo?.users | shortNumber }} / {{ maxValue(usageInfo?.maxUsers) }}
-
{{ usageInfo?.dashboards | shortNumber }} / {{ maxValue(usageInfo?.maxDashboards) }}
-
{{ usageInfo?.customers | shortNumber }} / {{ maxValue(usageInfo?.maxCustomers) }}
+ + + + +
+
+
device.devices
+
asset.assets
+
user.users
+
dashboard.dashboards
+
customer.customers
-
- - - - - +
+
+
{{ usageInfo?.devices | shortNumber }} / {{ maxValue(usageInfo?.maxDevices) }}
+
{{ usageInfo?.assets | shortNumber }} / {{ maxValue(usageInfo?.maxAssets) }}
+
{{ usageInfo?.users | shortNumber }} / {{ maxValue(usageInfo?.maxUsers) }}
+
{{ usageInfo?.dashboards | shortNumber }} / {{ maxValue(usageInfo?.maxDashboards) }}
+
{{ usageInfo?.customers | shortNumber }} / {{ maxValue(usageInfo?.maxCustomers) }}
+
+
+ + + + + +
-
-
- -
-
-
api-usage.transport-messages
-
api-usage.javascript
-
api-usage.alarms-created
-
api-usage.email
-
api-usage.sms
-
-
-
-
{{ usageInfo?.transportMessages | shortNumber }} / {{ maxValue(usageInfo?.maxTransportMessages) }}
-
{{ usageInfo?.jsExecutions | shortNumber }} / {{ maxValue(usageInfo?.maxJsExecutions) }}
-
{{ usageInfo?.alarms | shortNumber }} / {{ maxValue(usageInfo?.maxAlarms) }}
-
{{ usageInfo?.emails | shortNumber }} / {{ maxValue(usageInfo?.maxEmails) }}
-
{{ usageInfo?.sms | shortNumber }} / {{ maxValue(usageInfo?.maxSms) }}
+ + +
+
+
api-usage.transport-messages
+
api-usage.javascript
+
api-usage.alarms-created
+
api-usage.email
+
api-usage.sms
-
- - - - - +
+
+
{{ usageInfo?.transportMessages | shortNumber }} / {{ maxValue(usageInfo?.maxTransportMessages) }}
+
{{ usageInfo?.jsExecutions | shortNumber }} / {{ maxValue(usageInfo?.maxJsExecutions) }}
+
{{ usageInfo?.alarms | shortNumber }} / {{ maxValue(usageInfo?.maxAlarms) }}
+
{{ usageInfo?.emails | shortNumber }} / {{ maxValue(usageInfo?.maxEmails) }}
+
{{ usageInfo?.sms | shortNumber }} / {{ maxValue(usageInfo?.maxSms) }}
+
+
+ + + + + +
-
-
+ + + +
+
{{ 'widgets.usage-info.title' | translate }}
+
+
+
+
widgets.home.no-data-available
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.ts index ed5ff7dafc..03148d501a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.ts @@ -44,6 +44,8 @@ export class UsageInfoWidgetComponent extends PageComponent implements OnInit, O entityItemCritical: {[key: string]: boolean} = {}; apiCallItemCritical: {[key: string]: boolean} = {}; + hasUsageInfoAccess = true; + constructor(protected store: Store, private cd: ChangeDetectorRef, private shortNumberPipe: ShortNumberPipe, @@ -52,41 +54,43 @@ export class UsageInfoWidgetComponent extends PageComponent implements OnInit, O } ngOnInit() { - (this.authUser.authority === Authority.TENANT_ADMIN ? - this.usageInfoService.getUsageInfo() : of(null)).subscribe( - (usageInfo) => { - this.usageInfo = usageInfo; - this.entityItemCritical.devices = this.isItemCritical(this.usageInfo?.devices, this.usageInfo?.maxDevices); - this.entityItemCritical.assets = this.isItemCritical(this.usageInfo?.assets, this.usageInfo?.maxAssets); - this.entityItemCritical.users = this.isItemCritical(this.usageInfo?.users, this.usageInfo?.maxUsers); - this.entityItemCritical.dashboards = this.isItemCritical(this.usageInfo?.dashboards, this.usageInfo?.maxDashboards); - this.entityItemCritical.customers = this.isItemCritical(this.usageInfo?.customers, this.usageInfo?.maxCustomers); - this.apiCallItemCritical.transportMessages = this.isItemCritical(this.usageInfo?.transportMessages, - this.usageInfo?.maxTransportMessages); - this.apiCallItemCritical.jsExecutions = this.isItemCritical(this.usageInfo?.jsExecutions, this.usageInfo?.maxJsExecutions); - this.apiCallItemCritical.alarms = this.isItemCritical(this.usageInfo?.alarms, this.usageInfo?.maxAlarms); - this.apiCallItemCritical.emails = this.isItemCritical(this.usageInfo?.emails, this.usageInfo?.maxEmails); - this.apiCallItemCritical.sms = this.isItemCritical(this.usageInfo?.sms, this.usageInfo?.maxSms); - let entitiesHasCriticalItem = false; - let apiCallsHasCriticalItem = false; - for (const key of Object.keys(this.entityItemCritical)) { - if (this.entityItemCritical[key]) { - entitiesHasCriticalItem = true; - break; + this.hasUsageInfoAccess = this.authUser.authority === Authority.TENANT_ADMIN; + if (this.hasUsageInfoAccess) { + this.usageInfoService.getUsageInfo().subscribe( + (usageInfo) => { + this.usageInfo = usageInfo; + this.entityItemCritical.devices = this.isItemCritical(this.usageInfo?.devices, this.usageInfo?.maxDevices); + this.entityItemCritical.assets = this.isItemCritical(this.usageInfo?.assets, this.usageInfo?.maxAssets); + this.entityItemCritical.users = this.isItemCritical(this.usageInfo?.users, this.usageInfo?.maxUsers); + this.entityItemCritical.dashboards = this.isItemCritical(this.usageInfo?.dashboards, this.usageInfo?.maxDashboards); + this.entityItemCritical.customers = this.isItemCritical(this.usageInfo?.customers, this.usageInfo?.maxCustomers); + this.apiCallItemCritical.transportMessages = this.isItemCritical(this.usageInfo?.transportMessages, + this.usageInfo?.maxTransportMessages); + this.apiCallItemCritical.jsExecutions = this.isItemCritical(this.usageInfo?.jsExecutions, this.usageInfo?.maxJsExecutions); + this.apiCallItemCritical.alarms = this.isItemCritical(this.usageInfo?.alarms, this.usageInfo?.maxAlarms); + this.apiCallItemCritical.emails = this.isItemCritical(this.usageInfo?.emails, this.usageInfo?.maxEmails); + this.apiCallItemCritical.sms = this.isItemCritical(this.usageInfo?.sms, this.usageInfo?.maxSms); + let entitiesHasCriticalItem = false; + let apiCallsHasCriticalItem = false; + for (const key of Object.keys(this.entityItemCritical)) { + if (this.entityItemCritical[key]) { + entitiesHasCriticalItem = true; + break; + } } - } - for (const key of Object.keys(this.apiCallItemCritical)) { - if (this.apiCallItemCritical[key]) { - apiCallsHasCriticalItem = true; - break; + for (const key of Object.keys(this.apiCallItemCritical)) { + if (this.apiCallItemCritical[key]) { + apiCallsHasCriticalItem = true; + break; + } } + if (apiCallsHasCriticalItem && !entitiesHasCriticalItem) { + this.toggleValue = 'apiCalls'; + } + this.cd.markForCheck(); } - if (apiCallsHasCriticalItem && !entitiesHasCriticalItem) { - this.toggleValue = 'apiCalls'; - } - this.cd.markForCheck(); - } - ); + ); + } } maxValue(max: number): number | string { diff --git a/ui-ngx/src/app/modules/home/pages/device/device-table-header.component.html b/ui-ngx/src/app/modules/home/pages/device/device-table-header.component.html index 043f1b1d17..c8fdbf81dd 100644 --- a/ui-ngx/src/app/modules/home/pages/device/device-table-header.component.html +++ b/ui-ngx/src/app/modules/home/pages/device/device-table-header.component.html @@ -15,10 +15,6 @@ limitations under the License. --> - - + + diff --git a/ui-ngx/src/app/modules/home/pages/device/device-table-header.component.ts b/ui-ngx/src/app/modules/home/pages/device/device-table-header.component.ts index 6f424c8bb1..e169de5f07 100644 --- a/ui-ngx/src/app/modules/home/pages/device/device-table-header.component.ts +++ b/ui-ngx/src/app/modules/home/pages/device/device-table-header.component.ts @@ -18,7 +18,7 @@ import { Component } from '@angular/core'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { EntityTableHeaderComponent } from '../../components/entity/entity-table-header.component'; -import { DeviceInfo } from '@app/shared/models/device.models'; +import { DeviceInfo, DeviceInfoFilter } from '@app/shared/models/device.models'; import { EntityType } from '@shared/models/entity-type.models'; import { DeviceProfileId } from '../../../../shared/models/id/device-profile-id'; @@ -35,8 +35,8 @@ export class DeviceTableHeaderComponent extends EntityTableHeaderComponent> { @@ -103,17 +118,16 @@ export class DevicesTableConfigResolver implements Resolve this.translate.instant('device.delete-devices-text'); this.config.loadEntity = id => this.deviceService.getDeviceInfo(id.id); - this.config.saveEntity = device => { - return this.deviceService.saveDevice(device).pipe( + this.config.saveEntity = device => this.deviceService.saveDevice(device).pipe( tap(() => { this.broadcast.broadcast('deviceSaved'); }), mergeMap((savedDevice) => this.deviceService.getDeviceInfo(savedDevice.id.id) )); - }; this.config.onEntityAction = action => this.onDeviceAction(action, this.config); this.config.detailsReadonly = () => (this.config.componentsData.deviceScope === 'customer_user' || this.config.componentsData.deviceScope === 'edge_customer_user'); + this.config.onLoadAction = (route) => this.onLoadAction(route); this.config.headerComponent = DeviceTableHeaderComponent; @@ -123,7 +137,7 @@ export class DevicesTableConfigResolver implements Resolve(), edgeId: routeParams.edgeId }; @@ -162,7 +176,8 @@ export class DevicesTableConfigResolver implements Resolve this.config.componentsData.deviceScope === 'tenant'; return this.config; @@ -170,48 +185,97 @@ export class DevicesTableConfigResolver implements Resolve> { const columns: Array> = [ new DateEntityTableColumn('createdTime', 'common.created-time', this.datePipe, '150px'), new EntityTableColumn('name', 'device.name', '25%'), new EntityTableColumn('deviceProfileName', 'device-profile.device-profile', '25%'), - new EntityTableColumn('label', 'device.label', '25%') + new EntityTableColumn('label', 'device.label', '25%'), + new EntityTableColumn('active', 'device.state', '80px', + entity => this.deviceState(entity), entity => this.deviceStateStyle(entity)) ]; if (deviceScope === 'tenant') { columns.push( new EntityTableColumn('customerTitle', 'customer.customer', '25%'), new EntityTableColumn('customerIsPublic', 'device.public', '60px', - entity => { - return checkBoxCell(entity.customerIsPublic); - }, () => ({}), false), + entity => checkBoxCell(entity.customerIsPublic), () => ({})), ); } columns.push( new EntityTableColumn('gateway', 'device.is-gateway', '60px', - entity => { - return checkBoxCell(entity.additionalInfo && entity.additionalInfo.gateway); - }, () => ({}), false) + entity => checkBoxCell(entity.additionalInfo && entity.additionalInfo.gateway), () => ({}), false) ); return columns; } + private deviceState(device: DeviceInfo): string { + let translateKey = 'device.active'; + let backgroundColor = 'rgba(25, 128, 56, 0.08)'; + if (!device.active) { + translateKey = 'device.inactive'; + backgroundColor = 'rgba(209, 39, 48, 0.08)'; + } + return `
+ ${this.translate.instant(translateKey)} +
`; + } + + private deviceStateStyle(device: DeviceInfo): object { + const styleObj = { + fontSize: '14px', + color: '#198038', + cursor: 'pointer' + }; + if (!device.active) { + styleObj.color = '#d12730'; + } + return styleObj; + } + configureEntityFunctions(deviceScope: string): void { + this.config.entitiesFetchFunction = pageLink => this.deviceService.getDeviceInfosByQuery(this.prepareDeviceInfoQuery(pageLink)); if (deviceScope === 'tenant') { - this.config.entitiesFetchFunction = pageLink => - this.deviceService.getTenantDeviceInfosByDeviceProfileId(pageLink, - this.config.componentsData.deviceProfileId !== null ? - this.config.componentsData.deviceProfileId.id : ''); this.config.deleteEntity = id => this.deviceService.deleteDevice(id.id); - } else if (deviceScope === 'edge' || deviceScope === 'edge_customer_user') { - this.config.entitiesFetchFunction = pageLink => - this.deviceService.getEdgeDevices(this.config.componentsData.edgeId, pageLink, this.config.componentsData.edgeType); } else { - this.config.entitiesFetchFunction = pageLink => - this.deviceService.getCustomerDeviceInfosByDeviceProfileId(this.customerId, pageLink, - this.config.componentsData.deviceProfileId !== null ? - this.config.componentsData.deviceProfileId.id : ''); - this.config.deleteEntity = id => this.deviceService.unassignDeviceFromCustomer(id.id); + this.config.deleteEntity = () => of(); + } + } + + prepareDeviceInfoQuery(pageLink: PageLink): DeviceInfoQuery { + const deviceInfoFilter: DeviceInfoFilter = deepClone(this.config.componentsData.deviceInfoFilter); + if (this.config.componentsData.deviceScope === 'edge' || this.config.componentsData.deviceScope === 'edge_customer_user') { + deviceInfoFilter.edgeId = new EdgeId(this.config.componentsData.edgeId); + } else if (this.config.componentsData.deviceScope !== 'tenant') { + deviceInfoFilter.customerId = new CustomerId(this.customerId); } + return new DeviceInfoQuery(pageLink, deviceInfoFilter); } configureCellActions(deviceScope: string): Array> { @@ -540,7 +604,8 @@ export class DevicesTableConfigResolver implements Resolve { if (isDefinedAndNotNull(deviceCredentials)) { diff --git a/ui-ngx/src/app/modules/home/pages/home-links/tenant_admin_home_page.raw b/ui-ngx/src/app/modules/home/pages/home-links/tenant_admin_home_page.raw index 6735860bd4..9205fddf71 100644 --- a/ui-ngx/src/app/modules/home/pages/home-links/tenant_admin_home_page.raw +++ b/ui-ngx/src/app/modules/home/pages/home-links/tenant_admin_home_page.raw @@ -648,9 +648,9 @@ "padding": "16px", "settings": { "useMarkdownTextFunction": false, - "markdownTextPattern": "", + "markdownTextPattern": "", "applyDefaultMarkdownStyle": false, - "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: row;\n}\n\n.tb-content-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: flex-start;\n gap: 12px;\n}\n\n.tb-card-header {\n height: 36px;\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n}\n\n.tb-item-cards {\n flex: 1;\n display: flex;\n flex-direction: row;\n gap: 12px;\n}\n\na.tb-item-card {\n flex: 1;\n display: flex;\n flex-direction: column;\n padding: 8px 12px;\n border: 1px solid;\n border-radius: 10px;\n margin-bottom: 12px;\n}\n\na.tb-item-card.tb-inactive {\n background: rgba(209, 39, 48, 0.04);\n border-color: rgba(209, 39, 48, 0.06);\n}\n\na.tb-item-card.tb-active {\n background: rgba(48, 86, 128, 0.04);\n border-color: rgba(48, 86, 128, 0.12);\n}\n\na.tb-item-card.tb-total {\n background: rgba(0, 0, 0, 0.01);\n border-color: rgba(0, 0, 0, 0.05);\n}\n\n.tb-item-title-container {\n display: grid;\n}\n\n.tb-item-title {\n font-weight: 400;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.2px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis; \n color: rgba(0, 0, 0, 0.76);\n}\n\n.tb-item-title.tb-home-widget-link:after {\n position: absolute;\n right: 0;\n}\n\na.tb-item-card:hover .tb-item-title.tb-home-widget-link:after { \n color: rgba(0, 0, 0, 0.38);\n}\n\na.tb-item-card:hover {\n box-shadow: 0px 4px 10px rgba(23, 33, 90, 0.08);\n}\n\n.tb-count-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n justify-content: center;\n}\n\n.tb-count {\n font-style: normal;\n font-weight: 500;\n font-size: 24px;\n line-height: 36px;\n white-space: nowrap;\n color: rgba(0, 0, 0, 0.87);\n}\n\n@media screen and (max-width: 959px) {\n .tb-item-cards {\n flex-direction: column;\n }\n a.tb-item-card {\n margin-bottom: 0;\n }\n}\n\n@media screen and (max-width: 1279px) {\n a.tb-item-card {\n flex-direction: row;\n align-items: center;\n }\n .tb-item-title.tb-home-widget-link:after {\n position: relative;\n }\n .tb-count-container {\n align-items: flex-end;\n }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-item-title {\n font-size: 11px;\n line-height: 16px;\n }\n .tb-count {\n font-size: 16px;\n line-height: 24px;\n }\n a.tb-item-card {\n padding: 4px 8px;\n margin-bottom: 6px;\n }\n a.tb-item-card:hover {\n box-shadow: 0px 2px 5px rgba(23, 33, 90, 0.08);\n }\n}\n" + "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: row;\n}\n\n.tb-content-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: flex-start;\n gap: 12px;\n}\n\n.tb-card-header {\n height: 36px;\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n}\n\n.tb-item-cards {\n flex: 1;\n display: flex;\n flex-direction: row;\n gap: 12px;\n overflow: hidden;\n}\n\na.tb-item-card {\n flex: 1;\n display: flex;\n flex-direction: column;\n padding: 8px 12px;\n border: 1px solid;\n border-radius: 10px;\n margin-bottom: 12px;\n overflow: hidden;\n justify-content: space-evenly;\n}\n\na.tb-item-card.tb-inactive {\n background: rgba(209, 39, 48, 0.04);\n border-color: rgba(209, 39, 48, 0.06);\n}\n\na.tb-item-card.tb-active {\n background: rgba(48, 86, 128, 0.04);\n border-color: rgba(48, 86, 128, 0.12);\n}\n\na.tb-item-card.tb-total {\n background: rgba(0, 0, 0, 0.01);\n border-color: rgba(0, 0, 0, 0.05);\n}\n\n.tb-item-title-container {\n display: grid;\n}\n\n.tb-item-title {\n font-weight: 400;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.2px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis; \n color: rgba(0, 0, 0, 0.76);\n}\n\n.tb-item-title.tb-home-widget-link:after {\n position: absolute;\n right: 0;\n}\n\na.tb-item-card:hover .tb-item-title.tb-home-widget-link:after { \n color: rgba(0, 0, 0, 0.38);\n}\n\na.tb-item-card:hover {\n box-shadow: 0px 4px 10px rgba(23, 33, 90, 0.08);\n}\n\n.tb-count-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n justify-content: center;\n}\n\n.tb-count {\n font-style: normal;\n font-weight: 500;\n font-size: 24px;\n line-height: 36px;\n white-space: nowrap;\n color: rgba(0, 0, 0, 0.87);\n}\n\n@media screen and (max-width: 959px) {\n .tb-item-cards {\n flex-direction: column;\n }\n a.tb-item-card {\n margin-bottom: 0;\n }\n}\n\n@media screen and (max-width: 1279px) {\n a.tb-item-card {\n flex-direction: row;\n align-items: center;\n }\n .tb-item-title.tb-home-widget-link:after {\n position: relative;\n }\n .tb-count-container {\n align-items: flex-end;\n }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-item-title {\n font-size: 11px;\n line-height: 16px;\n }\n .tb-count {\n font-size: 16px;\n line-height: 24px;\n }\n a.tb-item-card {\n padding: 4px 8px;\n margin-bottom: 6px;\n }\n a.tb-item-card:hover {\n box-shadow: 0px 2px 5px rgba(23, 33, 90, 0.08);\n }\n}\n" }, "title": "Devices", "showTitleIcon": false, @@ -804,7 +804,7 @@ "useMarkdownTextFunction": false, "markdownTextPattern": "", "applyDefaultMarkdownStyle": false, - "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: row;\n}\n\n.tb-content-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: flex-start;\n gap: 12px;\n}\n\n.tb-card-header {\n height: 36px;\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n}\n\n.tb-item-cards {\n flex: 1;\n display: flex;\n flex-direction: row;\n gap: 12px;\n}\n\na.tb-item-card {\n flex: 1;\n display: flex;\n flex-direction: column;\n padding: 8px 12px;\n border: 1px solid;\n border-radius: 10px;\n margin-bottom: 12px;\n}\n\na.tb-item-card.tb-critical {\n background: rgba(209, 39, 48, 0.04);\n border-color: rgba(209, 39, 48, 0.06);\n}\n\na.tb-item-card.tb-assigned {\n background: rgba(48, 86, 128, 0.04);\n border-color: rgba(48, 86, 128, 0.12);\n}\n\na.tb-item-card.tb-total {\n background: rgba(0, 0, 0, 0.01);\n border-color: rgba(0, 0, 0, 0.05);\n}\n\n.tb-item-title-container {\n display: grid;\n}\n\n.tb-item-title {\n font-weight: 400;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.2px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis; \n color: rgba(0, 0, 0, 0.76);\n}\n\n.tb-item-title.tb-home-widget-link:after {\n position: absolute;\n right: 0;\n}\n\na.tb-item-card:hover .tb-item-title.tb-home-widget-link:after { \n color: rgba(0, 0, 0, 0.38);\n}\n\na.tb-item-card:hover {\n box-shadow: 0px 4px 10px rgba(23, 33, 90, 0.08);\n}\n\n.tb-count-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n justify-content: center;\n}\n\n.tb-count {\n font-style: normal;\n font-weight: 500;\n font-size: 24px;\n line-height: 36px;\n white-space: nowrap;\n color: rgba(0, 0, 0, 0.87);\n}\n\na.tb-item-card.tb-critical .tb-count:after {\n content: \"warning\";\n display: inline-block;\n position: relative;\n font-family: 'Material Icons Round';\n color: #D12730;\n vertical-align: bottom;\n margin-left: 6px;\n}\n\n@media screen and (max-width: 959px) {\n .tb-item-cards {\n flex-direction: column;\n }\n a.tb-item-card {\n margin-bottom: 0;\n }\n}\n\n@media screen and (max-width: 1279px) {\n a.tb-item-card {\n flex-direction: row;\n align-items: center;\n }\n .tb-item-title.tb-home-widget-link:after {\n position: relative;\n }\n .tb-count-container {\n align-items: flex-end;\n }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-item-title {\n font-size: 11px;\n line-height: 16px;\n }\n .tb-count {\n font-size: 16px;\n line-height: 24px;\n }\n a.tb-item-card {\n padding: 4px 8px;\n margin-bottom: 6px;\n }\n a.tb-item-card:hover {\n box-shadow: 0px 2px 5px rgba(23, 33, 90, 0.08);\n }\n a.tb-item-card.tb-critical .tb-count:after {\n margin-left: 2px;\n }\n}\n" + "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: row;\n}\n\n.tb-content-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: flex-start;\n gap: 12px;\n}\n\n.tb-card-header {\n height: 36px;\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n}\n\n.tb-item-cards {\n flex: 1;\n display: flex;\n flex-direction: row;\n gap: 12px;\n overflow: hidden;\n}\n\na.tb-item-card {\n flex: 1;\n display: flex;\n flex-direction: column;\n padding: 8px 12px;\n border: 1px solid;\n border-radius: 10px;\n margin-bottom: 12px;\n overflow: hidden;\n justify-content: space-evenly;\n}\n\na.tb-item-card.tb-critical {\n background: rgba(209, 39, 48, 0.04);\n border-color: rgba(209, 39, 48, 0.06);\n}\n\na.tb-item-card.tb-assigned {\n background: rgba(48, 86, 128, 0.04);\n border-color: rgba(48, 86, 128, 0.12);\n}\n\na.tb-item-card.tb-total {\n background: rgba(0, 0, 0, 0.01);\n border-color: rgba(0, 0, 0, 0.05);\n}\n\n.tb-item-title-container {\n display: grid;\n}\n\n.tb-item-title {\n font-weight: 400;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.2px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis; \n color: rgba(0, 0, 0, 0.76);\n}\n\n.tb-item-title.tb-home-widget-link:after {\n position: absolute;\n right: 0;\n}\n\na.tb-item-card:hover .tb-item-title.tb-home-widget-link:after { \n color: rgba(0, 0, 0, 0.38);\n}\n\na.tb-item-card:hover {\n box-shadow: 0px 4px 10px rgba(23, 33, 90, 0.08);\n}\n\n.tb-count-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n justify-content: center;\n}\n\n.tb-count {\n font-style: normal;\n font-weight: 500;\n font-size: 24px;\n line-height: 36px;\n white-space: nowrap;\n color: rgba(0, 0, 0, 0.87);\n}\n\na.tb-item-card.tb-critical .tb-count:after {\n content: \"warning\";\n display: inline-block;\n position: relative;\n font-family: 'Material Icons Round';\n color: #D12730;\n vertical-align: bottom;\n margin-left: 6px;\n}\n\n@media screen and (max-width: 959px) {\n .tb-item-cards {\n flex-direction: column;\n }\n a.tb-item-card {\n margin-bottom: 0;\n }\n}\n\n@media screen and (max-width: 1279px) {\n a.tb-item-card {\n flex-direction: row;\n align-items: center;\n }\n .tb-item-title.tb-home-widget-link:after {\n position: relative;\n }\n .tb-count-container {\n align-items: flex-end;\n }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-item-title {\n font-size: 11px;\n line-height: 16px;\n }\n .tb-count {\n font-size: 16px;\n line-height: 24px;\n }\n a.tb-item-card {\n padding: 4px 8px;\n margin-bottom: 6px;\n }\n a.tb-item-card:hover {\n box-shadow: 0px 2px 5px rgba(23, 33, 90, 0.08);\n }\n a.tb-item-card.tb-critical .tb-count:after {\n margin-left: 2px;\n }\n}\n" }, "title": "Alarms", "showTitleIcon": false, diff --git a/ui-ngx/src/app/shared/models/device.models.ts b/ui-ngx/src/app/shared/models/device.models.ts index 1cf06e9ec6..94231a26f6 100644 --- a/ui-ngx/src/app/shared/models/device.models.ts +++ b/ui-ngx/src/app/shared/models/device.models.ts @@ -35,6 +35,9 @@ import { getDefaultProfileObserveAttrConfig, PowerMode } from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models'; +import { PageLink } from '@shared/models/page/page-link'; +import { isDefinedAndNotNull, isNotEmptyStr } from '@core/utils'; +import { EdgeId } from '@shared/models/id/edge-id'; export enum DeviceProfileType { DEFAULT = 'DEFAULT', @@ -328,7 +331,7 @@ export interface DeviceProvisionConfiguration { allowCreateNewDevicesByX509Certificate?: boolean; } -export function createDeviceProfileConfiguration(type: DeviceProfileType): DeviceProfileConfiguration { +export const createDeviceProfileConfiguration = (type: DeviceProfileType): DeviceProfileConfiguration => { let configuration: DeviceProfileConfiguration = null; if (type) { switch (type) { @@ -339,9 +342,9 @@ export function createDeviceProfileConfiguration(type: DeviceProfileType): Devic } } return configuration; -} +}; -export function createDeviceConfiguration(type: DeviceProfileType): DeviceConfiguration { +export const createDeviceConfiguration = (type: DeviceProfileType): DeviceConfiguration => { let configuration: DeviceConfiguration = null; if (type) { switch (type) { @@ -352,9 +355,9 @@ export function createDeviceConfiguration(type: DeviceProfileType): DeviceConfig } } return configuration; -} +}; -export function createDeviceProfileTransportConfiguration(type: DeviceTransportType): DeviceProfileTransportConfiguration { +export const createDeviceProfileTransportConfiguration = (type: DeviceTransportType): DeviceProfileTransportConfiguration => { let transportConfiguration: DeviceProfileTransportConfiguration = null; if (type) { switch (type) { @@ -408,9 +411,9 @@ export function createDeviceProfileTransportConfiguration(type: DeviceTransportT } } return transportConfiguration; -} +}; -export function createDeviceTransportConfiguration(type: DeviceTransportType): DeviceTransportConfiguration { +export const createDeviceTransportConfiguration = (type: DeviceTransportType): DeviceTransportConfiguration => { let transportConfiguration: DeviceTransportConfiguration = null; if (type) { switch (type) { @@ -446,7 +449,7 @@ export function createDeviceTransportConfiguration(type: DeviceTransportType): D } } return transportConfiguration; -} +}; export enum AlarmConditionType { SIMPLE = 'SIMPLE', @@ -489,7 +492,7 @@ export const AlarmScheduleTypeTranslationMap = new Map + !(!alarmRule || !alarmRule.condition || !alarmRule.condition.condition || !alarmRule.condition.condition.length); + +export const alarmRuleValidator = (control: AbstractControl): ValidationErrors | null => { const alarmRule: AlarmRule = control.value; return alarmRuleValid(alarmRule) ? null : {alarmRule: true}; -} - -function alarmRuleValid(alarmRule: AlarmRule): boolean { - if (!alarmRule || !alarmRule.condition || !alarmRule.condition.condition || !alarmRule.condition.condition.length) { - return false; - } - return true; -} +}; export interface DeviceProfileAlarm { id: string; @@ -539,7 +538,7 @@ export interface DeviceProfileAlarm { propagateRelationTypes?: Array; } -export function deviceProfileAlarmValidator(control: AbstractControl): ValidationErrors | null { +export const deviceProfileAlarmValidator = (control: AbstractControl): ValidationErrors | null => { const deviceProfileAlarm: DeviceProfileAlarm = control.value; if (deviceProfileAlarm && deviceProfileAlarm.id && deviceProfileAlarm.alarmType && deviceProfileAlarm.createRules) { @@ -564,7 +563,7 @@ export function deviceProfileAlarmValidator(control: AbstractControl): Validatio } } return {deviceProfileAlarm: true}; -} +}; export interface DeviceProfileData { @@ -718,6 +717,47 @@ export interface DeviceInfo extends Device { customerTitle: string; customerIsPublic: boolean; deviceProfileName: string; + active: boolean; +} + +export interface DeviceInfoFilter { + customerId?: CustomerId; + edgeId?: EdgeId; + type?: string; + deviceProfileId?: DeviceProfileId; + active?: boolean; +} + +export class DeviceInfoQuery { + + pageLink: PageLink; + deviceInfoFilter: DeviceInfoFilter; + + constructor(pageLink: PageLink, deviceInfoFilter: DeviceInfoFilter) { + this.pageLink = pageLink; + this.deviceInfoFilter = deviceInfoFilter; + } + + public toQuery(): string { + let query; + if (this.deviceInfoFilter.customerId) { + query = `/customer/${this.deviceInfoFilter.customerId.id}/deviceInfos`; + } else if (this.deviceInfoFilter.edgeId) { + query = `/edge/${this.deviceInfoFilter.edgeId.id}/devices`; + } else { + query = '/tenant/deviceInfos'; + } + query += this.pageLink.toQuery(); + if (isNotEmptyStr(this.deviceInfoFilter.type)) { + query += `&type=${this.deviceInfoFilter.type}`; + } else if (this.deviceInfoFilter.deviceProfileId) { + query += `&deviceProfileId=${this.deviceInfoFilter.deviceProfileId.id}`; + } + if (isDefinedAndNotNull(this.deviceInfoFilter.active)) { + query += `&active=${this.deviceInfoFilter.active}`; + } + return query; + } } export enum DeviceCredentialsType { @@ -763,13 +803,11 @@ export interface DeviceCredentialMQTTBasic { password: string; } -export function getDeviceCredentialMQTTDefault(): DeviceCredentialMQTTBasic { - return { - clientId: '', - userName: '', - password: '' - }; -} +export const getDeviceCredentialMQTTDefault = (): DeviceCredentialMQTTBasic => ({ + clientId: '', + userName: '', + password: '' +}); export interface DeviceSearchQuery extends EntitySearchQuery { deviceTypes: Array; @@ -800,44 +838,23 @@ export const dayOfWeekTranslations = new Array( 'device-profile.schedule-day.sunday' ); -export function getDayString(day: number): string { - switch (day) { - case 0: - return 'device-profile.schedule-day.monday'; - case 1: - return this.translate.instant('device-profile.schedule-day.tuesday'); - case 2: - return this.translate.instant('device-profile.schedule-day.wednesday'); - case 3: - return this.translate.instant('device-profile.schedule-day.thursday'); - case 4: - return this.translate.instant('device-profile.schedule-day.friday'); - case 5: - return this.translate.instant('device-profile.schedule-day.saturday'); - case 6: - return this.translate.instant('device-profile.schedule-day.sunday'); - } -} - -export function timeOfDayToUTCTimestamp(date: Date | number): number { +export const timeOfDayToUTCTimestamp = (date: Date | number): number => { if (typeof date === 'number' || date === null) { return 0; } return _moment.utc([1970, 0, 1, date.getHours(), date.getMinutes(), date.getSeconds(), 0]).valueOf(); -} +}; -export function utcTimestampToTimeOfDay(time = 0): Date { - return new Date(time + new Date(time).getTimezoneOffset() * 60 * 1000); -} +export const utcTimestampToTimeOfDay = (time = 0): Date => new Date(time + new Date(time).getTimezoneOffset() * 60 * 1000); -function timeOfDayToMoment(date: Date | number): _moment.Moment { +const timeOfDayToMoment = (date: Date | number): _moment.Moment => { if (typeof date === 'number' || date === null) { return _moment([1970, 0, 1, 0, 0, 0, 0]); } return _moment([1970, 0, 1, date.getHours(), date.getMinutes(), 0, 0]); -} +}; -export function getAlarmScheduleRangeText(startsOn: Date | number, endsOn: Date | number): string { +export const getAlarmScheduleRangeText = (startsOn: Date | number, endsOn: Date | number): string => { const start = timeOfDayToMoment(startsOn); const end = timeOfDayToMoment(endsOn); if (start < end) { @@ -847,4 +864,4 @@ export function getAlarmScheduleRangeText(startsOn: Date | number, endsOn: Date } return `12:00 AM – ${end.format('hh:mm A')}` + ` and ${start.format('hh:mm A')} – 12:00 PM`; -} +}; diff --git a/ui-ngx/src/assets/home/no_data_folder_bg.svg b/ui-ngx/src/assets/home/no_data_folder_bg.svg new file mode 100644 index 0000000000..5c3d9ef141 --- /dev/null +++ b/ui-ngx/src/assets/home/no_data_folder_bg.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + 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 3aac9e1909..bf36a7d81d 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1296,6 +1296,14 @@ "unable-delete-device-alias-text": "Device alias '{{deviceAlias}}' can't be deleted as it used by the following widget(s):
{{widgetsList}}", "is-gateway": "Is gateway", "overwrite-activity-time": "Overwrite activity time for connected device", + "device-filter": "Device filter", + "device-filter-title": "Device Filter", + "filter-title": "Filter", + "device-state": "Device state", + "state": "State", + "any": "Any", + "active": "Active", + "inactive": "Inactive", "public": "Public", "device-public": "Device is public", "select-device": "Select device", @@ -5131,6 +5139,9 @@ "color": "Color", "shadow-color": "Shadow color" }, + "home": { + "no-data-available": "No data available" + }, "system-info": { "cpu": "CPU", "ram": "RAM", diff --git a/ui-ngx/src/styles.scss b/ui-ngx/src/styles.scss index f008d765c5..39885d1191 100644 --- a/ui-ngx/src/styles.scss +++ b/ui-ngx/src/styles.scss @@ -266,7 +266,7 @@ pre.tb-highlight { letter-spacing: normal; } -.tb-timewindow-panel, .tb-legend-config-panel, .tb-alarm-filter-panel { +.tb-timewindow-panel, .tb-legend-config-panel, .tb-filter-panel { overflow: hidden; background: #fff; border-radius: 4px; From 0f66c5901b4285352bb7ff374a944b1b5d77e333 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Fri, 28 Apr 2023 16:05:10 +0200 Subject: [PATCH 26/61] fixed using default timeout and improvements --- .../server/service/state/DefaultDeviceStateService.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java index cb08e855c2..bc1bdb256f 100644 --- a/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java @@ -86,6 +86,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -294,7 +295,7 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService> devicesActivity = new HashMap<>(); partitionedEntities.forEach((tpi, deviceIds) -> { log.debug("Calculating state updates. tpi {} for {} devices", tpi.getFullTopicName(), deviceIds.size()); + Set idsFromRemovedTenant = new HashSet<>(); for (DeviceId deviceId : deviceIds) { DeviceStateData stateData; try { @@ -453,9 +455,11 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService tenantDevicesActivity = devicesActivity.computeIfAbsent(stateData.getTenantId(), @@ -466,6 +470,7 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService { int active = tenantDevicesActivity.getLeft().get(); From 89efe12c451aacf5d542a552d5e3d03fa0188c82 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Fri, 28 Apr 2023 17:09:01 +0300 Subject: [PATCH 27/61] Fix Equals And HashCode issue for Device/Asset/EntityView Info --- .../server/controller/DeviceController.java | 7 ++++ .../server/edge/AbstractEdgeTest.java | 13 +++---- .../server/edge/BaseDeviceEdgeTest.java | 35 ++++++++++++------- .../server/common/data/DeviceInfo.java | 4 +++ .../server/common/data/EntityViewInfo.java | 2 ++ .../server/common/data/asset/AssetInfo.java | 4 +++ 6 files changed, 46 insertions(+), 19 deletions(-) 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 b0358b4824..5ba2dd6171 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java @@ -689,6 +689,10 @@ public class DeviceController extends BaseController { @RequestParam int page, @ApiParam(value = DEVICE_TYPE_DESCRIPTION) @RequestParam(required = false) String type, + @ApiParam(value = DEVICE_PROFILE_ID_PARAM_DESCRIPTION) + @RequestParam(required = false) String deviceProfileId, + @ApiParam(value = DEVICE_ACTIVE_PARAM_DESCRIPTION) + @RequestParam(required = false) Boolean active, @ApiParam(value = DEVICE_TEXT_SEARCH_DESCRIPTION) @RequestParam(required = false) String textSearch, @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = DEVICE_SORT_PROPERTY_ALLOWABLE_VALUES) @@ -707,8 +711,11 @@ public class DeviceController extends BaseController { DeviceInfoFilter.DeviceInfoFilterBuilder filter = DeviceInfoFilter.builder(); filter.tenantId(tenantId); filter.edgeId(edgeId); + filter.active(active); if (type != null && type.trim().length() > 0) { filter.type(type); + } else if (deviceProfileId != null && deviceProfileId.length() > 0) { + filter.deviceProfileId(new DeviceProfileId(toUUID(deviceProfileId))); } return checkNotNull(deviceService.findDeviceInfosByFilter(filter.build(), pageLink)); } diff --git a/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java index f4370d615e..19360da30e 100644 --- a/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java @@ -31,6 +31,7 @@ import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.DeviceInfo; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.OtaPackageInfo; import org.thingsboard.server.common.data.SaveOtaPackageInfoRequest; @@ -322,9 +323,9 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { UUID deviceUUID = new UUID(deviceUpdateMsg.getIdMSB(), deviceUpdateMsg.getIdLSB()); Device device = doGet("/api/device/" + deviceUUID, Device.class); Assert.assertNotNull(device); - List edgeDevices = doGetTypedWithPageLink("/api/edge/" + edge.getUuidId() + "/devices?", - new TypeReference>() {}, new PageLink(100)).getData(); - Assert.assertTrue(edgeDevices.contains(device)); + List edgeDevices = doGetTypedWithPageLink("/api/edge/" + edge.getUuidId() + "/devices?", + new TypeReference>() {}, new PageLink(100)).getData(); + Assert.assertTrue(edgeDevices.stream().map(DeviceInfo::getId).anyMatch(id -> id.equals(device.getId()))); testAutoGeneratedCodeByProtobuf(deviceUpdateMsg); } @@ -487,10 +488,10 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { } protected Device findDeviceByName(String deviceName) throws Exception { - List edgeDevices = doGetTypedWithPageLink("/api/edge/" + edge.getUuidId() + "/devices?", - new TypeReference>() { + List edgeDevices = doGetTypedWithPageLink("/api/edge/" + edge.getUuidId() + "/devices?", + new TypeReference>() { }, new PageLink(100)).getData(); - Optional foundDevice = edgeDevices.stream().filter(d -> d.getName().equals(deviceName)).findAny(); + Optional foundDevice = edgeDevices.stream().filter(d -> d.getName().equals(deviceName)).findAny(); Assert.assertTrue(foundDevice.isPresent()); Device device = foundDevice.get(); Assert.assertEquals(deviceName, device.getName()); diff --git a/application/src/test/java/org/thingsboard/server/edge/BaseDeviceEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/BaseDeviceEdgeTest.java index 904a6246ab..fa140fc3fb 100644 --- a/application/src/test/java/org/thingsboard/server/edge/BaseDeviceEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/BaseDeviceEdgeTest.java @@ -25,11 +25,13 @@ import io.netty.handler.codec.mqtt.MqttQoS; import org.awaitility.Awaitility; import org.junit.Assert; import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.TestPropertySource; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.DeviceInfo; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.OtaPackageInfo; import org.thingsboard.server.common.data.StringUtils; @@ -46,11 +48,14 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentialsType; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.common.msg.session.FeatureType; import org.thingsboard.server.common.transport.adaptor.JsonConverter; +import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.gen.edge.v1.AttributesRequestMsg; import org.thingsboard.server.gen.edge.v1.DeviceCredentialsRequestMsg; import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg; @@ -378,18 +383,18 @@ abstract public class BaseDeviceEdgeTest extends AbstractEdgeTest { sendAttributesRequestAndVerify(device, DataConstants.SHARED_SCOPE, "{\"key2\":\"value2\"}", "key2", "value2"); - doDelete("/api/plugins/telemetry/DEVICE/" + device.getUuidId() + "/" + DataConstants.SERVER_SCOPE, "keys","key1, inactivityTimeout"); + doDelete("/api/plugins/telemetry/DEVICE/" + device.getUuidId() + "/" + DataConstants.SERVER_SCOPE, "keys", "key1, inactivityTimeout"); doDelete("/api/plugins/telemetry/DEVICE/" + device.getUuidId() + "/" + DataConstants.SHARED_SCOPE, "keys", "key2"); } @Test public void testSendDeleteDeviceOnEdgeToCloud() throws Exception { - Device device = saveDeviceOnCloudAndVerifyDeliveryToEdge(); + Device savedDevice = saveDeviceOnCloudAndVerifyDeliveryToEdge(); UplinkMsg.Builder upLinkMsgBuilder = UplinkMsg.newBuilder(); DeviceUpdateMsg.Builder deviceDeleteMsgBuilder = DeviceUpdateMsg.newBuilder(); deviceDeleteMsgBuilder.setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE); - deviceDeleteMsgBuilder.setIdMSB(device.getId().getId().getMostSignificantBits()); - deviceDeleteMsgBuilder.setIdLSB(device.getId().getId().getLeastSignificantBits()); + deviceDeleteMsgBuilder.setIdMSB(savedDevice.getId().getId().getMostSignificantBits()); + deviceDeleteMsgBuilder.setIdLSB(savedDevice.getId().getId().getLeastSignificantBits()); testAutoGeneratedCodeByProtobuf(deviceDeleteMsgBuilder); upLinkMsgBuilder.addDeviceUpdateMsg(deviceDeleteMsgBuilder.build()); @@ -398,12 +403,12 @@ abstract public class BaseDeviceEdgeTest extends AbstractEdgeTest { edgeImitator.expectResponsesAmount(1); edgeImitator.sendUplinkMsg(upLinkMsgBuilder.build()); Assert.assertTrue(edgeImitator.waitForResponses()); - device = doGet("/api/device/" + device.getUuidId(), Device.class); - Assert.assertNotNull(device); - List edgeDevices = doGetTypedWithPageLink("/api/edge/" + edge.getUuidId() + "/devices?", - new TypeReference>() { + DeviceInfo deviceInfo = doGet("/api/device/info/" + savedDevice.getUuidId(), DeviceInfo.class); + Assert.assertNotNull(deviceInfo); + List edgeDevices = doGetTypedWithPageLink("/api/edge/" + edge.getUuidId() + "/devices?", + new TypeReference>() { }, new PageLink(100)).getData(); - Assert.assertFalse(edgeDevices.contains(device)); + Assert.assertFalse(edgeDevices.contains(deviceInfo)); } @Test @@ -455,7 +460,8 @@ abstract public class BaseDeviceEdgeTest extends AbstractEdgeTest { Assert.assertEquals(timeseriesValue, timeseries.get(timeseriesKey).get(0).get("value")); String attributeValuesUrl = "/api/plugins/telemetry/DEVICE/" + device.getId() + "/values/attributes/" + DataConstants.SERVER_SCOPE; - List> attributes = doGetAsyncTyped(attributeValuesUrl, new TypeReference<>() {}); + List> attributes = doGetAsyncTyped(attributeValuesUrl, new TypeReference<>() { + }); Assert.assertEquals(3, attributes.size()); @@ -599,7 +605,8 @@ abstract public class BaseDeviceEdgeTest extends AbstractEdgeTest { .atMost(10, TimeUnit.SECONDS) .until(() -> { String urlTemplate = "/api/plugins/telemetry/DEVICE/" + device.getId() + "/keys/attributes/" + scope; - List actualKeys = doGetAsyncTyped(urlTemplate, new TypeReference<>() {}); + List actualKeys = doGetAsyncTyped(urlTemplate, new TypeReference<>() { + }); return actualKeys != null && !actualKeys.isEmpty() && actualKeys.contains(expectedKey); }); @@ -656,7 +663,8 @@ abstract public class BaseDeviceEdgeTest extends AbstractEdgeTest { private Map>> loadDeviceTimeseries(Device device, String timeseriesKey) throws Exception { return doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + device.getUuidId() + "/values/timeseries?keys=" + timeseriesKey, - new TypeReference<>() {}); + new TypeReference<>() { + }); } @Test @@ -713,7 +721,8 @@ abstract public class BaseDeviceEdgeTest extends AbstractEdgeTest { .atMost(10, TimeUnit.SECONDS) .until(() -> { String urlTemplate = "/api/plugins/telemetry/DEVICE/" + device.getId() + "/keys/timeseries"; - List actualKeys = doGetAsyncTyped(urlTemplate, new TypeReference<>() {}); + List actualKeys = doGetAsyncTyped(urlTemplate, new TypeReference<>() { + }); return actualKeys != null && !actualKeys.isEmpty() && actualKeys.contains("temperature"); }); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceInfo.java index c1ffbeb79e..3204cc217a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceInfo.java @@ -18,12 +18,16 @@ package org.thingsboard.server.common.data; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; +import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.id.DeviceId; @ApiModel @Data +@EqualsAndHashCode(callSuper = true) public class DeviceInfo extends Device { + private static final long serialVersionUID = -3004579925090663691L; + @ApiModelProperty(position = 13, value = "Title of the Customer that owns the device.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String customerTitle; @ApiModelProperty(position = 14, value = "Indicates special 'Public' Customer that is auto-generated to use the devices on public dashboards.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EntityViewInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/EntityViewInfo.java index f203a887cb..069ea3ae5d 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/EntityViewInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/EntityViewInfo.java @@ -17,9 +17,11 @@ package org.thingsboard.server.common.data; import io.swagger.annotations.ApiModelProperty; import lombok.Data; +import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.id.EntityViewId; @Data +@EqualsAndHashCode(callSuper = true) public class EntityViewInfo extends EntityView { @ApiModelProperty(position = 12, value = "Title of the Customer that owns the entity view.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/asset/AssetInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/asset/AssetInfo.java index fe8a36406a..b2d1fa95a2 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/asset/AssetInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/asset/AssetInfo.java @@ -18,12 +18,16 @@ package org.thingsboard.server.common.data.asset; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; +import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.id.AssetId; @ApiModel @Data +@EqualsAndHashCode(callSuper = true) public class AssetInfo extends Asset { + private static final long serialVersionUID = -4094528227011066194L; + @ApiModelProperty(position = 10, value = "Title of the Customer that owns the asset.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String customerTitle; @ApiModelProperty(position = 11, value = "Indicates special 'Public' Customer that is auto-generated to use the assets on public dashboards.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) From 27ef2694114867abf85da751e0d5c32723f4cf12 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 28 Apr 2023 17:21:42 +0300 Subject: [PATCH 28/61] UI: Fixed translate in edge tables and page title --- .../home/pages/edge/edge-routing.module.ts | 2 +- .../home/pages/edge/edge.component.html | 2 +- .../pages/edge/edges-table-config.resolver.ts | 18 +++++++++--------- .../rulechains-table-config.resolver.ts | 2 +- .../assets/locale/locale.constant-en_US.json | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/edge/edge-routing.module.ts b/ui-ngx/src/app/modules/home/pages/edge/edge-routing.module.ts index 5dcdee3169..9ec7d15aa0 100644 --- a/ui-ngx/src/app/modules/home/pages/edge/edge-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/edge/edge-routing.module.ts @@ -260,7 +260,7 @@ const routes: Routes = [ path: ':edgeId/ruleChains', data: { breadcrumb: { - label: 'edge.edge-rulechains', + label: 'edge.rulechains', icon: 'settings_ethernet' } }, diff --git a/ui-ngx/src/app/modules/home/pages/edge/edge.component.html b/ui-ngx/src/app/modules/home/pages/edge/edge.component.html index ef0c452a7b..9c9a78f91a 100644 --- a/ui-ngx/src/app/modules/home/pages/edge/edge.component.html +++ b/ui-ngx/src/app/modules/home/pages/edge/edge.component.html @@ -117,7 +117,7 @@ diff --git a/ui-ngx/src/app/modules/home/pages/edge/edges-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/edge/edges-table-config.resolver.ts index e73bae7c7a..1acaff3841 100644 --- a/ui-ngx/src/app/modules/home/pages/edge/edges-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/edge/edges-table-config.resolver.ts @@ -211,31 +211,31 @@ export class EdgesTableConfigResolver implements Resolve this.unassignFromCustomer($event, entity) }, { - name: this.translate.instant('edge.edge-assets'), + name: this.translate.instant('edge.manage-assets'), icon: 'domain', isEnabled: (entity) => true, onAction: ($event, entity) => this.openEdgeEntitiesByType($event, entity, EntityType.ASSET) }, { - name: this.translate.instant('edge.edge-devices'), + name: this.translate.instant('edge.manage-devices'), icon: 'devices_other', isEnabled: (entity) => true, onAction: ($event, entity) => this.openEdgeEntitiesByType($event, entity, EntityType.DEVICE) }, { - name: this.translate.instant('edge.edge-entity-views'), + name: this.translate.instant('edge.manage-entity-views'), icon: 'view_quilt', isEnabled: (entity) => true, onAction: ($event, entity) => this.openEdgeEntitiesByType($event, entity, EntityType.ENTITY_VIEW) }, { - name: this.translate.instant('edge.edge-dashboards'), + name: this.translate.instant('edge.manage-dashboards'), icon: 'dashboard', isEnabled: (entity) => true, onAction: ($event, entity) => this.openEdgeEntitiesByType($event, entity, EntityType.DASHBOARD) }, { - name: this.translate.instant('edge.edge-rulechains'), + name: this.translate.instant('edge.manage-rulechains'), icon: 'settings_ethernet', isEnabled: (entity) => true, onAction: ($event, entity) => this.openEdgeEntitiesByType($event, entity, EntityType.RULE_CHAIN) @@ -261,25 +261,25 @@ export class EdgesTableConfigResolver implements Resolve true, onAction: ($event, entity) => this.openEdgeEntitiesByType($event, entity, EntityType.ASSET) }, { - name: this.translate.instant('edge.edge-devices'), + name: this.translate.instant('edge.manage-devices'), icon: 'devices_other', isEnabled: (entity) => true, onAction: ($event, entity) => this.openEdgeEntitiesByType($event, entity, EntityType.DEVICE) }, { - name: this.translate.instant('edge.edge-entity-views'), + name: this.translate.instant('edge.manage-entity-views'), icon: 'view_quilt', isEnabled: (entity) => true, onAction: ($event, entity) => this.openEdgeEntitiesByType($event, entity, EntityType.ENTITY_VIEW) }, { - name: this.translate.instant('edge.edge-dashboards'), + name: this.translate.instant('edge.manage-dashboards'), icon: 'dashboard', isEnabled: (entity) => true, onAction: ($event, entity) => this.openEdgeEntitiesByType($event, entity, EntityType.DASHBOARD) diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rulechains-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/rulechain/rulechains-table-config.resolver.ts index b2c2a9eb79..37714a992c 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rulechains-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rulechains-table-config.resolver.ts @@ -198,7 +198,7 @@ export class RuleChainsTableConfigResolver implements Resolve Date: Fri, 28 Apr 2023 17:53:59 +0300 Subject: [PATCH 29/61] UI: Fixed for long key and value --- .../components/attribute/attribute-table.component.html | 8 ++++---- .../components/attribute/attribute-table.component.scss | 5 +++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html b/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html index a2d230bde9..e056140b71 100644 --- a/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html +++ b/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html @@ -162,7 +162,7 @@ {{ 'attribute.key' | translate }}
- {{ attribute.key }} + {{ attribute.key }} -
-
- {{attribute.value | tbJson}} +
+
+ {{attribute.value | tbJson}} Date: Fri, 28 Apr 2023 18:10:05 +0300 Subject: [PATCH 30/61] UI: Refactoring --- .../home/components/attribute/attribute-table.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html b/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html index e056140b71..5cacb555b9 100644 --- a/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html +++ b/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html @@ -181,7 +181,7 @@ class="tb-value-cell" (click)="editAttribute($event, attribute)">
-
+
{{attribute.value | tbJson}} Date: Mon, 1 May 2023 13:37:17 +0300 Subject: [PATCH 31/61] Fix Sparkplug tests --- .../AbstractMqttV5ClientSparkplugAttributesTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/mqtt/sparkplug/attributes/AbstractMqttV5ClientSparkplugAttributesTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/sparkplug/attributes/AbstractMqttV5ClientSparkplugAttributesTest.java index 4e05e44668..31d6233a69 100644 --- a/application/src/test/java/org/thingsboard/server/transport/mqtt/sparkplug/attributes/AbstractMqttV5ClientSparkplugAttributesTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/sparkplug/attributes/AbstractMqttV5ClientSparkplugAttributesTest.java @@ -31,7 +31,7 @@ import java.util.concurrent.atomic.AtomicReference; import static org.awaitility.Awaitility.await; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.thingsboard.server.common.data.DataConstants.CLIENT_SCOPE; +import static org.thingsboard.server.common.data.DataConstants.SHARED_SCOPE; import static org.thingsboard.server.transport.mqtt.util.sparkplug.MetricDataType.UInt32; import static org.thingsboard.server.transport.mqtt.util.sparkplug.SparkplugMessageType.NCMD; import static org.thingsboard.server.transport.mqtt.util.sparkplug.SparkplugTopicUtil.NAMESPACE; @@ -409,7 +409,7 @@ public abstract class AbstractMqttV5ClientSparkplugAttributesTest extends Abstra protected void processClientNodeWithCorrectAccessTokenPublish_AttributesInProfileContainsKeyAttributes() throws Exception { clientWithCorrectNodeAccessTokenWithNDEATH(); connectionWithNBirth(metricBirthDataType_Int32, metricBirthName_Int32, nextInt32()); - String urlTemplate = "/api/plugins/telemetry/DEVICE/" + savedGateway.getId().getId() + "/keys/attributes/" + CLIENT_SCOPE; + String urlTemplate = "/api/plugins/telemetry/DEVICE/" + savedGateway.getId().getId() + "/keys/attributes/" + SHARED_SCOPE; AtomicReference> actualKeys = new AtomicReference<>(); await(alias + SparkplugMessageType.NBIRTH.name()) .atMost(40, TimeUnit.SECONDS) @@ -424,7 +424,7 @@ public abstract class AbstractMqttV5ClientSparkplugAttributesTest extends Abstra protected void processClientDeviceWithCorrectAccessTokenPublish_AttributesInProfileContainsKeyAttributes() throws Exception { long ts = calendar.getTimeInMillis(); List devices = connectClientWithCorrectAccessTokenWithNDEATHCreatedDevices(1, ts); - String urlTemplate = "/api/plugins/telemetry/DEVICE/" + devices.get(0).getId().getId() + "/keys/attributes/" + CLIENT_SCOPE; + String urlTemplate = "/api/plugins/telemetry/DEVICE/" + devices.get(0).getId().getId() + "/keys/attributes/" + SHARED_SCOPE; AtomicReference> actualKeys = new AtomicReference<>(); await(alias + SparkplugMessageType.DBIRTH.name()) .atMost(40, TimeUnit.SECONDS) From 1f334d58ad9c19ce3e771df6af0cb8b95122d413 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Mon, 1 May 2023 15:41:55 +0300 Subject: [PATCH 32/61] Introduce SystemParams object holding necessary UI system parameters --- .../controller/SystemInfoController.java | 90 ++++++++++++++++- .../server/common/data/SystemParams.java | 33 +++++++ ui-ngx/src/app/core/auth/auth.models.ts | 5 + ui-ngx/src/app/core/auth/auth.reducer.ts | 1 + ui-ngx/src/app/core/auth/auth.selectors.ts | 5 + ui-ngx/src/app/core/auth/auth.service.ts | 98 +++---------------- ui-ngx/src/app/core/services/time.service.ts | 16 +-- .../home-links/home-links-routing.module.ts | 44 +++++++-- 8 files changed, 185 insertions(+), 107 deletions(-) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/SystemParams.java diff --git a/application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java b/application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java index 19195c7681..b90b367a2d 100644 --- a/application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java +++ b/application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java @@ -22,27 +22,59 @@ import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.info.BuildProperties; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.DashboardInfo; +import org.thingsboard.server.common.data.SystemParams; +import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.common.data.settings.UserSettings; +import org.thingsboard.server.common.data.settings.UserSettingsType; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.security.model.SecurityUser; +import org.thingsboard.server.service.security.model.UserPrincipal; +import org.thingsboard.server.service.sync.vc.EntitiesVersionControlService; import springfox.documentation.annotations.ApiIgnore; import javax.annotation.PostConstruct; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; @ApiIgnore @RestController @TbCoreComponent @RequestMapping("/api") @Slf4j -public class SystemInfoController { +public class SystemInfoController extends BaseController { + + @Value("${security.user_token_access_enabled}") + private boolean userTokenAccessEnabled; + + @Value("${tbel.enabled:true}") + private boolean tbelEnabled; + + @Value("${state.persistToTelemetry:false}") + private boolean persistToTelemetry; + + @Value("${ui.dashboard.max_datapoints_limit}") + private long maxDatapointsLimit; @Autowired(required = false) private BuildProperties buildProperties; + @Autowired + private EntitiesVersionControlService versionControlService; + @PostConstruct public void init() { JsonNode info = buildInfoObject(); @@ -56,6 +88,62 @@ public class SystemInfoController { return buildInfoObject(); } + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/system/params", method = RequestMethod.GET) + @ResponseBody + public SystemParams getSystemParams() throws ThingsboardException { + SystemParams systemParams = new SystemParams(); + SecurityUser currentUser = getCurrentUser(); + TenantId tenantId = currentUser.getTenantId(); + CustomerId customerId = currentUser.getCustomerId(); + if (currentUser.isSystemAdmin() || currentUser.isTenantAdmin()) { + systemParams.setUserTokenAccessEnabled(userTokenAccessEnabled); + } else { + systemParams.setUserTokenAccessEnabled(false); + } + boolean forceFullscreen = isForceFullscreen(currentUser); + if (forceFullscreen && (currentUser.isTenantAdmin() || currentUser.isCustomerUser())) { + PageLink pageLink = new PageLink(100); + List dashboards; + if (currentUser.isTenantAdmin()) { + dashboards = dashboardService.findDashboardsByTenantId(tenantId, pageLink).getData(); + } else { + dashboards = dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink).getData(); + } + systemParams.setAllowedDashboardIds(dashboards.stream().map(d -> d.getId().getId().toString()).collect(Collectors.toList())); + } else { + systemParams.setAllowedDashboardIds(Collections.emptyList()); + } + systemParams.setEdgesSupportEnabled(edgesEnabled); + if (currentUser.isTenantAdmin()) { + systemParams.setHasRepository(versionControlService.getVersionControlSettings(tenantId) != null); + systemParams.setTbelEnabled(tbelEnabled); + } else { + systemParams.setHasRepository(false); + systemParams.setTbelEnabled(false); + } + if (currentUser.isTenantAdmin() || currentUser.isCustomerUser()) { + systemParams.setPersistDeviceStateToTelemetry(persistToTelemetry); + } else { + systemParams.setPersistDeviceStateToTelemetry(false); + } + UserSettings userSettings = userSettingsService.findUserSettings(currentUser.getTenantId(), currentUser.getId(), UserSettingsType.GENERAL); + ObjectNode userSettingsNode = userSettings == null ? JacksonUtil.newObjectNode() : (ObjectNode) userSettings.getSettings(); + if (!userSettingsNode.has("openedMenuSections")) { + userSettingsNode.set("openedMenuSections", JacksonUtil.newArrayNode()); + } + systemParams.setUserSettings(userSettingsNode); + systemParams.setMaxDatapointsLimit(maxDatapointsLimit); + return systemParams; + } + + private boolean isForceFullscreen(SecurityUser currentUser) { + return UserPrincipal.Type.PUBLIC_ID.equals(currentUser.getUserPrincipal().getType()) || + (currentUser.getAdditionalInfo() != null && + currentUser.getAdditionalInfo().has("defaultDashboardFullscreen") && + currentUser.getAdditionalInfo().get("defaultDashboardFullscreen").booleanValue()); + } + private JsonNode buildInfoObject() { ObjectMapper objectMapper = new ObjectMapper(); ObjectNode infoObject = objectMapper.createObjectNode(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/SystemParams.java b/common/data/src/main/java/org/thingsboard/server/common/data/SystemParams.java new file mode 100644 index 0000000000..1e7c0281a4 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/SystemParams.java @@ -0,0 +1,33 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Data; + +import java.util.List; + +@Data +public class SystemParams { + boolean userTokenAccessEnabled; + List allowedDashboardIds; + boolean edgesSupportEnabled; + boolean hasRepository; + boolean tbelEnabled; + boolean persistDeviceStateToTelemetry; + JsonNode userSettings; + long maxDatapointsLimit; +} diff --git a/ui-ngx/src/app/core/auth/auth.models.ts b/ui-ngx/src/app/core/auth/auth.models.ts index 8fd1c214b5..607a2cb78d 100644 --- a/ui-ngx/src/app/core/auth/auth.models.ts +++ b/ui-ngx/src/app/core/auth/auth.models.ts @@ -23,9 +23,14 @@ export interface SysParamsState { edgesSupportEnabled: boolean; hasRepository: boolean; tbelEnabled: boolean; + persistDeviceStateToTelemetry: boolean; userSettings: UserSettings; } +export interface SysParams extends SysParamsState { + maxDatapointsLimit: number; +} + export interface AuthPayload extends SysParamsState { authUser: AuthUser; userDetails: User; diff --git a/ui-ngx/src/app/core/auth/auth.reducer.ts b/ui-ngx/src/app/core/auth/auth.reducer.ts index 51786543e6..0847654399 100644 --- a/ui-ngx/src/app/core/auth/auth.reducer.ts +++ b/ui-ngx/src/app/core/auth/auth.reducer.ts @@ -27,6 +27,7 @@ const emptyUserAuthState: AuthPayload = { edgesSupportEnabled: false, hasRepository: false, tbelEnabled: false, + persistDeviceStateToTelemetry: false, userSettings: initialUserSettings }; diff --git a/ui-ngx/src/app/core/auth/auth.selectors.ts b/ui-ngx/src/app/core/auth/auth.selectors.ts index 45162565d9..2e8406293e 100644 --- a/ui-ngx/src/app/core/auth/auth.selectors.ts +++ b/ui-ngx/src/app/core/auth/auth.selectors.ts @@ -66,6 +66,11 @@ export const selectTbelEnabled = createSelector( (state: AuthState) => state.tbelEnabled ); +export const selectPersistDeviceStateToTelemetry = createSelector( + selectAuthState, + (state: AuthState) => state.persistDeviceStateToTelemetry +); + export const selectUserSettings = createSelector( selectAuthState, (state: AuthState) => state.userSettings diff --git a/ui-ngx/src/app/core/auth/auth.service.ts b/ui-ngx/src/app/core/auth/auth.service.ts index d1be94fdb6..87c6561315 100644 --- a/ui-ngx/src/app/core/auth/auth.service.ts +++ b/ui-ngx/src/app/core/auth/auth.service.ts @@ -18,7 +18,7 @@ import { Injectable, NgZone } from '@angular/core'; import { JwtHelperService } from '@auth0/angular-jwt'; import { HttpClient } from '@angular/common/http'; -import { forkJoin, Observable, of, ReplaySubject, throwError } from 'rxjs'; +import { Observable, of, ReplaySubject, throwError } from 'rxjs'; import { catchError, map, mergeMap, tap } from 'rxjs/operators'; import { LoginRequest, LoginResponse, PublicLoginRequest } from '@shared/models/login.models'; @@ -31,25 +31,17 @@ import { ActionAuthAuthenticated, ActionAuthLoadUser, ActionAuthUnauthenticated import { getCurrentAuthState, getCurrentAuthUser } from './auth.selectors'; import { Authority } from '@shared/models/authority.enum'; import { ActionSettingsChangeLanguage } from '@app/core/settings/settings.actions'; -import { AuthPayload, AuthState, SysParamsState } from '@core/auth/auth.models'; +import { AuthPayload, AuthState, SysParams, SysParamsState } from '@core/auth/auth.models'; import { TranslateService } from '@ngx-translate/core'; import { AuthUser } from '@shared/models/user.model'; import { TimeService } from '@core/services/time.service'; import { UtilsService } from '@core/services/utils.service'; -import { DashboardService } from '@core/http/dashboard.service'; -import { PageLink } from '@shared/models/page/page-link'; -import { DashboardInfo } from '@shared/models/dashboard.models'; -import { PageData } from '@app/shared/models/page/page-data'; -import { AdminService } from '@core/http/admin.service'; -import { ActionNotificationShow } from '@core/notification/notification.actions'; import { MatDialog, MatDialogConfig } from '@angular/material/dialog'; import { AlertDialogComponent } from '@shared/components/dialog/alert-dialog.component'; import { OAuth2ClientInfo, PlatformType } from '@shared/models/oauth2.models'; import { isMobileApp } from '@core/utils'; import { TwoFactorAuthProviderType, TwoFaProviderInfo } from '@shared/models/two-factor-auth.models'; import { UserPasswordPolicy } from '@shared/models/settings.models'; -import { UserSettings } from '@shared/models/user-settings.models'; -import { UserSettingsService } from '@core/http/user-settings.service'; @Injectable({ providedIn: 'root' @@ -65,10 +57,7 @@ export class AuthService { private route: ActivatedRoute, private zone: NgZone, private utils: UtilsService, - private dashboardService: DashboardService, - private adminService: AdminService, private translate: TranslateService, - private userSettingsService: UserSettingsService, private dialog: MatDialog ) { } @@ -400,7 +389,7 @@ export class AuthService { authPayload.forceFullscreen = true; } if (authPayload.authUser.isPublic) { - this.loadSystemParams(authPayload).subscribe( + this.loadSystemParams().subscribe( (sysParams) => { authPayload = {...authPayload, ...sysParams}; loadUserSubject.next(authPayload); @@ -421,7 +410,7 @@ export class AuthService { if (this.userForceFullscreen(authPayload)) { authPayload.forceFullscreen = true; } - this.loadSystemParams(authPayload).subscribe( + this.loadSystemParams().subscribe( (sysParams) => { authPayload = {...authPayload, ...sysParams}; let userLang; @@ -455,57 +444,14 @@ export class AuthService { return loadUserSubject; } - private loadIsUserTokenAccessEnabled(authUser: AuthUser): Observable { - if (authUser.authority === Authority.SYS_ADMIN || - authUser.authority === Authority.TENANT_ADMIN) { - return this.http.get('/api/user/tokenAccessEnabled', defaultHttpOptions()); - } else { - return of(false); - } - } - - public loadIsEdgesSupportEnabled(): Observable { - return this.http.get('/api/edges/enabled', defaultHttpOptions()); - } - - private loadHasRepository(authUser: AuthUser): Observable { - if (authUser.authority === Authority.TENANT_ADMIN) { - return this.http.get('/api/admin/repositorySettings/exists', defaultHttpOptions()); - } else { - return of(false); - } - } - - private loadTbelEnabled(authUser: AuthUser): Observable { - if (authUser.authority === Authority.TENANT_ADMIN) { - return this.http.get('/api/ruleChain/tbelEnabled', defaultHttpOptions()); - } else { - return of(false); - } - } - - private loadUserSettings(): Observable { - return this.userSettingsService.loadUserSettings(); - } - - private loadSystemParams(authPayload: AuthPayload): Observable { - const sources = [this.loadIsUserTokenAccessEnabled(authPayload.authUser), - this.fetchAllowedDashboardIds(authPayload), - this.loadIsEdgesSupportEnabled(), - this.loadHasRepository(authPayload.authUser), - this.loadTbelEnabled(authPayload.authUser), - this.loadUserSettings(), - this.timeService.loadMaxDatapointsLimit()]; - return forkJoin(sources) - .pipe(map((data) => { - const userTokenAccessEnabled: boolean = data[0] as boolean; - const allowedDashboardIds: string[] = data[1] as string[]; - const edgesSupportEnabled: boolean = data[2] as boolean; - const hasRepository: boolean = data[3] as boolean; - const tbelEnabled: boolean = data[4] as boolean; - const userSettings = data[5] as UserSettings; - return {userTokenAccessEnabled, allowedDashboardIds, edgesSupportEnabled, hasRepository, tbelEnabled, userSettings}; - }, catchError((err) => of({})))); + private loadSystemParams(): Observable { + return this.http.get('/api/system/params', defaultHttpOptions()).pipe( + map((sysParams) => { + this.timeService.setMaxDatapointsLimit(sysParams.maxDatapointsLimit); + return sysParams; + }), + catchError(() => of({} as SysParamsState)) + ); } public refreshJwtToken(loadUserElseStoreJwtToken = true): Observable { @@ -689,24 +635,4 @@ export class AuthService { } } - private fetchAllowedDashboardIds(authPayload: AuthPayload): Observable { - if (authPayload.forceFullscreen && (authPayload.authUser.authority === Authority.TENANT_ADMIN || - authPayload.authUser.authority === Authority.CUSTOMER_USER)) { - const pageLink = new PageLink(100); - let fetchDashboardsObservable: Observable>; - if (authPayload.authUser.authority === Authority.TENANT_ADMIN) { - fetchDashboardsObservable = this.dashboardService.getTenantDashboards(pageLink); - } else { - fetchDashboardsObservable = this.dashboardService.getCustomerDashboards(authPayload.authUser.customerId, pageLink); - } - return fetchDashboardsObservable.pipe( - map((result) => { - const dashboards = result.data; - return dashboards.map(dashboard => dashboard.id.id); - }) - ); - } else { - return of([]); - } - } } diff --git a/ui-ngx/src/app/core/services/time.service.ts b/ui-ngx/src/app/core/services/time.service.ts index 829f245331..f9ab858b4f 100644 --- a/ui-ngx/src/app/core/services/time.service.ts +++ b/ui-ngx/src/app/core/services/time.service.ts @@ -53,17 +53,11 @@ export class TimeService { private http: HttpClient ) {} - public loadMaxDatapointsLimit(): Observable { - return this.http.get('/api/dashboard/maxDatapointsLimit', - defaultHttpOptions(true)).pipe( - map((limit) => { - this.maxDatapointsLimit = limit; - if (!this.maxDatapointsLimit || this.maxDatapointsLimit <= MIN_LIMIT) { - this.maxDatapointsLimit = MIN_LIMIT + 1; - } - return this.maxDatapointsLimit; - }) - ); + public setMaxDatapointsLimit(limit: number) { + this.maxDatapointsLimit = limit; + if (!this.maxDatapointsLimit || this.maxDatapointsLimit <= MIN_LIMIT) { + this.maxDatapointsLimit = MIN_LIMIT + 1; + } } public matchesExistingInterval(min: number, max: number, intervalMs: number): boolean { diff --git a/ui-ngx/src/app/modules/home/pages/home-links/home-links-routing.module.ts b/ui-ngx/src/app/modules/home/pages/home-links/home-links-routing.module.ts index a046172c3e..5edc9cb7fc 100644 --- a/ui-ngx/src/app/modules/home/pages/home-links/home-links-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/home-links/home-links-routing.module.ts @@ -19,16 +19,21 @@ import { Resolve, RouterModule, Routes } from '@angular/router'; import { HomeLinksComponent } from './home-links.component'; import { Authority } from '@shared/models/authority.enum'; -import { Observable } from 'rxjs'; +import { mergeMap, Observable, of } from 'rxjs'; import { HomeDashboard } from '@shared/models/dashboard.models'; import { DashboardService } from '@core/http/dashboard.service'; -import { Store } from '@ngrx/store'; +import { select, Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { map } from 'rxjs/operators'; -import { getCurrentAuthUser } from '@core/auth/auth.selectors'; +import { + getCurrentAuthUser, + selectHasRepository, + selectPersistDeviceStateToTelemetry +} from '@core/auth/auth.selectors'; import sysAdminHomePageDashboardJson from '!raw-loader!./sys_admin_home_page.raw'; import tenantAdminHomePageDashboardJson from '!raw-loader!./tenant_admin_home_page.raw'; import customerUserHomePageDashboardJson from '!raw-loader!./customer_user_home_page.raw'; +import { EntityKeyType } from '@shared/models/query/query.models'; @Injectable() export class HomeDashboardResolver implements Resolve { @@ -39,22 +44,43 @@ export class HomeDashboardResolver implements Resolve { resolve(): Observable { return this.dashboardService.getHomeDashboard().pipe( - map((dashboard) => { + mergeMap((dashboard) => { if (!dashboard) { + let dashboard$: Observable; const authority = getCurrentAuthUser(this.store).authority; switch (authority) { case Authority.SYS_ADMIN: - dashboard = JSON.parse(sysAdminHomePageDashboardJson); + dashboard$ = of(JSON.parse(sysAdminHomePageDashboardJson)); break; case Authority.TENANT_ADMIN: - dashboard = JSON.parse(tenantAdminHomePageDashboardJson); + dashboard$ = this.updateDeviceActivityKeyFilterIfNeeded(JSON.parse(tenantAdminHomePageDashboardJson)); break; case Authority.CUSTOMER_USER: - dashboard = JSON.parse(customerUserHomePageDashboardJson); + dashboard$ = this.updateDeviceActivityKeyFilterIfNeeded(JSON.parse(customerUserHomePageDashboardJson)); break; } - if (dashboard) { - dashboard.hideDashboardToolbar = true; + if (dashboard$) { + return dashboard$.pipe( + map((homeDashboard) => { + homeDashboard.hideDashboardToolbar = true; + return homeDashboard; + }) + ); + } + } + return of(dashboard); + }) + ); + } + + private updateDeviceActivityKeyFilterIfNeeded(dashboard: HomeDashboard): Observable { + return this.store.pipe(select(selectPersistDeviceStateToTelemetry)).pipe( + map((persistToTelemetry) => { + if (persistToTelemetry) { + for (const filterId of Object.keys(dashboard.configuration.filters)) { + if (['Active Devices', 'Inactive Devices'].includes(dashboard.configuration.filters[filterId].filter)) { + dashboard.configuration.filters[filterId].keyFilters[0].key.type = EntityKeyType.TIME_SERIES; + } } } return dashboard; From 0dd8161790bc02cee46a2428a51e811d282f77ab Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Mon, 1 May 2023 16:26:49 +0300 Subject: [PATCH 33/61] UI: Copy button visible on hover --- .../attribute/attribute-table.component.html | 6 ++++-- .../attribute/attribute-table.component.scss | 12 ++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html b/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html index 5cacb555b9..0a57bfef9b 100644 --- a/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html +++ b/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html @@ -161,9 +161,10 @@ {{ 'attribute.key' | translate }} -
+
{{ attribute.key }}
-
+
{{attribute.value | tbJson}} Date: Mon, 1 May 2023 16:57:56 +0300 Subject: [PATCH 34/61] UI: correct custom action code samples --- .../components/widget/action/custom-sample-css.raw | 2 -- .../components/widget/action/custom-sample-html.raw | 2 +- .../home/components/widget/action/custom-sample-js.raw | 4 ++-- .../examples/custom_pretty_create_dialog_html.md | 4 ++-- .../action/examples/custom_pretty_edit_dialog_html.md | 10 +++++----- .../action/examples/custom_pretty_edit_dialog_js.md | 4 ++-- .../custom_pretty_create_dialog_html.md | 4 ++-- .../custom_pretty_edit_dialog_html.md | 10 +++++----- .../custom_pretty_edit_dialog_js.md | 4 ++-- 9 files changed, 21 insertions(+), 23 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/action/custom-sample-css.raw b/ui-ngx/src/app/modules/home/components/widget/action/custom-sample-css.raw index a8c69855c7..1f1de13677 100644 --- a/ui-ngx/src/app/modules/home/components/widget/action/custom-sample-css.raw +++ b/ui-ngx/src/app/modules/home/components/widget/action/custom-sample-css.raw @@ -9,7 +9,6 @@ } .edit-entity-form .boolean-value-input .checkbox-label { - margin-bottom: 8px; color: rgba(0,0,0,0.54); font-size: 12px; } @@ -61,7 +60,6 @@ } .add-entity-form .boolean-value-input .checkbox-label { - margin-bottom: 8px; color: rgba(0,0,0,0.54); font-size: 12px; } diff --git a/ui-ngx/src/app/modules/home/components/widget/action/custom-sample-html.raw b/ui-ngx/src/app/modules/home/components/widget/action/custom-sample-html.raw index f42fcd5813..95e11ccef6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/action/custom-sample-html.raw +++ b/ui-ngx/src/app/modules/home/components/widget/action/custom-sample-html.raw @@ -111,7 +111,7 @@ - + diff --git a/ui-ngx/src/app/modules/home/components/widget/action/custom-sample-js.raw b/ui-ngx/src/app/modules/home/components/widget/action/custom-sample-js.raw index 96dc54d998..30e25a0a5b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/action/custom-sample-js.raw +++ b/ui-ngx/src/app/modules/home/components/widget/action/custom-sample-js.raw @@ -136,9 +136,9 @@ // vm.relations().markAsDirty(); // }; // -// vm.removeOldRelation = function(index) { +// vm.removeOldRelation = function(index, relation) { // vm.oldRelations().removeAt(index); -// vm.relationsToDelete.push(vm.oldRelationsData[index]); +// vm.relationsToDelete.push(relation); // vm.oldRelations().markAsDirty(); // }; // diff --git a/ui-ngx/src/assets/help/en_US/widget/action/examples/custom_pretty_create_dialog_html.md b/ui-ngx/src/assets/help/en_US/widget/action/examples/custom_pretty_create_dialog_html.md index 567e093c76..e9a2a90ef7 100644 --- a/ui-ngx/src/assets/help/en_US/widget/action/examples/custom_pretty_create_dialog_html.md +++ b/ui-ngx/src/assets/help/en_US/widget/action/examples/custom_pretty_create_dialog_html.md @@ -82,7 +82,7 @@
- + {{ (addEntityFormGroup.get('attributes.booleanValue').value ? "value.true" : "value.false") | translate }}
@@ -98,7 +98,7 @@ Direction - + {{ ("relation.search-direction." + direction.value) | translate}} diff --git a/ui-ngx/src/assets/help/en_US/widget/action/examples/custom_pretty_edit_dialog_html.md b/ui-ngx/src/assets/help/en_US/widget/action/examples/custom_pretty_edit_dialog_html.md index 4e3cd0c608..e2f4f531bc 100644 --- a/ui-ngx/src/assets/help/en_US/widget/action/examples/custom_pretty_edit_dialog_html.md +++ b/ui-ngx/src/assets/help/en_US/widget/action/examples/custom_pretty_edit_dialog_html.md @@ -5,7 +5,7 @@
-

Edit

+

Edit {{entityType.toLowerCase()}} {{entityName}}

@@ -84,7 +84,7 @@ Direction - + {{ ("relation.search-direction." + direction.value) | translate}} @@ -109,7 +109,7 @@
@@ -98,7 +98,7 @@ Direction - + {{ ("relation.search-direction." + direction.value) | translate}} diff --git a/ui-ngx/src/assets/help/en_US/widget/action/examples_custom_pretty/custom_pretty_edit_dialog_html.md b/ui-ngx/src/assets/help/en_US/widget/action/examples_custom_pretty/custom_pretty_edit_dialog_html.md index 4e3cd0c608..05f494666f 100644 --- a/ui-ngx/src/assets/help/en_US/widget/action/examples_custom_pretty/custom_pretty_edit_dialog_html.md +++ b/ui-ngx/src/assets/help/en_US/widget/action/examples_custom_pretty/custom_pretty_edit_dialog_html.md @@ -5,7 +5,7 @@ -

Edit

+

Edit {{entityType.toLowerCase()}} {{entityName}}

@@ -84,7 +84,7 @@ Direction - + {{ ("relation.search-direction." + direction.value) | translate}} @@ -109,7 +109,7 @@ + + {{ 'device.client-id-pattern' | translate }} @@ -26,6 +45,25 @@ device.user-name + + + {{ 'device.user-name-required' | translate }} @@ -37,6 +75,25 @@ (ngModelChange)="passwordChanged()" type="password"> + + + device.access-token + + {{ 'device.access-token-required' | translate }} @@ -41,6 +50,15 @@ device.certificate-pem-format + + {{ 'device.certificate-pem-format-required' | translate }} diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html index fb78ba0b97..00492970dc 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html @@ -95,6 +95,7 @@ Date: Wed, 3 May 2023 15:26:53 +0300 Subject: [PATCH 56/61] Default valid keys cache for 24 hours --- dao/src/main/java/org/thingsboard/server/dao/util/KvUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/util/KvUtils.java b/dao/src/main/java/org/thingsboard/server/dao/util/KvUtils.java index ec6f5540c8..b0d1f123c9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/util/KvUtils.java +++ b/dao/src/main/java/org/thingsboard/server/dao/util/KvUtils.java @@ -32,7 +32,7 @@ public class KvUtils { static { validatedKeys = Caffeine.newBuilder() .weakKeys() - .expireAfterAccess(60, TimeUnit.MINUTES) + .expireAfterAccess(24, TimeUnit.HOURS) .maximumSize(100000).build(); } From 4a53707124dcbc5f3ae3904c70656550ed1b71b9 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Wed, 3 May 2023 15:59:31 +0300 Subject: [PATCH 57/61] UI: Refactoring --- ...vice-credentials-mqtt-basic.component.html | 65 ++++++++++--------- ...device-credentials-mqtt-basic.component.ts | 2 +- .../device/device-credentials.component.html | 23 +++---- .../device/device-credentials.component.ts | 2 +- .../admin/oauth2-settings.component.html | 6 +- .../button/copy-button.component.html | 2 +- .../button/copy-button.component.scss | 2 +- .../button/copy-button.component.ts | 3 +- 8 files changed, 56 insertions(+), 49 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/device/device-credentials-mqtt-basic.component.html b/ui-ngx/src/app/modules/home/components/device/device-credentials-mqtt-basic.component.html index 03b0f7a28b..692289b753 100644 --- a/ui-ngx/src/app/modules/home/components/device/device-credentials-mqtt-basic.component.html +++ b/ui-ngx/src/app/modules/home/components/device/device-credentials-mqtt-basic.component.html @@ -26,18 +26,19 @@ matTooltip="{{ 'device.generate-client-id' | translate }}" matTooltipPosition="above" (click)="generate('clientId')" - *ngIf="!deviceCredentialsMqttFormGroup.get('clientId').value"> + *ngIf="!deviceCredentialsMqttFormGroup.get('clientId').value; else copyClientId"> autorenew - - + + + + {{ 'device.client-id-pattern' | translate }} @@ -52,18 +53,20 @@ matTooltip="{{ 'device.generate-user-name' | translate }}" matTooltipPosition="above" (click)="generate('userName')" - *ngIf="!deviceCredentialsMqttFormGroup.get('userName').value"> + *ngIf="!deviceCredentialsMqttFormGroup.get('userName').value; else copyUserName"> autorenew - - + + + + {{ 'device.user-name-required' | translate }} @@ -82,18 +85,20 @@ matTooltip="{{ 'device.generate-password' | translate }}" matTooltipPosition="above" (click)="generate('password')" - *ngIf="!deviceCredentialsMqttFormGroup.get('password').value"> + *ngIf="!deviceCredentialsMqttFormGroup.get('password').value; else copyPassword"> autorenew - - + + + + + *ngIf="!deviceCredentialsFormGroup.get('credentialsId').value; else copyAccessToken"> autorenew - - + + + + {{ 'device.access-token-required' | translate }} @@ -62,7 +63,7 @@