72 changed files with 844 additions and 342 deletions
@ -0,0 +1,29 @@ |
|||
/** |
|||
* Copyright © 2016-2022 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.service.executors; |
|||
|
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.common.util.AbstractListeningExecutor; |
|||
|
|||
@Component |
|||
public class NotificationExecutorService extends AbstractListeningExecutor { |
|||
|
|||
@Override |
|||
protected int getThreadPollSize() { |
|||
return 10; // FIXME [viacheslav]
|
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,168 @@ |
|||
/** |
|||
* Copyright © 2016-2022 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.service.notification; |
|||
|
|||
import com.fasterxml.jackson.core.type.TypeReference; |
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.test.web.servlet.ResultActions; |
|||
import org.springframework.test.web.servlet.ResultMatcher; |
|||
import org.thingsboard.server.common.data.User; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.notification.targets.AllUsersNotificationTargetConfig; |
|||
import org.thingsboard.server.common.data.notification.targets.CustomerUsersNotificationTargetConfig; |
|||
import org.thingsboard.server.common.data.notification.targets.NotificationTarget; |
|||
import org.thingsboard.server.common.data.notification.targets.SingleUserNotificationTargetConfig; |
|||
import org.thingsboard.server.common.data.notification.targets.UserListNotificationTargetConfig; |
|||
import org.thingsboard.server.common.data.page.PageData; |
|||
import org.thingsboard.server.controller.AbstractControllerTest; |
|||
import org.thingsboard.server.dao.notification.NotificationTargetDao; |
|||
import org.thingsboard.server.dao.service.DaoSqlTest; |
|||
|
|||
import java.util.Collections; |
|||
import java.util.List; |
|||
|
|||
import static org.assertj.core.api.Assertions.assertThat; |
|||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
|||
|
|||
@DaoSqlTest |
|||
public class NotificationTargetApiTest extends AbstractControllerTest { |
|||
|
|||
@Autowired |
|||
private NotificationTargetDao notificationTargetDao; |
|||
|
|||
@Before |
|||
public void beforeEach() throws Exception { |
|||
loginTenantAdmin(); |
|||
} |
|||
|
|||
@Test |
|||
public void givenInvalidNotificationTarget_whenSaving_returnValidationError() throws Exception { |
|||
NotificationTarget notificationTarget = new NotificationTarget(); |
|||
notificationTarget.setTenantId(null); |
|||
notificationTarget.setName(null); |
|||
notificationTarget.setConfiguration(null); |
|||
|
|||
String validationError = saveAndGetError(notificationTarget, status().isBadRequest()); |
|||
assertThat(validationError) |
|||
.contains("name must not be") |
|||
.contains("configuration must not be"); |
|||
|
|||
SingleUserNotificationTargetConfig singleUserConfig = new SingleUserNotificationTargetConfig(); |
|||
singleUserConfig.setUserId(null); |
|||
notificationTarget.setConfiguration(singleUserConfig); |
|||
|
|||
validationError = saveAndGetError(notificationTarget, status().isBadRequest()); |
|||
assertThat(validationError) |
|||
.contains("userId must not be"); |
|||
|
|||
UserListNotificationTargetConfig userListConfig = new UserListNotificationTargetConfig(); |
|||
userListConfig.setUsersIds(Collections.emptyList()); |
|||
notificationTarget.setConfiguration(userListConfig); |
|||
|
|||
validationError = saveAndGetError(notificationTarget, status().isBadRequest()); |
|||
assertThat(validationError) |
|||
.contains("usersIds must not be"); |
|||
} |
|||
|
|||
@Test |
|||
public void givenNotificationTargetWithUsersFromDifferentTenant_whenSaving_returnAccessDeniedError() throws Exception { |
|||
loginDifferentTenant(); |
|||
NotificationTarget notificationTarget = new NotificationTarget(); |
|||
notificationTarget.setTenantId(differentTenantId); |
|||
notificationTarget.setName("Target 1"); |
|||
UserListNotificationTargetConfig userListConfig = new UserListNotificationTargetConfig(); |
|||
userListConfig.setUsersIds(List.of(customerUserId.getId(), tenantAdminUserId.getId())); |
|||
notificationTarget.setConfiguration(userListConfig); |
|||
|
|||
saveAndGetError(notificationTarget, status().isForbidden()); |
|||
|
|||
SingleUserNotificationTargetConfig singleUserConfig = new SingleUserNotificationTargetConfig(); |
|||
singleUserConfig.setUserId(customerUserId.getId()); |
|||
notificationTarget.setConfiguration(singleUserConfig); |
|||
|
|||
saveAndGetError(notificationTarget, status().isForbidden()); |
|||
|
|||
loginSysAdmin(); |
|||
notificationTarget.setTenantId(TenantId.SYS_TENANT_ID); |
|||
notificationTarget.setConfiguration(userListConfig); |
|||
save(notificationTarget, status().isOk()); |
|||
} |
|||
|
|||
@Test |
|||
public void givenNotificationTargetConfig_testGetRecipients() throws Exception { |
|||
NotificationTarget notificationTarget = new NotificationTarget(); |
|||
notificationTarget.setTenantId(tenantId); |
|||
notificationTarget.setName("Test target"); |
|||
CustomerUsersNotificationTargetConfig customerUsersConfig = new CustomerUsersNotificationTargetConfig(); |
|||
customerUsersConfig.setCustomerId(customerId.getId()); |
|||
notificationTarget.setConfiguration(customerUsersConfig); |
|||
|
|||
List<User> recipients = getRecipients(notificationTarget); |
|||
assertThat(recipients).size().isNotZero(); |
|||
assertThat(recipients).allSatisfy(recipient -> { |
|||
assertThat(recipient.getCustomerId()).isEqualTo(customerId); |
|||
}); |
|||
|
|||
AllUsersNotificationTargetConfig allUsersConfig = new AllUsersNotificationTargetConfig(); |
|||
notificationTarget.setConfiguration(allUsersConfig); |
|||
recipients = getRecipients(notificationTarget); |
|||
assertThat(recipients).size().isGreaterThanOrEqualTo(2); |
|||
assertThat(recipients).allSatisfy(recipient -> { |
|||
assertThat(recipient.getTenantId()).isEqualTo(tenantId); |
|||
}); |
|||
|
|||
createDifferentTenant(); |
|||
loginSysAdmin(); |
|||
recipients = getRecipients(notificationTarget); |
|||
assertThat(recipients).size().isGreaterThanOrEqualTo(3); |
|||
assertThat(recipients).anySatisfy(recipient -> { |
|||
assertThat(recipient.getTenantId()).isEqualTo(tenantId); |
|||
}); |
|||
assertThat(recipients).anySatisfy(recipient -> { |
|||
assertThat(recipient.getTenantId()).isEqualTo(differentTenantId); |
|||
}); |
|||
} |
|||
|
|||
@Test |
|||
public void whenDeletingTenant_thenDeleteNotificationTarget() throws Exception { |
|||
createDifferentTenant(); |
|||
NotificationTarget notificationTarget = new NotificationTarget(); |
|||
notificationTarget.setName("Test 1"); |
|||
notificationTarget.setTenantId(differentTenantId); |
|||
notificationTarget.setConfiguration(new AllUsersNotificationTargetConfig()); |
|||
save(notificationTarget, status().isOk()); |
|||
assertThat(notificationTargetDao.find(TenantId.SYS_TENANT_ID)).isNotEmpty(); |
|||
|
|||
deleteDifferentTenant(); |
|||
assertThat(notificationTargetDao.find(TenantId.SYS_TENANT_ID)).isEmpty(); |
|||
} |
|||
|
|||
private String saveAndGetError(NotificationTarget notificationTarget, ResultMatcher statusMatcher) throws Exception { |
|||
return getErrorMessage(save(notificationTarget, statusMatcher)); |
|||
} |
|||
|
|||
private ResultActions save(NotificationTarget notificationTarget, ResultMatcher statusMatcher) throws Exception { |
|||
return doPost("/api/notification/target", notificationTarget) |
|||
.andExpect(statusMatcher); |
|||
} |
|||
|
|||
private List<User> getRecipients(NotificationTarget notificationTarget) throws Exception { |
|||
return doPostWithTypedResponse("/api/notification/target/recipients?page=0&pageSize=100", notificationTarget, new TypeReference<PageData<User>>() {}).getData(); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,90 @@ |
|||
/** |
|||
* Copyright © 2016-2022 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.service.notification; |
|||
|
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
import org.springframework.test.web.servlet.ResultActions; |
|||
import org.springframework.test.web.servlet.ResultMatcher; |
|||
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; |
|||
import org.thingsboard.server.common.data.notification.template.EmailDeliveryMethodNotificationTemplate; |
|||
import org.thingsboard.server.common.data.notification.template.NotificationTemplate; |
|||
import org.thingsboard.server.common.data.notification.template.NotificationTemplateConfig; |
|||
import org.thingsboard.server.controller.AbstractControllerTest; |
|||
import org.thingsboard.server.dao.service.DaoSqlTest; |
|||
|
|||
import java.util.Map; |
|||
|
|||
import static org.assertj.core.api.Assertions.assertThat; |
|||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
|||
|
|||
@DaoSqlTest |
|||
public class NotificationTemplateApiTest extends AbstractControllerTest { |
|||
|
|||
@Before |
|||
public void beforeEach() throws Exception { |
|||
loginTenantAdmin(); |
|||
} |
|||
|
|||
@Test |
|||
public void givenInvalidNotificationTemplate_whenSaving_returnValidationError() throws Exception { |
|||
NotificationTemplate notificationTemplate = new NotificationTemplate(); |
|||
notificationTemplate.setTenantId(tenantId); |
|||
notificationTemplate.setName(null); |
|||
notificationTemplate.setNotificationType(null); |
|||
notificationTemplate.setConfiguration(null); |
|||
|
|||
String validationError = saveAndGetError(notificationTemplate, status().isBadRequest()); |
|||
assertThat(validationError) |
|||
.contains("name must not be") |
|||
.contains("notificationType must not be") |
|||
.contains("configuration must not be"); |
|||
|
|||
NotificationTemplateConfig config = new NotificationTemplateConfig(); |
|||
notificationTemplate.setConfiguration(config); |
|||
config.setDefaultTextTemplate("Default text"); |
|||
EmailDeliveryMethodNotificationTemplate emailTemplate = new EmailDeliveryMethodNotificationTemplate(); |
|||
emailTemplate.setMethod(NotificationDeliveryMethod.EMAIL); |
|||
emailTemplate.setBody(null); |
|||
emailTemplate.setSubject(null); |
|||
config.setTemplates(Map.of( |
|||
NotificationDeliveryMethod.EMAIL, emailTemplate |
|||
)); |
|||
notificationTemplate.setName("<script/>"); |
|||
|
|||
validationError = saveAndGetError(notificationTemplate, status().isBadRequest()); |
|||
assertThat(validationError) |
|||
.doesNotContain("defaultTextTemplate must be specified") |
|||
.contains("subject must not be") |
|||
.contains("name is malformed"); |
|||
|
|||
config.setDefaultTextTemplate(null); |
|||
|
|||
validationError = saveAndGetError(notificationTemplate, status().isBadRequest()); |
|||
assertThat(validationError) |
|||
.contains("defaultTextTemplate must be specified"); |
|||
} |
|||
|
|||
private String saveAndGetError(NotificationTemplate notificationTemplate, ResultMatcher statusMatcher) throws Exception { |
|||
return getErrorMessage(save(notificationTemplate, statusMatcher)); |
|||
} |
|||
|
|||
private ResultActions save(NotificationTemplate notificationTemplate, ResultMatcher statusMatcher) throws Exception { |
|||
return doPost("/api/notification/template", notificationTemplate) |
|||
.andExpect(statusMatcher); |
|||
} |
|||
|
|||
} |
|||
@ -1,86 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2022 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.service.notification; |
|||
|
|||
import com.google.common.base.Strings; |
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.thingsboard.rest.client.RestClient; |
|||
import org.thingsboard.server.common.data.notification.AlarmOriginatedNotificationInfo; |
|||
import org.thingsboard.server.common.data.notification.Notification; |
|||
import org.thingsboard.server.common.data.notification.NotificationInfo; |
|||
import org.thingsboard.server.common.data.notification.NotificationOriginatorType; |
|||
|
|||
import java.text.SimpleDateFormat; |
|||
import java.util.Date; |
|||
import java.util.List; |
|||
import java.util.Scanner; |
|||
|
|||
public class NotificationsClient extends NotificationApiWsClient { |
|||
|
|||
private NotificationsClient(String wsUrl, String token) throws Exception { |
|||
super(wsUrl, token); |
|||
} |
|||
|
|||
public static NotificationsClient newInstance(String username, String password) throws Exception { |
|||
RestClient restClient = new RestClient("http://localhost:8080"); |
|||
restClient.login(username, password); |
|||
NotificationsClient client = new NotificationsClient("ws://localhost:8080", restClient.getToken()); |
|||
client.connectBlocking(); |
|||
return client; |
|||
} |
|||
|
|||
@Override |
|||
public void onMessage(String s) { |
|||
super.onMessage(s); |
|||
// printNotificationsCount();
|
|||
printNotifications(); |
|||
} |
|||
|
|||
public void printNotifications() { |
|||
System.out.println(StringUtils.repeat(System.lineSeparator(), 20)); |
|||
List<Notification> notifications = getNotifications(); |
|||
System.out.printf(" %s NEW MESSAGE%s\n\n", getUnreadCount(), notifications.size() > 1 ? "S" : ""); |
|||
notifications.forEach(notification -> { |
|||
String notificationInfoStr = ""; |
|||
if (notification.getOriginatorType() == NotificationOriginatorType.ALARM) { |
|||
AlarmOriginatedNotificationInfo info = (AlarmOriginatedNotificationInfo) notification.getInfo(); |
|||
notificationInfoStr = String.format("Alarm of type %s - %s severity - status: %s", |
|||
info.getAlarmType(), info.getAlarmSeverity(), info.getAlarmStatus()); |
|||
} else if (notification.getInfo() != null) { |
|||
notificationInfoStr = Strings.nullToEmpty(notification.getInfo().getDescription()); |
|||
} |
|||
SimpleDateFormat format = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss"); |
|||
String time = format.format(new Date(notification.getCreatedTime())); |
|||
// System.out.printf("[%s] %-19s | %-30s | (%s)\n", time, notification.getReason(), notification.getText(), notificationInfoStr);
|
|||
}); |
|||
System.out.println(StringUtils.repeat(System.lineSeparator(), 5)); |
|||
} |
|||
|
|||
public void printNotificationsCount() { |
|||
System.out.println(); |
|||
System.out.println(); |
|||
System.out.println(); |
|||
int unreadCount = getUnreadCount(); |
|||
System.out.printf("\r\r%s NEW MESSAGE%s", unreadCount, unreadCount > 1 ? "S" : ""); |
|||
} |
|||
|
|||
public static void main(String[] args) throws Exception { |
|||
NotificationsClient client = NotificationsClient.newInstance("tenant@thingsboard.org", "tenant"); |
|||
client.subscribeForUnreadNotifications(5); |
|||
// client.subscribeForUnreadNotificationsCount();
|
|||
new Scanner(System.in).nextLine(); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue