diff --git a/application/src/main/java/org/thingsboard/server/controller/UserController.java b/application/src/main/java/org/thingsboard/server/controller/UserController.java index 002b27d265..0e569401bf 100644 --- a/application/src/main/java/org/thingsboard/server/controller/UserController.java +++ b/application/src/main/java/org/thingsboard/server/controller/UserController.java @@ -25,6 +25,7 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.context.ApplicationEventPublisher; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -41,6 +42,7 @@ import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.UserEmailInfo; +import org.thingsboard.server.common.data.UserMobileInfo; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; @@ -81,6 +83,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Optional; import static org.thingsboard.server.common.data.query.EntityKeyType.ENTITY_FIELD; import static org.thingsboard.server.controller.ControllerConstants.ALARM_ID_PARAM_DESCRIPTION; @@ -584,6 +587,20 @@ public class UserController extends BaseController { return userSettingsService.reportUserDashboardAction(currentUser.getTenantId(), currentUser.getId(), dashboardId, action); } + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @GetMapping("/user/mobile/info") + public UserMobileInfo getMobileInfo(@AuthenticationPrincipal SecurityUser securityUser) { + return Optional.ofNullable(userService.findMobileInfo(securityUser.getTenantId(), securityUser.getId())) + .orElseGet(UserMobileInfo::new); + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @PostMapping("/user/mobile/info") + public void saveMobileInfo(@RequestBody UserMobileInfo mobileInfo, + @AuthenticationPrincipal SecurityUser securityUser) { + userService.saveMobileInfo(securityUser.getTenantId(), securityUser.getId(), mobileInfo); + } + private void checkNotReserved(String strType, UserSettingsType type) throws ThingsboardException { if (type.isReserved()) { throw new ThingsboardException("Settings with type: " + strType + " are reserved for internal use!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); diff --git a/application/src/main/java/org/thingsboard/server/service/notification/NotificationProcessingContext.java b/application/src/main/java/org/thingsboard/server/service/notification/NotificationProcessingContext.java index e91a3e80fa..29cac96be6 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/NotificationProcessingContext.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/NotificationProcessingContext.java @@ -58,7 +58,7 @@ public class NotificationProcessingContext { @Builder public NotificationProcessingContext(TenantId tenantId, NotificationRequest request, Set deliveryMethods, - NotificationTemplate template, NotificationSettings settings) { + NotificationTemplate template, NotificationSettings settings) { this.tenantId = tenantId; this.request = request; this.deliveryMethods = deliveryMethods; diff --git a/application/src/main/java/org/thingsboard/server/service/notification/channels/MobileNotificationChannel.java b/application/src/main/java/org/thingsboard/server/service/notification/channels/MobileAppNotificationChannel.java similarity index 51% rename from application/src/main/java/org/thingsboard/server/service/notification/channels/MobileNotificationChannel.java rename to application/src/main/java/org/thingsboard/server/service/notification/channels/MobileAppNotificationChannel.java index 135ecfe717..839368c2d7 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/channels/MobileNotificationChannel.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/channels/MobileAppNotificationChannel.java @@ -15,54 +15,65 @@ */ package org.thingsboard.server.service.notification.channels; -import com.fasterxml.jackson.databind.JsonNode; +import com.google.firebase.messaging.FirebaseMessagingException; +import com.google.firebase.messaging.MessagingErrorCode; import lombok.RequiredArgsConstructor; -import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; import org.thingsboard.rule.engine.api.notification.FirebaseService; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.UserMobileInfo; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; -import org.thingsboard.server.common.data.notification.settings.MobileNotificationDeliveryMethodConfig; +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.MobileDeliveryMethodNotificationTemplate; +import org.thingsboard.server.common.data.notification.template.MobileAppDeliveryMethodNotificationTemplate; import org.thingsboard.server.dao.notification.NotificationSettingsService; +import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.service.notification.NotificationProcessingContext; import java.util.Optional; @Component @RequiredArgsConstructor -public class MobileNotificationChannel implements NotificationChannel { +public class MobileAppNotificationChannel implements NotificationChannel { private final FirebaseService firebaseService; + private final UserService userService; private final NotificationSettingsService notificationSettingsService; @Override - public void sendNotification(User recipient, MobileDeliveryMethodNotificationTemplate processedTemplate, NotificationProcessingContext ctx) throws Exception { - String fcmToken = Optional.ofNullable(recipient.getAdditionalInfo()) - .map(info -> info.get("fcmToken")).filter(JsonNode::isTextual).map(JsonNode::asText) - .orElse(null); - if (StringUtils.isEmpty(fcmToken)) { - throw new RuntimeException("User doesn't have the mobile app installed"); - } + public void sendNotification(User recipient, MobileAppDeliveryMethodNotificationTemplate processedTemplate, NotificationProcessingContext ctx) throws Exception { + UserMobileInfo mobileInfo = userService.findMobileInfo(recipient.getTenantId(), recipient.getId()); + String fcmToken = Optional.ofNullable(mobileInfo) + .map(UserMobileInfo::getFcmToken) + .orElseThrow(() -> new IllegalArgumentException("User doesn't use the mobile app")); - MobileNotificationDeliveryMethodConfig config = ctx.getDeliveryMethodConfig(NotificationDeliveryMethod.MOBILE); - firebaseService.sendMessage(ctx.getTenantId(), config.getFirebaseServiceAccountCredentials(), - fcmToken, processedTemplate.getSubject(), processedTemplate.getBody()); + MobileAppNotificationDeliveryMethodConfig config = ctx.getDeliveryMethodConfig(NotificationDeliveryMethod.MOBILE_APP); + try { + firebaseService.sendMessage(ctx.getTenantId(), config.getFirebaseServiceAccountCredentials(), + fcmToken, processedTemplate.getSubject(), processedTemplate.getBody()); + } catch (FirebaseMessagingException e) { + if (e.getMessagingErrorCode() == MessagingErrorCode.UNREGISTERED) { + // the token is no longer valid + mobileInfo.setFcmToken(null); + userService.saveMobileInfo(recipient.getTenantId(), recipient.getId(), mobileInfo); + throw new IllegalArgumentException("User doesn't use the mobile app"); + } + throw new RuntimeException("Failed to send message via FCM: " + e.getMessage(), e); + } } @Override public void check(TenantId tenantId) throws Exception { NotificationSettings settings = notificationSettingsService.findNotificationSettings(tenantId); - if (!settings.getDeliveryMethodsConfigs().containsKey(NotificationDeliveryMethod.MOBILE)) { + if (!settings.getDeliveryMethodsConfigs().containsKey(NotificationDeliveryMethod.MOBILE_APP)) { throw new RuntimeException("Push-notifications to mobile are not configured"); } } @Override public NotificationDeliveryMethod getDeliveryMethod() { - return NotificationDeliveryMethod.MOBILE; + return NotificationDeliveryMethod.MOBILE_APP; } } diff --git a/application/src/main/java/org/thingsboard/server/service/notification/provider/DefaultFirebaseService.java b/application/src/main/java/org/thingsboard/server/service/notification/provider/DefaultFirebaseService.java index 372f631526..e6fdc55902 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/provider/DefaultFirebaseService.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/provider/DefaultFirebaseService.java @@ -48,7 +48,7 @@ public class DefaultFirebaseService implements FirebaseService { .build(); @Override - public void sendMessage(TenantId tenantId, String credentials, String fcmToken, String title, String body) { + public void sendMessage(TenantId tenantId, String credentials, String fcmToken, String title, String body) throws FirebaseMessagingException { FirebaseContext firebaseContext = contexts.asMap().compute(tenantId.toString(), (key, context) -> { if (context == null) { return new FirebaseContext(key, credentials); @@ -65,11 +65,7 @@ public class DefaultFirebaseService implements FirebaseService { .build()) .setToken(fcmToken) .build(); - try { - firebaseContext.getMessaging().send(message); - } catch (FirebaseMessagingException e) { - throw new RuntimeException("Failed to send message via FCM: " + e.getMessage(), e); - } + firebaseContext.getMessaging().send(message); } public static class FirebaseContext { diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java index b874e038d2..4e7e3a038b 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.user; import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.UserMobileInfo; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantProfileId; @@ -89,4 +90,8 @@ public interface UserService extends EntityDaoService { void setLastLoginTs(TenantId tenantId, UserId userId); + void saveMobileInfo(TenantId tenantId, UserId userId, UserMobileInfo mobileInfo); + + UserMobileInfo findMobileInfo(TenantId tenantId, UserId userId); + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/UserMobileInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/UserMobileInfo.java new file mode 100644 index 0000000000..d8337272b6 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/UserMobileInfo.java @@ -0,0 +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.common.data; + +import lombok.Data; + +@Data +public class UserMobileInfo { + private String fcmToken; + private long fcmTokenTimestamp; +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationDeliveryMethod.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationDeliveryMethod.java index 5f8ac6bdc2..72361ca748 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationDeliveryMethod.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationDeliveryMethod.java @@ -26,9 +26,10 @@ public enum NotificationDeliveryMethod { SMS("SMS"), SLACK("Slack"), MICROSOFT_TEAMS("Microsoft Teams"), - MOBILE("mobile"); + MOBILE_APP("mobile app"); @Getter private final String name; + // TODO: private final boolean allowUseSystemSettings; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/MobileNotificationDeliveryMethodConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/MobileAppNotificationDeliveryMethodConfig.java similarity index 87% rename from common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/MobileNotificationDeliveryMethodConfig.java rename to common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/MobileAppNotificationDeliveryMethodConfig.java index 4e443227ee..261fdd6fbd 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/MobileNotificationDeliveryMethodConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/MobileAppNotificationDeliveryMethodConfig.java @@ -21,7 +21,7 @@ import org.thingsboard.server.common.data.notification.NotificationDeliveryMetho import javax.validation.constraints.NotEmpty; @Data -public class MobileNotificationDeliveryMethodConfig implements NotificationDeliveryMethodConfig { +public class MobileAppNotificationDeliveryMethodConfig implements NotificationDeliveryMethodConfig { private String firebaseServiceAccountCredentialsFileName; @NotEmpty @@ -29,7 +29,7 @@ public class MobileNotificationDeliveryMethodConfig implements NotificationDeliv @Override public NotificationDeliveryMethod getMethod() { - return NotificationDeliveryMethod.MOBILE; + return NotificationDeliveryMethod.MOBILE_APP; } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/NotificationDeliveryMethodConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/NotificationDeliveryMethodConfig.java index 28020d6c4e..4dfcc17349 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/NotificationDeliveryMethodConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/NotificationDeliveryMethodConfig.java @@ -28,7 +28,7 @@ import java.io.Serializable; @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "method") @JsonSubTypes({ @Type(name = "SLACK", value = SlackNotificationDeliveryMethodConfig.class), - @Type(name = "MOBILE", value = MobileNotificationDeliveryMethodConfig.class) + @Type(name = "MOBILE_APP", value = MobileAppNotificationDeliveryMethodConfig.class) }) public interface NotificationDeliveryMethodConfig extends Serializable { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/NotificationSettings.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/NotificationSettings.java index 4ed6cfc30d..119e67e5f6 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/NotificationSettings.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/NotificationSettings.java @@ -28,7 +28,8 @@ public class NotificationSettings implements Serializable { @NotNull @Valid - // location on the screen, shown notifications count, timings of displaying private Map deliveryMethodsConfigs; + // TODO: disable option, location on the screen, shown notifications count, timings of displaying + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetType.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetType.java index 2d0257d4a7..f6fd6c962f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetType.java @@ -25,7 +25,7 @@ import java.util.Set; @RequiredArgsConstructor public enum NotificationTargetType { - PLATFORM_USERS(Set.of(NotificationDeliveryMethod.WEB, NotificationDeliveryMethod.EMAIL, NotificationDeliveryMethod.SMS, NotificationDeliveryMethod.MOBILE)), + PLATFORM_USERS(Set.of(NotificationDeliveryMethod.WEB, NotificationDeliveryMethod.EMAIL, NotificationDeliveryMethod.SMS, NotificationDeliveryMethod.MOBILE_APP)), SLACK(Set.of(NotificationDeliveryMethod.SLACK)), MICROSOFT_TEAMS(Set.of(NotificationDeliveryMethod.MICROSOFT_TEAMS)); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/DeliveryMethodNotificationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/DeliveryMethodNotificationTemplate.java index 6e5b6d2b0c..b3dbb5068f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/DeliveryMethodNotificationTemplate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/DeliveryMethodNotificationTemplate.java @@ -35,7 +35,7 @@ import java.util.List; @Type(name = "SMS", value = SmsDeliveryMethodNotificationTemplate.class), @Type(name = "SLACK", value = SlackDeliveryMethodNotificationTemplate.class), @Type(name = "MICROSOFT_TEAMS", value = MicrosoftTeamsDeliveryMethodNotificationTemplate.class), - @Type(name = "MOBILE", value = MobileDeliveryMethodNotificationTemplate.class) + @Type(name = "MOBILE_APP", value = MobileAppDeliveryMethodNotificationTemplate.class) }) @Data @NoArgsConstructor diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/MobileDeliveryMethodNotificationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/MobileAppDeliveryMethodNotificationTemplate.java similarity index 78% rename from common/data/src/main/java/org/thingsboard/server/common/data/notification/template/MobileDeliveryMethodNotificationTemplate.java rename to common/data/src/main/java/org/thingsboard/server/common/data/notification/template/MobileAppDeliveryMethodNotificationTemplate.java index 42cff39c81..8d567e12ac 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/MobileDeliveryMethodNotificationTemplate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/MobileAppDeliveryMethodNotificationTemplate.java @@ -28,7 +28,7 @@ import java.util.List; @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) -public class MobileDeliveryMethodNotificationTemplate extends DeliveryMethodNotificationTemplate implements HasSubject { +public class MobileAppDeliveryMethodNotificationTemplate extends DeliveryMethodNotificationTemplate implements HasSubject { @NotEmpty private String subject; @@ -38,19 +38,19 @@ public class MobileDeliveryMethodNotificationTemplate extends DeliveryMethodNoti TemplatableValue.of(this::getSubject, this::setSubject) ); - public MobileDeliveryMethodNotificationTemplate(MobileDeliveryMethodNotificationTemplate other) { + public MobileAppDeliveryMethodNotificationTemplate(MobileAppDeliveryMethodNotificationTemplate other) { super(other); this.subject = other.subject; } @Override public NotificationDeliveryMethod getMethod() { - return NotificationDeliveryMethod.MOBILE; + return NotificationDeliveryMethod.MOBILE_APP; } @Override - public MobileDeliveryMethodNotificationTemplate copy() { - return new MobileDeliveryMethodNotificationTemplate(this); + public MobileAppDeliveryMethodNotificationTemplate copy() { + return new MobileAppDeliveryMethodNotificationTemplate(this); } @Override diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/settings/UserSettingsType.java b/common/data/src/main/java/org/thingsboard/server/common/data/settings/UserSettingsType.java index cd627821ac..7449221d8c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/settings/UserSettingsType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/settings/UserSettingsType.java @@ -19,7 +19,14 @@ import lombok.Getter; public enum UserSettingsType { - GENERAL, VISITED_DASHBOARDS(true), QUICK_LINKS, DOC_LINKS, DASHBOARDS, GETTING_STARTED, NOTIFICATIONS; + GENERAL, + VISITED_DASHBOARDS(true), + QUICK_LINKS, + DOC_LINKS, + DASHBOARDS, + GETTING_STARTED, + NOTIFICATIONS, + MOBILE(true); @Getter private final boolean reserved; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java index 391981b8f4..ee8f041498 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java @@ -135,6 +135,11 @@ public class JpaUserDao extends JpaAbstractDao implements User DaoUtil.toPageable(pageLink))); } + @Override + public void unassignFcmToken(TenantId tenantId, String fcmToken) { + userRepository.unassignFcmToken(fcmToken); + } + @Override public Long countByTenantId(TenantId tenantId) { return userRepository.countByTenantId(tenantId.getId()); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java index ab7a537ec7..d07ddc739b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java @@ -18,8 +18,10 @@ package org.thingsboard.server.dao.sql.user; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; +import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.dao.model.sql.UserEntity; @@ -71,4 +73,9 @@ public interface UserRepository extends JpaRepository { Long countByTenantId(UUID tenantId); + @Query(value = "UPDATE user_settings SET settings = (settings::jsonb - 'fcmToken')::text WHERE type = 'MOBILE'", nativeQuery = true) + @Modifying + @Transactional + void unassignFcmToken(@Param("fcmToken") String fcmToken); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java index 268e138b2f..d37107a91f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java @@ -101,4 +101,6 @@ public interface UserDao extends Dao, TenantEntityDao { PageData findByAuthorityAndTenantProfilesIds(Authority authority, List tenantProfilesIds, PageLink pageLink); + void unassignFcmToken(TenantId tenantId, String fcmToken); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java index a584fef86c..67ace67a49 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java @@ -21,6 +21,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.util.concurrent.ListenableFuture; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.ApplicationEventPublisher; import org.springframework.security.authentication.DisabledException; @@ -30,6 +31,7 @@ import org.springframework.transaction.annotation.Transactional; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.UserMobileInfo; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; @@ -43,6 +45,8 @@ import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.common.data.security.event.UserCredentialsInvalidationEvent; +import org.thingsboard.server.common.data.settings.UserSettings; +import org.thingsboard.server.common.data.settings.UserSettingsType; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.entity.EntityCountService; import org.thingsboard.server.dao.eventsourcing.ActionEntityEvent; @@ -85,6 +89,7 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic private final UserDao userDao; private final UserCredentialsDao userCredentialsDao; private final UserAuthSettingsDao userAuthSettingsDao; + private final UserSettingsService userSettingsService; private final DataValidator userValidator; private final DataValidator userCredentialsValidator; private final ApplicationEventPublisher eventPublisher; @@ -391,6 +396,22 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic saveUser(tenantId, user); } + @Override + public void saveMobileInfo(TenantId tenantId, UserId userId, UserMobileInfo mobileInfo) { + if (StringUtils.isNotEmpty(mobileInfo.getFcmToken())) { + // unassigning fcm token from other users, in case we didn't clean up it on log out or mobile app uninstall + userDao.unassignFcmToken(tenantId, mobileInfo.getFcmToken()); + } + userSettingsService.updateUserSettings(tenantId, userId, UserSettingsType.MOBILE, JacksonUtil.valueToTree(mobileInfo)); + } + + @Override + public UserMobileInfo findMobileInfo(TenantId tenantId, UserId userId) { + return Optional.ofNullable(userSettingsService.findUserSettings(tenantId, userId, UserSettingsType.MOBILE)) + .map(UserSettings::getSettings).map(settings -> JacksonUtil.treeToValue(settings, UserMobileInfo.class)) + .orElse(null); + } + @Override public int increaseFailedLoginAttempts(TenantId tenantId, UserId userId) { log.trace("Executing onUserLoginIncorrectCredentials [{}]", userId); diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/notification/FirebaseService.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/notification/FirebaseService.java index e0da1e9b66..e89b061ed1 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/notification/FirebaseService.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/notification/FirebaseService.java @@ -19,6 +19,6 @@ import org.thingsboard.server.common.data.id.TenantId; public interface FirebaseService { - void sendMessage(TenantId tenantId, String credentials, String fcmToken, String title, String body); + void sendMessage(TenantId tenantId, String credentials, String fcmToken, String title, String body) throws Exception; } diff --git a/ui-ngx/src/app/modules/home/pages/admin/sms-provider.component.html b/ui-ngx/src/app/modules/home/pages/admin/sms-provider.component.html index 48ebf37249..3a0a7e2a8f 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/sms-provider.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/sms-provider.component.html @@ -75,14 +75,14 @@ -
+
+ [existingFileName]="notificationSettingsForm.get('deliveryMethodsConfigs.MOBILE_APP.firebaseServiceAccountCredentialsFileName')?.value" + (fileNameChanged)="notificationSettingsForm?.get('deliveryMethodsConfigs.MOBILE_APP.firebaseServiceAccountCredentialsFileName').patchValue($event)">
diff --git a/ui-ngx/src/app/modules/home/pages/admin/sms-provider.component.ts b/ui-ngx/src/app/modules/home/pages/admin/sms-provider.component.ts index 5c0520cdb1..1867ad69ff 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/sms-provider.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/sms-provider.component.ts @@ -117,7 +117,7 @@ export class SmsProviderComponent extends PageComponent implements HasConfirmFor SLACK: this.fb.group({ botToken: [''] }), - MOBILE: this.fb.group({ + MOBILE_APP: this.fb.group({ firebaseServiceAccountCredentialsFileName: [''], firebaseServiceAccountCredentials: [''] }) diff --git a/ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.component.html b/ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.component.html index a7cc0cea42..d6b5ad3d8a 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.component.html @@ -446,9 +446,9 @@ - {{ 'notification.delivery-method.mobile' | translate }} + {{ 'notification.delivery-method.mobile-app' | translate }}
{{ 'notification.input-field-support-templatization' | translate}}
-
+
-
notification.delivery-method.mobile-preview
+
notification.delivery-method.mobile-app-preview
-
{{ preview.processedTemplates.MOBILE.subject }}
-
{{ preview.processedTemplates.MOBILE.body }}
+
{{ preview.processedTemplates.MOBILE_APP.subject }}
+
{{ preview.processedTemplates.MOBILE_APP.body }}
diff --git a/ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.componet.ts b/ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.componet.ts index 1ac53c87ce..c6d4f62bb4 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.componet.ts +++ b/ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.componet.ts @@ -306,7 +306,7 @@ export class SentNotificationDialogComponent extends allowConfigureDeliveryMethod(deliveryMethod: NotificationDeliveryMethod): boolean { const tenantAllowConfigureDeliveryMethod = new Set([ NotificationDeliveryMethod.SLACK, - NotificationDeliveryMethod.MOBILE + NotificationDeliveryMethod.MOBILE_APP ]); if (deliveryMethod === NotificationDeliveryMethod.WEB) { return false; @@ -329,7 +329,7 @@ export class SentNotificationDialogComponent extends return '/settings/outgoing-mail'; case NotificationDeliveryMethod.SMS: case NotificationDeliveryMethod.SLACK: - case NotificationDeliveryMethod.MOBILE: + case NotificationDeliveryMethod.MOBILE_APP: return '/settings/notifications'; } } diff --git a/ui-ngx/src/app/modules/home/pages/notification/template/template-configuration.ts b/ui-ngx/src/app/modules/home/pages/notification/template/template-configuration.ts index 53b5a44a77..09bc937544 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/template/template-configuration.ts +++ b/ui-ngx/src/app/modules/home/pages/notification/template/template-configuration.ts @@ -148,7 +148,7 @@ export abstract class TemplateConfiguration extends DialogComponent< [NotificationDeliveryMethod.SMS, this.smsTemplateForm], [NotificationDeliveryMethod.SLACK, this.slackTemplateForm], [NotificationDeliveryMethod.MICROSOFT_TEAMS, this.microsoftTeamsTemplateForm], - [NotificationDeliveryMethod.MOBILE, this.mobileTemplateForm] + [NotificationDeliveryMethod.MOBILE_APP, this.mobileTemplateForm] ]); } diff --git a/ui-ngx/src/app/modules/home/pages/notification/template/template-notification-dialog.component.html b/ui-ngx/src/app/modules/home/pages/notification/template/template-notification-dialog.component.html index c13e33b6e3..78208e4425 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/template/template-notification-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/notification/template/template-notification-dialog.component.html @@ -364,9 +364,9 @@ - - {{ 'notification.delivery-method.mobile' | translate }} + {{ 'notification.delivery-method.mobile-app' | translate }}
{{ 'notification.input-field-support-templatization' | translate}} ([ @@ -389,7 +389,7 @@ export const NotificationDeliveryMethodTranslateMap = new Map