Merge branch 'feature/push-notifications' of github.com:thingsboard/thingsboard into feature/push-notifications
This commit is contained in:
commit
2023ddeb6c
@ -20,10 +20,12 @@ import com.google.firebase.messaging.MessagingErrorCode;
|
|||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.thingsboard.common.util.JacksonUtil;
|
||||||
import org.thingsboard.rule.engine.api.notification.FirebaseService;
|
import org.thingsboard.rule.engine.api.notification.FirebaseService;
|
||||||
import org.thingsboard.server.common.data.User;
|
import org.thingsboard.server.common.data.User;
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
|
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
|
||||||
|
import org.thingsboard.server.common.data.notification.info.NotificationInfo;
|
||||||
import org.thingsboard.server.common.data.notification.settings.MobileAppNotificationDeliveryMethodConfig;
|
import org.thingsboard.server.common.data.notification.settings.MobileAppNotificationDeliveryMethodConfig;
|
||||||
import org.thingsboard.server.common.data.notification.settings.NotificationSettings;
|
import org.thingsboard.server.common.data.notification.settings.NotificationSettings;
|
||||||
import org.thingsboard.server.common.data.notification.template.MobileAppDeliveryMethodNotificationTemplate;
|
import org.thingsboard.server.common.data.notification.template.MobileAppDeliveryMethodNotificationTemplate;
|
||||||
@ -31,7 +33,10 @@ import org.thingsboard.server.dao.notification.NotificationSettingsService;
|
|||||||
import org.thingsboard.server.dao.user.UserService;
|
import org.thingsboard.server.dao.user.UserService;
|
||||||
import org.thingsboard.server.service.notification.NotificationProcessingContext;
|
import org.thingsboard.server.service.notification.NotificationProcessingContext;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@ -53,9 +58,20 @@ public class MobileAppNotificationChannel implements NotificationChannel<User, M
|
|||||||
MobileAppNotificationDeliveryMethodConfig config = ctx.getDeliveryMethodConfig(NotificationDeliveryMethod.MOBILE_APP);
|
MobileAppNotificationDeliveryMethodConfig config = ctx.getDeliveryMethodConfig(NotificationDeliveryMethod.MOBILE_APP);
|
||||||
String credentials = config.getFirebaseServiceAccountCredentials();
|
String credentials = config.getFirebaseServiceAccountCredentials();
|
||||||
Set<String> validTokens = new HashSet<>(mobileSessions.keySet());
|
Set<String> validTokens = new HashSet<>(mobileSessions.keySet());
|
||||||
|
|
||||||
|
String subject = processedTemplate.getSubject();
|
||||||
|
String body = processedTemplate.getBody();
|
||||||
|
Map<String, String> data = Optional.ofNullable(processedTemplate.getAdditionalConfig())
|
||||||
|
.map(JacksonUtil::toFlatMap).orElseGet(HashMap::new);
|
||||||
|
Optional.ofNullable(ctx.getRequest().getInfo())
|
||||||
|
.map(NotificationInfo::getStateEntityId)
|
||||||
|
.ifPresent(stateEntityId -> {
|
||||||
|
data.put("stateEntityId", stateEntityId.getId().toString());
|
||||||
|
data.put("stateEntityType", stateEntityId.getEntityType().name());
|
||||||
|
});
|
||||||
for (String token : mobileSessions.keySet()) {
|
for (String token : mobileSessions.keySet()) {
|
||||||
try {
|
try {
|
||||||
firebaseService.sendMessage(ctx.getTenantId(), credentials, token, processedTemplate.getSubject(), processedTemplate.getBody());
|
firebaseService.sendMessage(ctx.getTenantId(), credentials, token, subject, body, data);
|
||||||
} catch (FirebaseMessagingException e) {
|
} catch (FirebaseMessagingException e) {
|
||||||
MessagingErrorCode errorCode = e.getMessagingErrorCode();
|
MessagingErrorCode errorCode = e.getMessagingErrorCode();
|
||||||
if (errorCode == MessagingErrorCode.UNREGISTERED || errorCode == MessagingErrorCode.INVALID_ARGUMENT) {
|
if (errorCode == MessagingErrorCode.UNREGISTERED || errorCode == MessagingErrorCode.INVALID_ARGUMENT) {
|
||||||
|
|||||||
@ -35,6 +35,7 @@ import org.thingsboard.server.common.data.id.TenantId;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@ -51,7 +52,7 @@ public class DefaultFirebaseService implements FirebaseService {
|
|||||||
.build();
|
.build();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendMessage(TenantId tenantId, String credentials, String fcmToken, String title, String body) throws FirebaseMessagingException {
|
public void sendMessage(TenantId tenantId, String credentials, String fcmToken, String title, String body, Map<String, String> data) throws FirebaseMessagingException {
|
||||||
FirebaseContext firebaseContext = contexts.asMap().compute(tenantId.toString(), (key, context) -> {
|
FirebaseContext firebaseContext = contexts.asMap().compute(tenantId.toString(), (key, context) -> {
|
||||||
if (context == null) {
|
if (context == null) {
|
||||||
return new FirebaseContext(key, credentials);
|
return new FirebaseContext(key, credentials);
|
||||||
@ -62,6 +63,7 @@ public class DefaultFirebaseService implements FirebaseService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Message message = Message.builder()
|
Message message = Message.builder()
|
||||||
|
.setToken(fcmToken)
|
||||||
.setNotification(Notification.builder()
|
.setNotification(Notification.builder()
|
||||||
.setTitle(title)
|
.setTitle(title)
|
||||||
.setBody(body)
|
.setBody(body)
|
||||||
@ -69,7 +71,7 @@ public class DefaultFirebaseService implements FirebaseService {
|
|||||||
.setAndroidConfig(AndroidConfig.builder()
|
.setAndroidConfig(AndroidConfig.builder()
|
||||||
.setPriority(AndroidConfig.Priority.HIGH)
|
.setPriority(AndroidConfig.Priority.HIGH)
|
||||||
.build())
|
.build())
|
||||||
.setToken(fcmToken)
|
.putAllData(data)
|
||||||
.build();
|
.build();
|
||||||
firebaseContext.getMessaging().send(message);
|
firebaseContext.getMessaging().send(message);
|
||||||
log.trace("[{}] Sent message for FCM token {}", tenantId, fcmToken);
|
log.trace("[{}] Sent message for FCM token {}", tenantId, fcmToken);
|
||||||
|
|||||||
@ -27,6 +27,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||||
import org.springframework.test.web.servlet.ResultActions;
|
import org.springframework.test.web.servlet.ResultActions;
|
||||||
import org.springframework.web.client.RestTemplate;
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
import org.thingsboard.common.util.JacksonUtil;
|
||||||
import org.thingsboard.rule.engine.api.NotificationCenter;
|
import org.thingsboard.rule.engine.api.NotificationCenter;
|
||||||
import org.thingsboard.rule.engine.api.notification.FirebaseService;
|
import org.thingsboard.rule.engine.api.notification.FirebaseService;
|
||||||
import org.thingsboard.server.common.data.EntityType;
|
import org.thingsboard.server.common.data.EntityType;
|
||||||
@ -66,6 +67,7 @@ import org.thingsboard.server.common.data.notification.template.DeliveryMethodNo
|
|||||||
import org.thingsboard.server.common.data.notification.template.EmailDeliveryMethodNotificationTemplate;
|
import org.thingsboard.server.common.data.notification.template.EmailDeliveryMethodNotificationTemplate;
|
||||||
import org.thingsboard.server.common.data.notification.template.MicrosoftTeamsDeliveryMethodNotificationTemplate;
|
import org.thingsboard.server.common.data.notification.template.MicrosoftTeamsDeliveryMethodNotificationTemplate;
|
||||||
import org.thingsboard.server.common.data.notification.template.MicrosoftTeamsDeliveryMethodNotificationTemplate.Button.LinkType;
|
import org.thingsboard.server.common.data.notification.template.MicrosoftTeamsDeliveryMethodNotificationTemplate.Button.LinkType;
|
||||||
|
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.NotificationTemplate;
|
||||||
import org.thingsboard.server.common.data.notification.template.NotificationTemplateConfig;
|
import org.thingsboard.server.common.data.notification.template.NotificationTemplateConfig;
|
||||||
import org.thingsboard.server.common.data.notification.template.SlackDeliveryMethodNotificationTemplate;
|
import org.thingsboard.server.common.data.notification.template.SlackDeliveryMethodNotificationTemplate;
|
||||||
@ -76,7 +78,6 @@ import org.thingsboard.server.common.data.security.Authority;
|
|||||||
import org.thingsboard.server.dao.notification.DefaultNotifications;
|
import org.thingsboard.server.dao.notification.DefaultNotifications;
|
||||||
import org.thingsboard.server.dao.notification.NotificationDao;
|
import org.thingsboard.server.dao.notification.NotificationDao;
|
||||||
import org.thingsboard.server.dao.service.DaoSqlTest;
|
import org.thingsboard.server.dao.service.DaoSqlTest;
|
||||||
import org.thingsboard.server.service.executors.DbCallbackExecutorService;
|
|
||||||
import org.thingsboard.server.service.notification.channels.MicrosoftTeamsNotificationChannel;
|
import org.thingsboard.server.service.notification.channels.MicrosoftTeamsNotificationChannel;
|
||||||
import org.thingsboard.server.service.ws.notification.cmd.UnreadNotificationsUpdate;
|
import org.thingsboard.server.service.ws.notification.cmd.UnreadNotificationsUpdate;
|
||||||
|
|
||||||
@ -92,6 +93,8 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||||||
import static org.assertj.core.api.InstanceOfAssertFactories.type;
|
import static org.assertj.core.api.InstanceOfAssertFactories.type;
|
||||||
import static org.awaitility.Awaitility.await;
|
import static org.awaitility.Awaitility.await;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyMap;
|
||||||
|
import static org.mockito.ArgumentMatchers.argThat;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.clearInvocations;
|
import static org.mockito.Mockito.clearInvocations;
|
||||||
import static org.mockito.Mockito.doThrow;
|
import static org.mockito.Mockito.doThrow;
|
||||||
@ -739,6 +742,9 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
|
|||||||
loginTenantAdmin();
|
loginTenantAdmin();
|
||||||
NotificationTarget target = createNotificationTarget(new AllUsersFilter());
|
NotificationTarget target = createNotificationTarget(new AllUsersFilter());
|
||||||
NotificationTemplate template = createNotificationTemplate(NotificationType.GENERAL, "Title", "Message", NotificationDeliveryMethod.MOBILE_APP);
|
NotificationTemplate template = createNotificationTemplate(NotificationType.GENERAL, "Title", "Message", NotificationDeliveryMethod.MOBILE_APP);
|
||||||
|
((MobileAppDeliveryMethodNotificationTemplate) template.getConfiguration().getDeliveryMethodsTemplates().get(NotificationDeliveryMethod.MOBILE_APP))
|
||||||
|
.setAdditionalConfig(JacksonUtil.newObjectNode().set("test", JacksonUtil.newObjectNode().put("test", "test")));
|
||||||
|
saveNotificationTemplate(template);
|
||||||
|
|
||||||
NotificationRequest request = submitNotificationRequest(List.of(target.getId()), template.getId(), 0);
|
NotificationRequest request = submitNotificationRequest(List.of(target.getId()), template.getId(), 0);
|
||||||
NotificationRequestStats stats = awaitNotificationRequest(request.getId());
|
NotificationRequestStats stats = awaitNotificationRequest(request.getId());
|
||||||
@ -747,11 +753,11 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
|
|||||||
.contains("doesn't use the mobile app");
|
.contains("doesn't use the mobile app");
|
||||||
|
|
||||||
verify(firebaseService).sendMessage(eq(tenantId), eq("testCredentials"),
|
verify(firebaseService).sendMessage(eq(tenantId), eq("testCredentials"),
|
||||||
eq("tenantFcmToken1"), eq("Title"), eq("Message"));
|
eq("tenantFcmToken1"), eq("Title"), eq("Message"), argThat(data -> "test".equals(data.get("test.test"))));
|
||||||
verify(firebaseService).sendMessage(eq(tenantId), eq("testCredentials"),
|
verify(firebaseService).sendMessage(eq(tenantId), eq("testCredentials"),
|
||||||
eq("tenantFcmToken2"), eq("Title"), eq("Message"));
|
eq("tenantFcmToken2"), eq("Title"), eq("Message"), argThat(data -> "test".equals(data.get("test.test"))));
|
||||||
verify(firebaseService).sendMessage(eq(tenantId), eq("testCredentials"),
|
verify(firebaseService).sendMessage(eq(tenantId), eq("testCredentials"),
|
||||||
eq("customerFcmToken"), eq("Title"), eq("Message"));
|
eq("customerFcmToken"), eq("Title"), eq("Message"), argThat(data -> "test".equals(data.get("test.test"))));
|
||||||
verifyNoMoreInteractions(firebaseService);
|
verifyNoMoreInteractions(firebaseService);
|
||||||
clearInvocations(firebaseService);
|
clearInvocations(firebaseService);
|
||||||
|
|
||||||
@ -759,9 +765,9 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
|
|||||||
request = submitNotificationRequest(List.of(target.getId()), template.getId(), 0);
|
request = submitNotificationRequest(List.of(target.getId()), template.getId(), 0);
|
||||||
awaitNotificationRequest(request.getId());
|
awaitNotificationRequest(request.getId());
|
||||||
verify(firebaseService).sendMessage(eq(tenantId), eq("testCredentials"),
|
verify(firebaseService).sendMessage(eq(tenantId), eq("testCredentials"),
|
||||||
eq("tenantFcmToken1"), eq("Title"), eq("Message"));
|
eq("tenantFcmToken1"), eq("Title"), eq("Message"), anyMap());
|
||||||
verify(firebaseService).sendMessage(eq(tenantId), eq("testCredentials"),
|
verify(firebaseService).sendMessage(eq(tenantId), eq("testCredentials"),
|
||||||
eq("customerFcmToken"), eq("Title"), eq("Message"));
|
eq("customerFcmToken"), eq("Title"), eq("Message"), anyMap());
|
||||||
verifyNoMoreInteractions(firebaseService);
|
verifyNoMoreInteractions(firebaseService);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.thingsboard.server.common.data.notification.template;
|
package org.thingsboard.server.common.data.notification.template;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
@ -32,6 +33,7 @@ public class MobileAppDeliveryMethodNotificationTemplate extends DeliveryMethodN
|
|||||||
|
|
||||||
@NotEmpty
|
@NotEmpty
|
||||||
private String subject;
|
private String subject;
|
||||||
|
private JsonNode additionalConfig;
|
||||||
|
|
||||||
private final List<TemplatableValue> templatableValues = List.of(
|
private final List<TemplatableValue> templatableValues = List.of(
|
||||||
TemplatableValue.of(this::getBody, this::setBody),
|
TemplatableValue.of(this::getBody, this::setBody),
|
||||||
@ -41,6 +43,7 @@ public class MobileAppDeliveryMethodNotificationTemplate extends DeliveryMethodN
|
|||||||
public MobileAppDeliveryMethodNotificationTemplate(MobileAppDeliveryMethodNotificationTemplate other) {
|
public MobileAppDeliveryMethodNotificationTemplate(MobileAppDeliveryMethodNotificationTemplate other) {
|
||||||
super(other);
|
super(other);
|
||||||
this.subject = other.subject;
|
this.subject = other.subject;
|
||||||
|
this.additionalConfig = other.additionalConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -17,8 +17,10 @@ package org.thingsboard.rule.engine.api.notification;
|
|||||||
|
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public interface FirebaseService {
|
public interface FirebaseService {
|
||||||
|
|
||||||
void sendMessage(TenantId tenantId, String credentials, String fcmToken, String title, String body) throws Exception;
|
void sendMessage(TenantId tenantId, String credentials, String fcmToken, String title, String body, Map<String, String> data) throws Exception;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user