Mobile notifications: additionalConfig and Firebase notification data

This commit is contained in:
ViacheslavKlimov 2024-02-07 17:28:14 +02:00
parent ad98dd0db1
commit c7dd55925a
5 changed files with 39 additions and 10 deletions

View File

@ -20,10 +20,12 @@ import com.google.firebase.messaging.MessagingErrorCode;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.api.notification.FirebaseService;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.TenantId;
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.NotificationSettings;
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.service.notification.NotificationProcessingContext;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
@Component
@ -53,9 +58,20 @@ public class MobileAppNotificationChannel implements NotificationChannel<User, M
MobileAppNotificationDeliveryMethodConfig config = ctx.getDeliveryMethodConfig(NotificationDeliveryMethod.MOBILE_APP);
String credentials = config.getFirebaseServiceAccountCredentials();
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()) {
try {
firebaseService.sendMessage(ctx.getTenantId(), credentials, token, processedTemplate.getSubject(), processedTemplate.getBody());
firebaseService.sendMessage(ctx.getTenantId(), credentials, token, subject, body, data);
} catch (FirebaseMessagingException e) {
MessagingErrorCode errorCode = e.getMessagingErrorCode();
if (errorCode == MessagingErrorCode.UNREGISTERED || errorCode == MessagingErrorCode.INVALID_ARGUMENT) {

View File

@ -35,6 +35,7 @@ import org.thingsboard.server.common.data.id.TenantId;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@Service
@ -51,7 +52,7 @@ public class DefaultFirebaseService implements FirebaseService {
.build();
@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) -> {
if (context == null) {
return new FirebaseContext(key, credentials);
@ -62,6 +63,7 @@ public class DefaultFirebaseService implements FirebaseService {
});
Message message = Message.builder()
.setToken(fcmToken)
.setNotification(Notification.builder()
.setTitle(title)
.setBody(body)
@ -69,7 +71,7 @@ public class DefaultFirebaseService implements FirebaseService {
.setAndroidConfig(AndroidConfig.builder()
.setPriority(AndroidConfig.Priority.HIGH)
.build())
.setToken(fcmToken)
.putAllData(data)
.build();
firebaseContext.getMessaging().send(message);
log.trace("[{}] Sent message for FCM token {}", tenantId, fcmToken);

View File

@ -27,6 +27,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.web.servlet.ResultActions;
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.notification.FirebaseService;
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.MicrosoftTeamsDeliveryMethodNotificationTemplate;
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.NotificationTemplateConfig;
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.NotificationDao;
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.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.awaitility.Awaitility.await;
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.Mockito.clearInvocations;
import static org.mockito.Mockito.doThrow;
@ -739,6 +742,9 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
loginTenantAdmin();
NotificationTarget target = createNotificationTarget(new AllUsersFilter());
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);
NotificationRequestStats stats = awaitNotificationRequest(request.getId());
@ -747,11 +753,11 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
.contains("doesn't use the mobile app");
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"),
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"),
eq("customerFcmToken"), eq("Title"), eq("Message"));
eq("customerFcmToken"), eq("Title"), eq("Message"), argThat(data -> "test".equals(data.get("test.test"))));
verifyNoMoreInteractions(firebaseService);
clearInvocations(firebaseService);
@ -759,9 +765,9 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
request = submitNotificationRequest(List.of(target.getId()), template.getId(), 0);
awaitNotificationRequest(request.getId());
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"),
eq("customerFcmToken"), eq("Title"), eq("Message"));
eq("customerFcmToken"), eq("Title"), eq("Message"), anyMap());
verifyNoMoreInteractions(firebaseService);
}

View File

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.common.data.notification.template;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@ -32,6 +33,7 @@ public class MobileAppDeliveryMethodNotificationTemplate extends DeliveryMethodN
@NotEmpty
private String subject;
private JsonNode additionalConfig;
private final List<TemplatableValue> templatableValues = List.of(
TemplatableValue.of(this::getBody, this::setBody),
@ -41,6 +43,7 @@ public class MobileAppDeliveryMethodNotificationTemplate extends DeliveryMethodN
public MobileAppDeliveryMethodNotificationTemplate(MobileAppDeliveryMethodNotificationTemplate other) {
super(other);
this.subject = other.subject;
this.additionalConfig = other.additionalConfig;
}
@Override

View File

@ -17,8 +17,10 @@ package org.thingsboard.rule.engine.api.notification;
import org.thingsboard.server.common.data.id.TenantId;
import java.util.Map;
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;
}