From 3518be97a4c27266572681b6d7abfbf570cbe844 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Tue, 2 Dec 2025 15:19:47 +0200 Subject: [PATCH 1/5] fixed java rest client --- .../msa/connectivity/JavaRestClientTest.java | 318 +++++++++++++++++- .../thingsboard/rest/client/RestClient.java | 58 +++- 2 files changed, 364 insertions(+), 12 deletions(-) diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/JavaRestClientTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/JavaRestClientTest.java index 636c80d5b3..7cee0a6ba6 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/JavaRestClientTest.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/JavaRestClientTest.java @@ -15,10 +15,14 @@ */ package org.thingsboard.server.msa.connectivity; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.TextNode; +import com.google.gson.JsonObject; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClients; -import org.apache.hc.client5.http.io.HttpClientConnectionManager; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder; +import org.apache.hc.client5.http.io.HttpClientConnectionManager; import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy; import org.apache.hc.client5.http.ssl.HostnameVerificationPolicy; import org.apache.hc.client5.http.ssl.NoopHostnameVerifier; @@ -30,26 +34,83 @@ import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rest.client.RestClient; import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.EntityType; +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.alarm.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; import org.thingsboard.server.common.data.alarm.AlarmSeverity; +import org.thingsboard.server.common.data.domain.Domain; +import org.thingsboard.server.common.data.domain.DomainInfo; +import org.thingsboard.server.common.data.id.NotificationTargetId; +import org.thingsboard.server.common.data.id.NotificationTemplateId; +import org.thingsboard.server.common.data.id.UUIDBased; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.mobile.app.MobileApp; +import org.thingsboard.server.common.data.mobile.app.MobileAppStatus; +import org.thingsboard.server.common.data.mobile.bundle.MobileAppBundle; +import org.thingsboard.server.common.data.mobile.bundle.MobileAppBundleInfo; +import org.thingsboard.server.common.data.notification.Notification; +import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; +import org.thingsboard.server.common.data.notification.NotificationRequest; +import org.thingsboard.server.common.data.notification.NotificationRequestConfig; +import org.thingsboard.server.common.data.notification.NotificationRequestInfo; +import org.thingsboard.server.common.data.notification.NotificationRequestPreview; +import org.thingsboard.server.common.data.notification.NotificationType; +import org.thingsboard.server.common.data.notification.settings.NotificationSettings; +import org.thingsboard.server.common.data.notification.settings.SlackNotificationDeliveryMethodConfig; +import org.thingsboard.server.common.data.notification.settings.UserNotificationSettings; +import org.thingsboard.server.common.data.notification.targets.NotificationTarget; +import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig; +import org.thingsboard.server.common.data.notification.targets.platform.UserListFilter; +import org.thingsboard.server.common.data.notification.targets.platform.UsersFilter; +import org.thingsboard.server.common.data.notification.template.DeliveryMethodNotificationTemplate; +import org.thingsboard.server.common.data.notification.template.EmailDeliveryMethodNotificationTemplate; +import org.thingsboard.server.common.data.notification.template.HasSubject; +import org.thingsboard.server.common.data.notification.template.MobileAppDeliveryMethodNotificationTemplate; +import org.thingsboard.server.common.data.notification.template.NotificationTemplate; +import org.thingsboard.server.common.data.notification.template.NotificationTemplateConfig; +import org.thingsboard.server.common.data.notification.template.SmsDeliveryMethodNotificationTemplate; +import org.thingsboard.server.common.data.notification.template.WebDeliveryMethodNotificationTemplate; +import org.thingsboard.server.common.data.oauth2.PlatformType; import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.TimePageLink; +import org.thingsboard.server.common.data.query.EntityDataPageLink; +import org.thingsboard.server.common.data.query.EntityDataQuery; +import org.thingsboard.server.common.data.query.EntityDataSortOrder; +import org.thingsboard.server.common.data.query.EntityKey; +import org.thingsboard.server.common.data.query.EntityKeyType; +import org.thingsboard.server.common.data.query.EntityTypeFilter; +import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.msa.AbstractContainerTest; import org.thingsboard.server.msa.TestProperties; import javax.net.ssl.SSLContext; +import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; +import static org.thingsboard.server.common.data.notification.NotificationDeliveryMethod.EMAIL; +import static org.thingsboard.server.common.data.notification.NotificationDeliveryMethod.MICROSOFT_TEAMS; +import static org.thingsboard.server.common.data.notification.NotificationDeliveryMethod.WEB; import static org.thingsboard.server.msa.prototypes.DevicePrototypes.defaultDevicePrototype; +import static org.thingsboard.server.msa.ui.utils.EntityPrototypes.defaultTenantAdmin; public class JavaRestClientTest extends AbstractContainerTest { + public static final String DEFAULT_NOTIFICATION_SUBJECT = "Just a test"; + public static final NotificationType DEFAULT_NOTIFICATION_TYPE = NotificationType.GENERAL; private RestClient restClient; + private Tenant tenant; + private User user; @BeforeClass public void beforeClass() throws Exception { @@ -77,11 +138,27 @@ public class JavaRestClientTest extends AbstractContainerTest { @BeforeMethod public void setUp() throws Exception { - restClient.login("tenant@thingsboard.org", "tenant"); + restClient.login("sysadmin@thingsboard.org", "sysadmin"); + + // create tenant and tenant admin + tenant = new Tenant(); + tenant.setTitle("Java Rest Client Test Tenant " + RandomStringUtils.randomAlphabetic(5)); + tenant = restClient.saveTenant(tenant); + + String email = RandomStringUtils.randomAlphabetic(5) + "@gmail.com"; + + user = restClient.saveUser(defaultTenantAdmin(tenant.getId(), email), false); + + restClient.activateUser(user.getId(), "password123", false); + restClient.login(email, "password123"); } @AfterMethod public void tearDown() { + restClient.login("sysadmin@thingsboard.org", "sysadmin"); + if (tenant != null) { + restClient.deleteTenant(tenant.getId()); + } } @Test @@ -123,6 +200,243 @@ public class JavaRestClientTest extends AbstractContainerTest { PageData allClearedAlarms = restClient.getAllAlarms(AlarmSearchStatus.CLEARED, null, new TimePageLink(10, 0), null); assertThat(allClearedAlarms.getData()).hasSize(0); + } + + @Test + public void testTimeSeriesByReadTsKvQueries() { + Device device = restClient.saveDevice(defaultDevicePrototype(RandomStringUtils.randomAlphabetic(5))); + assertThat(device).isNotNull(); + + DeviceCredentials deviceCredentials = restClient.getDeviceCredentialsByDeviceId(device.getId()).get(); + for (int i = 0; i < 3; i++) { + JsonObject values = new JsonObject(); + values.addProperty("temperature", i + 25); + testRestClient.postTelemetry(deviceCredentials.getCredentialsId(), JacksonUtil.toJsonNode(createPayload().toString())); + } + + restClient.saveEntityTelemetry(device.getId(), "ts", JacksonUtil.toJsonNode("{\"temperature\": 25, \"humidity\": 60}")); + restClient.saveEntityTelemetry(device.getId(), "ts", JacksonUtil.toJsonNode("{\"temperature\": 27, \"humidity\": 59}")); + restClient.saveEntityTelemetry(device.getId(), "ts", JacksonUtil.toJsonNode("{\"temperature\": 33, \"humidity\": 62}")); + + EntityTypeFilter filter = new EntityTypeFilter(); + filter.setEntityType(EntityType.DEVICE); + var pageLink = new EntityDataPageLink(20, 0, null, new EntityDataSortOrder(new EntityKey(EntityKeyType.ENTITY_FIELD, "createdTime"), EntityDataSortOrder.Direction.DESC), false); + + var entityFields = Arrays.asList(new EntityKey(EntityKeyType.ENTITY_FIELD, "name"), new EntityKey(EntityKeyType.ENTITY_FIELD, "createdTime")); + + EntityDataQuery entityDataQuery = new EntityDataQuery(filter, pageLink, entityFields, null, null); + JsonNode result = restClient.findEntityTimeseriesAndAttributesKeysByQuery(entityDataQuery, true, true, null); + assertThat(result).isNotNull(); + assertThat((ArrayNode)result.get("timeseries")).contains(new TextNode("temperature"), new TextNode("humidity")); + } + + @Test + public void testFindNotifications() { + NotificationTarget notificationTarget = createNotificationTarget(user.getId()); + String notificationText1 = "Notification 1"; + NotificationTemplate notificationTemplate = createNotificationTemplate(DEFAULT_NOTIFICATION_TYPE, DEFAULT_NOTIFICATION_SUBJECT, notificationText1, new NotificationDeliveryMethod[]{WEB}); + NotificationRequest notificationRequest = submitNotificationRequest(notificationTarget.getId(), notificationTemplate.getId()); + + String notificationText2 = "Notification 2"; + NotificationTemplate notificationTemplate2 = createNotificationTemplate(DEFAULT_NOTIFICATION_TYPE, DEFAULT_NOTIFICATION_SUBJECT, notificationText2, new NotificationDeliveryMethod[]{WEB}); + NotificationRequest notificationRequest2 = submitNotificationRequest(notificationTarget.getId(), notificationTemplate2.getId()); + + PageData initialRequests = restClient.getNotificationRequests(new PageLink(30)); + assertThat(initialRequests.getTotalElements()).isGreaterThanOrEqualTo(2); + + NotificationRequestInfo notificationRequestInfo = restClient.getNotificationRequestById(notificationRequest.getId()).get(); + assertThat(notificationRequestInfo.getName()).isEqualTo(notificationRequest.getName()); + assertThat(notificationRequestInfo.getTemplateName()).isEqualTo(notificationTemplate.getName()); + + NotificationRequestPreview requestPreview = restClient.getNotificationRequestPreview(notificationRequest, 10); + assertThat(requestPreview.getTotalRecipientsCount()).isEqualTo(1); + assertThat(requestPreview.getRecipientsPreview()).isEqualTo(List.of(user.getEmail())); + + PageData notifications = restClient.getNotifications(false, WEB, new PageLink(30)); + assertThat(notifications.getTotalElements()).isEqualTo(2); + + Integer unreadCount = restClient.getUnreadNotificationsCount(WEB); + assertThat(unreadCount).isEqualTo(2); + + restClient.markNotificationAsRead(notifications.getData().get(0).getId()); + + Integer unreadCountAfterRead = restClient.getUnreadNotificationsCount(WEB); + assertThat(unreadCountAfterRead).isEqualTo(1); + + restClient.markAllNotificationsAsRead(WEB); + + Integer unreadCountAfterAllRead = restClient.getUnreadNotificationsCount(WEB); + assertThat(unreadCountAfterAllRead).isEqualTo(0); + + restClient.deleteNotification(notifications.getData().get(0).getId()); + notifications = restClient.getNotifications(false, WEB, new PageLink(30)); + assertThat(notifications.getTotalElements()).isEqualTo(1); + + restClient.deleteNotificationRequest(notificationRequest.getId()); + PageData requestsAfterUpdate = restClient.getNotificationRequests(new PageLink(30)); + assertThat(requestsAfterUpdate.getTotalElements()).isEqualTo(initialRequests.getTotalElements() - 1); + List availableDeliveryMethods = restClient.getAvailableDeliveryMethods(); + assertThat(availableDeliveryMethods).contains(WEB, EMAIL, MICROSOFT_TEAMS); + } + + @Test + public void testSaveNotificationSettings() { + NotificationSettings settings = new NotificationSettings(); + SlackNotificationDeliveryMethodConfig slackConfig = new SlackNotificationDeliveryMethodConfig(); + String slackToken = "xoxb-123123123"; + slackConfig.setBotToken(slackToken); + settings.setDeliveryMethodsConfigs(Map.of( + NotificationDeliveryMethod.SLACK, slackConfig + )); + + restClient.saveNotificationSettings(settings); + + NotificationSettings savedSettings = restClient.getNotificationSettings().get(); + assertThat(savedSettings.getDeliveryMethodsConfigs()).hasSize(1); + assertThat(savedSettings.getDeliveryMethodsConfigs().get(slackConfig.getMethod())).isEqualTo(slackConfig); + + // save user notification settings + var entityActionNotificationPref = new UserNotificationSettings.NotificationPref(); + entityActionNotificationPref.setEnabled(true); + entityActionNotificationPref.setEnabledDeliveryMethods(Map.of( + NotificationDeliveryMethod.WEB, true, + NotificationDeliveryMethod.SMS, false, + NotificationDeliveryMethod.EMAIL, false + )); + + var entitiesLimitNotificationPref = new UserNotificationSettings.NotificationPref(); + entitiesLimitNotificationPref.setEnabled(true); + entitiesLimitNotificationPref.setEnabledDeliveryMethods(Map.of( + NotificationDeliveryMethod.SMS, true, + NotificationDeliveryMethod.WEB, false, + NotificationDeliveryMethod.EMAIL, false + )); + + var apiUsageLimitNotificationPref = new UserNotificationSettings.NotificationPref(); + apiUsageLimitNotificationPref.setEnabled(false); + apiUsageLimitNotificationPref.setEnabledDeliveryMethods(Map.of( + NotificationDeliveryMethod.WEB, true, + NotificationDeliveryMethod.SMS, false, + NotificationDeliveryMethod.EMAIL, false + )); + + UserNotificationSettings userNotificationSettings = new UserNotificationSettings(Map.of( + NotificationType.ENTITY_ACTION, entityActionNotificationPref, + NotificationType.ENTITIES_LIMIT, entitiesLimitNotificationPref, + NotificationType.API_USAGE_LIMIT, apiUsageLimitNotificationPref + )); + UserNotificationSettings saved = restClient.saveUserNotificationSettings(userNotificationSettings); + UserNotificationSettings retrieved = restClient.getUserNotificationSettings().get(); + assertThat(retrieved).isEqualTo(saved); + } + + @Test + public void testSaveDomain() { + restClient.login("sysadmin@thingsboard.org", "sysadmin"); + + Domain domain = new Domain(); + String prefix = RandomStringUtils.randomAlphabetic(5).toLowerCase(); + domain.setName(prefix + ".test.com"); + Domain savedDomain = restClient.saveDomain(domain); + assertThat(savedDomain.getName()).isEqualTo(domain.getName()); + + PageData tenantDomainInfos = restClient.getTenantDomainInfos(new PageLink(10)); + List domainInfos = tenantDomainInfos.getData().stream().filter(domainInfo -> domainInfo.getName().startsWith(prefix)).toList(); + assertThat(domainInfos).hasSize(1); + } + + @Test + public void testSaveMobileApp() { + restClient.login("sysadmin@thingsboard.org", "sysadmin"); + + MobileApp mobileApp = new MobileApp(); + String prefix = RandomStringUtils.randomAlphabetic(5).toLowerCase(); + mobileApp.setPkgName(prefix + "test.app.apple"); + mobileApp.setPlatformType(PlatformType.ANDROID); + mobileApp.setAppSecret(RandomStringUtils.randomAlphabetic(20)); + mobileApp.setStatus(MobileAppStatus.DRAFT); + + MobileApp savedMobileApp = restClient.saveMobileApp(mobileApp); + assertThat(savedMobileApp.getName()).isEqualTo(mobileApp.getName()); + + PageData mobileApps = restClient.getTenantMobileApps(new PageLink(10)); + List retrieved = mobileApps.getData().stream().filter(app -> app.getPkgName().startsWith(prefix)).toList(); + assertThat(retrieved).hasSize(1); + + MobileAppBundle mobileAppBundle = new MobileAppBundle(); + String bundlePrefix = RandomStringUtils.randomAlphabetic(5).toLowerCase(); + mobileAppBundle.setTitle(bundlePrefix + "Test Bundle"); + mobileAppBundle.setAndroidAppId(savedMobileApp.getId()); + + MobileAppBundle savedMobileAppBundle = restClient.saveMobileBundle(mobileAppBundle); + PageData mobileBundleInfos = restClient.getTenantMobileBundleInfos(new PageLink(10)); + List bundleInfos = mobileBundleInfos.getData().stream().filter(mobileAppBundleInfo -> mobileAppBundleInfo.getTitle().startsWith(bundlePrefix)).toList(); + assertThat(bundleInfos).hasSize(1); + } + + protected NotificationTarget createNotificationTarget(UserId... usersIds) { + UserListFilter filter = new UserListFilter(); + filter.setUsersIds(Arrays.stream(usersIds).map(UUIDBased::getId).toList()); + return createNotificationTarget(filter); + } + + protected NotificationTarget createNotificationTarget(UsersFilter usersFilter) { + NotificationTarget notificationTarget = new NotificationTarget(); + notificationTarget.setName(usersFilter.toString() + org.apache.commons.lang3.RandomStringUtils.randomNumeric(5)); + PlatformUsersNotificationTargetConfig targetConfig = new PlatformUsersNotificationTargetConfig(); + targetConfig.setUsersFilter(usersFilter); + notificationTarget.setConfiguration(targetConfig); + return restClient.createNotificationTarget(notificationTarget); + } + + protected NotificationTemplate createNotificationTemplate(NotificationType notificationType, String subject, + String text, NotificationDeliveryMethod... deliveryMethods) { + NotificationTemplate notificationTemplate = new NotificationTemplate(); + notificationTemplate.setName("Notification template: " + RandomStringUtils.randomAlphabetic(5)); + notificationTemplate.setNotificationType(notificationType); + NotificationTemplateConfig config = new NotificationTemplateConfig(); + config.setDeliveryMethodsTemplates(new HashMap<>()); + for (NotificationDeliveryMethod deliveryMethod : deliveryMethods) { + DeliveryMethodNotificationTemplate deliveryMethodNotificationTemplate; + switch (deliveryMethod) { + case WEB: { + deliveryMethodNotificationTemplate = new WebDeliveryMethodNotificationTemplate(); + break; + } + case EMAIL: { + deliveryMethodNotificationTemplate = new EmailDeliveryMethodNotificationTemplate(); + break; + } + case SMS: { + deliveryMethodNotificationTemplate = new SmsDeliveryMethodNotificationTemplate(); + break; + } + case MOBILE_APP: + deliveryMethodNotificationTemplate = new MobileAppDeliveryMethodNotificationTemplate(); + break; + default: + throw new IllegalArgumentException("Unsupported delivery method " + deliveryMethod); + } + deliveryMethodNotificationTemplate.setEnabled(true); + deliveryMethodNotificationTemplate.setBody(text); + if (deliveryMethodNotificationTemplate instanceof HasSubject) { + ((HasSubject) deliveryMethodNotificationTemplate).setSubject(subject); + } + config.getDeliveryMethodsTemplates().put(deliveryMethod, deliveryMethodNotificationTemplate); + } + notificationTemplate.setConfiguration(config); + return restClient.createNotificationTemplate(notificationTemplate); + } + + protected NotificationRequest submitNotificationRequest(NotificationTargetId targetId, NotificationTemplateId notificationTemplateId) { + NotificationRequestConfig config = new NotificationRequestConfig(); + config.setSendingDelayInSec(0); + NotificationRequest notificationRequest = NotificationRequest.builder() + .targets(List.of(targetId).stream().map(UUIDBased::getId).collect(Collectors.toList())) + .templateId(notificationTemplateId) + .additionalConfig(config) + .build(); + return restClient.createNotificationRequest(notificationRequest); } } diff --git a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java index 2c8e01d246..28304de049 100644 --- a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java +++ b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java @@ -144,6 +144,8 @@ import org.thingsboard.server.common.data.notification.NotificationRequestInfo; import org.thingsboard.server.common.data.notification.NotificationRequestPreview; import org.thingsboard.server.common.data.notification.settings.NotificationSettings; import org.thingsboard.server.common.data.notification.settings.UserNotificationSettings; +import org.thingsboard.server.common.data.notification.targets.NotificationTarget; +import org.thingsboard.server.common.data.notification.template.NotificationTemplate; import org.thingsboard.server.common.data.oauth2.OAuth2Client; import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; import org.thingsboard.server.common.data.oauth2.OAuth2ClientLoginInfo; @@ -1824,12 +1826,24 @@ public class RestClient implements Closeable { }).getBody(); } - public JsonNode findEntityTimeseriesAndAttributesKeysByQuery(EntityDataQuery query) { + public JsonNode findEntityTimeseriesAndAttributesKeysByQuery(EntityDataQuery query, boolean isTimeseries, boolean isAttributes, String scope) { + Map params = new HashMap<>(); + params.put("timeseries", String.valueOf(isTimeseries)); + params.put("attributes", String.valueOf(isAttributes)); + + StringBuilder urlBuilder = new StringBuilder(baseURL); + urlBuilder.append("/api/entitiesQuery/find/keys?timeseries={timeseries}&attributes={attributes}"); + + if (scope != null) { + urlBuilder.append("&scope={scope}"); + params.put("scope", scope); + } return restTemplate.exchange( - baseURL + "/api/entitiesQuery/find/keys", + urlBuilder.toString(), HttpMethod.POST, new HttpEntity<>(query), new ParameterizedTypeReference() { - }).getBody(); + }, + params).getBody(); } public PageData findAlarmDataByQuery(AlarmDataQuery query) { @@ -2288,7 +2302,8 @@ public class RestClient implements Closeable { HttpMethod.GET, HttpEntity.EMPTY, new ParameterizedTypeReference>() { - }).getBody(); + }, + params).getBody(); } public Optional getDomainInfoById(DomainId domainId) { @@ -2324,7 +2339,8 @@ public class RestClient implements Closeable { HttpMethod.GET, HttpEntity.EMPTY, new ParameterizedTypeReference>() { - }).getBody(); + }, + params).getBody(); } public Optional getMobileAppById(MobileAppId mobileAppId) { @@ -2356,7 +2372,8 @@ public class RestClient implements Closeable { HttpMethod.GET, HttpEntity.EMPTY, new ParameterizedTypeReference>() { - }).getBody(); + }, + params).getBody(); } public Optional getMobileBundleById(MobileAppBundleId mobileAppBundleId) { @@ -4283,11 +4300,23 @@ public class RestClient implements Closeable { } } - public PageData getNotifications(PageLink pageLink) { + public PageData getNotifications(Boolean unreadOnly, NotificationDeliveryMethod deliveryMethod, PageLink pageLink) { Map params = new HashMap<>(); + + StringBuilder urlBuilder = new StringBuilder(); + urlBuilder.append(baseURL).append("/api/notifications?").append(getUrlParams(pageLink)); addPageLinkToParam(params, pageLink); - return restTemplate.exchange( - baseURL + "/api/notifications?" + getUrlParams(pageLink), + + if (unreadOnly != null) { + urlBuilder.append("&unreadOnly={unreadOnly}"); + params.put("unreadOnly", unreadOnly.toString()); + } + if (deliveryMethod != null) { + urlBuilder.append("&deliveryMethod={deliveryMethod}"); + params.put("deliveryMethod", deliveryMethod.name()); + } + + return restTemplate.exchange(urlBuilder.toString(), HttpMethod.GET, HttpEntity.EMPTY, new ParameterizedTypeReference>() { @@ -4328,7 +4357,8 @@ public class RestClient implements Closeable { baseURL + uri, HttpMethod.PUT, HttpEntity.EMPTY, - Void.class); + Void.class, + params); } @@ -4415,6 +4445,14 @@ public class RestClient implements Closeable { } } + public NotificationTarget createNotificationTarget(NotificationTarget notificationTarget) { + return restTemplate.postForEntity(baseURL + "/api/notification/target", notificationTarget, NotificationTarget.class).getBody(); + } + + public NotificationTemplate createNotificationTemplate(NotificationTemplate notificationTemplate) { + return restTemplate.postForEntity(baseURL + "/api/notification/template", notificationTemplate, NotificationTemplate.class).getBody(); + } + public AiModel saveAiModel(AiModel aiModel) { return restTemplate.postForEntity(baseURL + "/api/ai/model", aiModel, AiModel.class).getBody(); } From 9da2b57a6c465b9540ac4c0c940dcfda108776c6 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Tue, 2 Dec 2025 16:27:14 +0200 Subject: [PATCH 2/5] fixed compilation after merge with master --- .../server/msa/connectivity/JavaRestClientTest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/JavaRestClientTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/JavaRestClientTest.java index 7cee0a6ba6..f96f9aa953 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/JavaRestClientTest.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/JavaRestClientTest.java @@ -80,6 +80,7 @@ import org.thingsboard.server.common.data.oauth2.PlatformType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.TimePageLink; +import org.thingsboard.server.common.data.query.AvailableEntityKeys; import org.thingsboard.server.common.data.query.EntityDataPageLink; import org.thingsboard.server.common.data.query.EntityDataQuery; import org.thingsboard.server.common.data.query.EntityDataSortOrder; @@ -225,9 +226,9 @@ public class JavaRestClientTest extends AbstractContainerTest { var entityFields = Arrays.asList(new EntityKey(EntityKeyType.ENTITY_FIELD, "name"), new EntityKey(EntityKeyType.ENTITY_FIELD, "createdTime")); EntityDataQuery entityDataQuery = new EntityDataQuery(filter, pageLink, entityFields, null, null); - JsonNode result = restClient.findEntityTimeseriesAndAttributesKeysByQuery(entityDataQuery, true, true, null); - assertThat(result).isNotNull(); - assertThat((ArrayNode)result.get("timeseries")).contains(new TextNode("temperature"), new TextNode("humidity")); + AvailableEntityKeys availableEntityKeys = restClient.findAvailableEntityKeysByQuery(entityDataQuery, true, true, null); + assertThat(availableEntityKeys).isNotNull(); + assertThat(availableEntityKeys.timeseries()).contains("temperature", "humidity"); } @Test From bf4ffc8f9c7ae5b60e893cacdb46b3d24c6f9e7b Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Tue, 2 Dec 2025 16:50:18 +0200 Subject: [PATCH 3/5] refactoring --- .../msa/connectivity/JavaRestClientTest.java | 39 +++---------------- 1 file changed, 6 insertions(+), 33 deletions(-) diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/JavaRestClientTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/JavaRestClientTest.java index f96f9aa953..9d0292cc7e 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/JavaRestClientTest.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/JavaRestClientTest.java @@ -15,9 +15,6 @@ */ package org.thingsboard.server.msa.connectivity; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.TextNode; import com.google.gson.JsonObject; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClients; @@ -67,7 +64,6 @@ import org.thingsboard.server.common.data.notification.settings.UserNotification import org.thingsboard.server.common.data.notification.targets.NotificationTarget; import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig; import org.thingsboard.server.common.data.notification.targets.platform.UserListFilter; -import org.thingsboard.server.common.data.notification.targets.platform.UsersFilter; import org.thingsboard.server.common.data.notification.template.DeliveryMethodNotificationTemplate; import org.thingsboard.server.common.data.notification.template.EmailDeliveryMethodNotificationTemplate; import org.thingsboard.server.common.data.notification.template.HasSubject; @@ -147,9 +143,7 @@ public class JavaRestClientTest extends AbstractContainerTest { tenant = restClient.saveTenant(tenant); String email = RandomStringUtils.randomAlphabetic(5) + "@gmail.com"; - user = restClient.saveUser(defaultTenantAdmin(tenant.getId(), email), false); - restClient.activateUser(user.getId(), "password123", false); restClient.login(email, "password123"); } @@ -306,26 +300,8 @@ public class JavaRestClientTest extends AbstractContainerTest { NotificationDeliveryMethod.EMAIL, false )); - var entitiesLimitNotificationPref = new UserNotificationSettings.NotificationPref(); - entitiesLimitNotificationPref.setEnabled(true); - entitiesLimitNotificationPref.setEnabledDeliveryMethods(Map.of( - NotificationDeliveryMethod.SMS, true, - NotificationDeliveryMethod.WEB, false, - NotificationDeliveryMethod.EMAIL, false - )); - - var apiUsageLimitNotificationPref = new UserNotificationSettings.NotificationPref(); - apiUsageLimitNotificationPref.setEnabled(false); - apiUsageLimitNotificationPref.setEnabledDeliveryMethods(Map.of( - NotificationDeliveryMethod.WEB, true, - NotificationDeliveryMethod.SMS, false, - NotificationDeliveryMethod.EMAIL, false - )); - UserNotificationSettings userNotificationSettings = new UserNotificationSettings(Map.of( - NotificationType.ENTITY_ACTION, entityActionNotificationPref, - NotificationType.ENTITIES_LIMIT, entitiesLimitNotificationPref, - NotificationType.API_USAGE_LIMIT, apiUsageLimitNotificationPref + NotificationType.ENTITY_ACTION, entityActionNotificationPref )); UserNotificationSettings saved = restClient.saveUserNotificationSettings(userNotificationSettings); UserNotificationSettings retrieved = restClient.getUserNotificationSettings().get(); @@ -376,22 +352,19 @@ public class JavaRestClientTest extends AbstractContainerTest { assertThat(bundleInfos).hasSize(1); } - protected NotificationTarget createNotificationTarget(UserId... usersIds) { + private NotificationTarget createNotificationTarget(UserId... usersIds) { UserListFilter filter = new UserListFilter(); filter.setUsersIds(Arrays.stream(usersIds).map(UUIDBased::getId).toList()); - return createNotificationTarget(filter); - } - protected NotificationTarget createNotificationTarget(UsersFilter usersFilter) { NotificationTarget notificationTarget = new NotificationTarget(); - notificationTarget.setName(usersFilter.toString() + org.apache.commons.lang3.RandomStringUtils.randomNumeric(5)); + notificationTarget.setName(filter.toString() + org.apache.commons.lang3.RandomStringUtils.randomNumeric(5)); PlatformUsersNotificationTargetConfig targetConfig = new PlatformUsersNotificationTargetConfig(); - targetConfig.setUsersFilter(usersFilter); + targetConfig.setUsersFilter(filter); notificationTarget.setConfiguration(targetConfig); return restClient.createNotificationTarget(notificationTarget); } - protected NotificationTemplate createNotificationTemplate(NotificationType notificationType, String subject, + private NotificationTemplate createNotificationTemplate(NotificationType notificationType, String subject, String text, NotificationDeliveryMethod... deliveryMethods) { NotificationTemplate notificationTemplate = new NotificationTemplate(); notificationTemplate.setName("Notification template: " + RandomStringUtils.randomAlphabetic(5)); @@ -430,7 +403,7 @@ public class JavaRestClientTest extends AbstractContainerTest { return restClient.createNotificationTemplate(notificationTemplate); } - protected NotificationRequest submitNotificationRequest(NotificationTargetId targetId, NotificationTemplateId notificationTemplateId) { + private NotificationRequest submitNotificationRequest(NotificationTargetId targetId, NotificationTemplateId notificationTemplateId) { NotificationRequestConfig config = new NotificationRequestConfig(); config.setSendingDelayInSec(0); NotificationRequest notificationRequest = NotificationRequest.builder() From ccde77036a2ee10f328ee8d3097af9f986e1828a Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Tue, 2 Dec 2025 16:55:32 +0200 Subject: [PATCH 4/5] refactoring --- .../msa/connectivity/JavaRestClientTest.java | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/JavaRestClientTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/JavaRestClientTest.java index 9d0292cc7e..d439bdcf48 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/JavaRestClientTest.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/JavaRestClientTest.java @@ -318,9 +318,8 @@ public class JavaRestClientTest extends AbstractContainerTest { Domain savedDomain = restClient.saveDomain(domain); assertThat(savedDomain.getName()).isEqualTo(domain.getName()); - PageData tenantDomainInfos = restClient.getTenantDomainInfos(new PageLink(10)); - List domainInfos = tenantDomainInfos.getData().stream().filter(domainInfo -> domainInfo.getName().startsWith(prefix)).toList(); - assertThat(domainInfos).hasSize(1); + PageData domainInfos = restClient.getTenantDomainInfos(new PageLink(10, 0 , prefix)); + assertThat(domainInfos.getData()).hasSize(1); } @Test @@ -337,9 +336,8 @@ public class JavaRestClientTest extends AbstractContainerTest { MobileApp savedMobileApp = restClient.saveMobileApp(mobileApp); assertThat(savedMobileApp.getName()).isEqualTo(mobileApp.getName()); - PageData mobileApps = restClient.getTenantMobileApps(new PageLink(10)); - List retrieved = mobileApps.getData().stream().filter(app -> app.getPkgName().startsWith(prefix)).toList(); - assertThat(retrieved).hasSize(1); + PageData retrieved = restClient.getTenantMobileApps(new PageLink(10, 0, prefix)); + assertThat(retrieved.getData()).hasSize(1); MobileAppBundle mobileAppBundle = new MobileAppBundle(); String bundlePrefix = RandomStringUtils.randomAlphabetic(5).toLowerCase(); @@ -347,9 +345,8 @@ public class JavaRestClientTest extends AbstractContainerTest { mobileAppBundle.setAndroidAppId(savedMobileApp.getId()); MobileAppBundle savedMobileAppBundle = restClient.saveMobileBundle(mobileAppBundle); - PageData mobileBundleInfos = restClient.getTenantMobileBundleInfos(new PageLink(10)); - List bundleInfos = mobileBundleInfos.getData().stream().filter(mobileAppBundleInfo -> mobileAppBundleInfo.getTitle().startsWith(bundlePrefix)).toList(); - assertThat(bundleInfos).hasSize(1); + PageData bundleInfos = restClient.getTenantMobileBundleInfos(new PageLink(10, 0, bundlePrefix)); + assertThat(bundleInfos.getData()).hasSize(1); } private NotificationTarget createNotificationTarget(UserId... usersIds) { From 6f17dd1503ba2c221e5eab424f55c4897a6ae8c0 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Mon, 8 Dec 2025 11:10:41 +0200 Subject: [PATCH 5/5] RestClient: renamed methods --- .../server/msa/connectivity/JavaRestClientTest.java | 6 +++--- .../main/java/org/thingsboard/rest/client/RestClient.java | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/JavaRestClientTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/JavaRestClientTest.java index d439bdcf48..fb41cdf77f 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/JavaRestClientTest.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/JavaRestClientTest.java @@ -358,7 +358,7 @@ public class JavaRestClientTest extends AbstractContainerTest { PlatformUsersNotificationTargetConfig targetConfig = new PlatformUsersNotificationTargetConfig(); targetConfig.setUsersFilter(filter); notificationTarget.setConfiguration(targetConfig); - return restClient.createNotificationTarget(notificationTarget); + return restClient.saveNotificationTarget(notificationTarget); } private NotificationTemplate createNotificationTemplate(NotificationType notificationType, String subject, @@ -397,7 +397,7 @@ public class JavaRestClientTest extends AbstractContainerTest { config.getDeliveryMethodsTemplates().put(deliveryMethod, deliveryMethodNotificationTemplate); } notificationTemplate.setConfiguration(config); - return restClient.createNotificationTemplate(notificationTemplate); + return restClient.saveNotificationTemplate(notificationTemplate); } private NotificationRequest submitNotificationRequest(NotificationTargetId targetId, NotificationTemplateId notificationTemplateId) { @@ -408,6 +408,6 @@ public class JavaRestClientTest extends AbstractContainerTest { .templateId(notificationTemplateId) .additionalConfig(config) .build(); - return restClient.createNotificationRequest(notificationRequest); + return restClient.saveNotificationRequest(notificationRequest); } } diff --git a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java index 64fd891f2c..c82a80e7cc 100644 --- a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java +++ b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java @@ -4360,7 +4360,7 @@ public class RestClient implements Closeable { restTemplate.delete(baseURL + "/api/notification/{id}", notificationId.getId()); } - public NotificationRequest createNotificationRequest(NotificationRequest notificationRequest) { + public NotificationRequest saveNotificationRequest(NotificationRequest notificationRequest) { return restTemplate.postForEntity(baseURL + "/api/notification/request", notificationRequest, NotificationRequest.class).getBody(); } @@ -4439,11 +4439,11 @@ public class RestClient implements Closeable { } } - public NotificationTarget createNotificationTarget(NotificationTarget notificationTarget) { + public NotificationTarget saveNotificationTarget(NotificationTarget notificationTarget) { return restTemplate.postForEntity(baseURL + "/api/notification/target", notificationTarget, NotificationTarget.class).getBody(); } - public NotificationTemplate createNotificationTemplate(NotificationTemplate notificationTemplate) { + public NotificationTemplate saveNotificationTemplate(NotificationTemplate notificationTemplate) { return restTemplate.postForEntity(baseURL + "/api/notification/template", notificationTemplate, NotificationTemplate.class).getBody(); }