Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,19 @@
public class FirebaseMessagingConfig {
private Logger logger = LoggerFactory.getLogger(this.getClass().getSimpleName());

@Value("${firebase.enabled:false}")
@Value("${firebase.enabled}")
private boolean firebaseEnabled;

@Value("${firebase.credential-file:}")
@Value("${firebase.credential-file}")
private String firebaseCredentialFile;


@Bean
@ConditionalOnProperty(name = "firebase.enabled", havingValue = "true")
public FirebaseMessaging firebaseMessaging() throws IOException {
logger.info("===== Initializing Firebase =====");
logger.info("firebaseEnabled={}", firebaseEnabled);
logger.info("firebaseCredentialFile={}", firebaseCredentialFile);
if (!firebaseEnabled) {
logger.error("⚠️ Firebase disabled by config");
return null;
Expand All @@ -42,9 +45,11 @@ public FirebaseMessaging firebaseMessaging() throws IOException {
return null; // don't throw, app will still start
}

GoogleCredentials credentials = GoogleCredentials.fromStream(
new ClassPathResource(firebaseCredentialFile).getInputStream()
);
GoogleCredentials credentials =
GoogleCredentials.fromStream(
new FileInputStream(firebaseCredentialFile)
);

FirebaseOptions options = FirebaseOptions.builder()
.setCredentials(credentials)
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,12 @@
import com.iemr.common.model.notification.UserToken;
import com.iemr.common.service.firebaseNotification.FirebaseNotificationService;
import com.iemr.common.utils.exception.IEMRException;
import com.iemr.common.utils.response.ApiResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
Expand All @@ -41,17 +44,60 @@ public class FirebaseNotificationController {
@Autowired
FirebaseNotificationService firebaseNotificationService;

@RequestMapping(value = "sendNotification",method = RequestMethod.POST,headers = "Authorization")
public String sendNotificationByToken(@RequestBody NotificationMessage notificationMessage){
return firebaseNotificationService.sendNotification(notificationMessage);

@PostMapping("/sendNotification")
public ResponseEntity<String> sendNotificationByToken(@RequestBody NotificationMessage notificationMessage) {

logger.info("Received notification request. Token={}, Title={}, ReceiverId={}, Type={}",
notificationMessage.getToken(),
notificationMessage.getTitle(),
notificationMessage.getData() != null ? notificationMessage.getData().get("receiver_user_id") : null,
notificationMessage.getData() != null ? notificationMessage.getData().get("notification_type") : null);

logger.debug("Notification payload: {}", notificationMessage);

try {
String response = firebaseNotificationService.sendNotification(notificationMessage);

logger.info("Notification processed successfully. Response={}", response);

return ResponseEntity.ok(response);

} catch (Exception e) {

logger.error("Failed to process notification request. Token={}, ReceiverId={}, Error={}",
notificationMessage.getToken(),
notificationMessage.getData() != null ? notificationMessage.getData().get("receiver_user_id") : null,
e.getMessage(),
e);

return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Failed to send notification");
}
}

@RequestMapping(value = "updateToken",method = RequestMethod.POST,headers = "Authorization")
public String updateToken(@RequestBody UserToken userToken){
return firebaseNotificationService.updateToken(userToken);
@PostMapping("/updateToken")
public ResponseEntity<?> updateToken(@RequestBody UserToken userToken) {
try {

Object result = firebaseNotificationService.updateToken(userToken);

return ResponseEntity.ok(result);

} catch (IllegalArgumentException e) {

return ResponseEntity.badRequest().body(e.getMessage());

} catch (Exception e) {

logger.error("Error while updating Firebase token", e);

return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Failed to update Firebase token.");
}
}

@RequestMapping(value = "getToken",method = RequestMethod.GET,headers = "Authorization")
@PostMapping(value = "getToken")
public String getUserToken() throws IEMRException {

return firebaseNotificationService.getUserToken();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,26 @@
/*
* AMRIT – Accessible Medical Records via Integrated Technology
*/
package com.iemr.common.data.userToken;

Check warning on line 25 in src/main/java/com/iemr/common/data/userToken/UserFcmTokenData.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this package name to match the regular expression '^[a-z_]+(\.[a-z_][a-z0-9_]*)*$'.

See more on https://sonarcloud.io/project/issues?id=PSMRI_Common-API&issues=AZ-iQibxsCkuEMFqPu_o&open=AZ-iQibxsCkuEMFqPu_o&pullRequest=443

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.persistence.*;
import lombok.Data;

import java.sql.Timestamp;

import static jakarta.persistence.GenerationType.IDENTITY;

@Entity
@Table(name = "user_tokens", schema = "db_iemr")
@Table(name = "user_fcm_tokens", schema = "db_iemr")
@Data
public class UserTokenData {
public class UserFcmTokenData {
@Id
@GeneratedValue(strategy = IDENTITY)
private int id;
@Column(name = "user_id")
Integer userId;
private Integer userId;
@Column(name = "token")
String token;
private String token;
@Column(name = "updated_at")
Timestamp updatedAt;
private Timestamp updatedAt;
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,4 @@ public class NotificationMessage {
private String title;
private String body;
private Map<String ,String> data;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,17 @@
/*
* AMRIT – Accessible Medical Records via Integrated Technology
*/
package com.iemr.common.repo.userToken;

Check warning on line 25 in src/main/java/com/iemr/common/repo/userToken/UserFcmTokenRepo.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this package name to match the regular expression '^[a-z_]+(\.[a-z_][a-z0-9_]*)*$'.

See more on https://sonarcloud.io/project/issues?id=PSMRI_Common-API&issues=AZ-iQicEsCkuEMFqPu_p&open=AZ-iQicEsCkuEMFqPu_p&pullRequest=443

import com.iemr.common.data.userToken.UserTokenData;
import com.iemr.common.data.userToken.UserFcmTokenData;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

public interface UserTokenRepo extends JpaRepository<UserTokenData,Integer> {
import java.util.Optional;

@Repository

public interface UserFcmTokenRepo extends JpaRepository<UserFcmTokenData,Integer> {

Optional<UserFcmTokenData> findByUserId(Integer userId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,35 +26,38 @@

import com.google.firebase.FirebaseException;
import com.google.firebase.messaging.FirebaseMessaging;
import com.google.firebase.messaging.FirebaseMessagingException;
import com.google.firebase.messaging.Message;
import com.google.firebase.messaging.Notification;
import com.iemr.common.data.userToken.UserTokenData;
import com.google.gson.Gson;
import com.iemr.common.config.firebase.FirebaseMessagingConfig;
import com.iemr.common.data.userToken.UserFcmTokenData;
import com.iemr.common.model.notification.NotificationMessage;
import com.iemr.common.model.notification.UserToken;
import com.iemr.common.repo.userToken.UserTokenRepo;
import com.iemr.common.repo.userToken.UserFcmTokenRepo;
import com.iemr.common.utils.CookieUtil;
import com.iemr.common.utils.JwtUtil;
import com.iemr.common.utils.exception.IEMRException;
import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.sql.Timestamp;
import java.util.Optional;

@Service
public class FirebaseNotificationService {
final Logger logger = LoggerFactory.getLogger(this.getClass().getName());

@Autowired(required = false)
FirebaseMessaging firebaseMessaging;
private FirebaseMessaging firebaseMessaging;

@Autowired
private UserTokenRepo userTokenRepo;
private UserFcmTokenRepo userTokenRepo;

@Autowired
private CookieUtil cookieUtil;
Expand All @@ -64,42 +67,77 @@

private Message message;

@Value("${firebase.enabled}")
private boolean firebaseEnabled;

@Value("${firebase.credential-file}")
private String firebaseCredentialFile;



public String sendNotification(NotificationMessage notificationMessage) {

logger.info("===== Initializing Firebase =====");
logger.info("firebaseEnabled={}", firebaseEnabled);
logger.info("firebaseCredentialFile={}", firebaseCredentialFile);

logger.info("========== FCM Notification Request ==========");
logger.info("Request : {}", new Gson().toJson(notificationMessage));

if (firebaseMessaging == null) {
logger.error("⚠️ Firebase is not configured, skipping notification");
return null;
logger.error("FirebaseMessaging bean is not initialized.");
return "FirebaseMessaging bean is not initialized.";
}

Notification notification = Notification.builder().setTitle(notificationMessage.getTitle()).setBody(notificationMessage.getBody()).build();
try {

Message message = Message.builder().setTopic(notificationMessage.getToken()).setNotification(notification).putAllData(notificationMessage.getData()).build();
Notification notification = Notification.builder()
.setTitle(notificationMessage.getTitle())
.setBody(notificationMessage.getBody())
.build();

Message message = Message.builder()

Check warning on line 99 in src/main/java/com/iemr/common/service/firebaseNotification/FirebaseNotificationService.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename "message" which hides the field declared at line 68.

See more on https://sonarcloud.io/project/issues?id=PSMRI_Common-API&issues=AZ-iQiYBsCkuEMFqPu_n&open=AZ-iQiYBsCkuEMFqPu_n&pullRequest=443
.setToken(notificationMessage.getToken())
.setNotification(notification)
.putAllData(notificationMessage.getData())
.build();

try {
String response = FirebaseMessaging.getInstance().send(message);
logger.info("Sending notification to token: {}", notificationMessage.getToken());

String response = firebaseMessaging.send(message);

logger.info("Notification sent successfully.");
logger.info("Firebase Message Id : {}", response);

return response;
} catch (FirebaseException e) {
return "Error sending notification";

} catch (FirebaseMessagingException e) {

logger.error("FirebaseMessagingException");
logger.error("Error Code : {}", e.getMessagingErrorCode());
logger.error("Error Message : {}", e.getMessage(), e);

return "FCM Error : " + e.getMessage();

} catch (Exception e) {

logger.error("Unexpected exception while sending notification.", e);

return "Unexpected Error : " + e.getMessage();
}
}

public String updateToken(UserToken userToken) {
Optional<UserTokenData> existingTokenData = userTokenRepo.findById(userToken.getUserId());
Optional<UserFcmTokenData> existingTokenData = userTokenRepo.findByUserId(userToken.getUserId());

UserTokenData userTokenData;
UserFcmTokenData userTokenData;

if (existingTokenData.isPresent()) {
userTokenData = existingTokenData.get();
userTokenData.setToken(userToken.getToken());
userTokenData.setUpdatedAt(new Timestamp(System.currentTimeMillis()));
} else {
userTokenData = new UserTokenData();
userTokenData = new UserFcmTokenData();
userTokenData.setUserId(userToken.getUserId());
userTokenData.setToken(userToken.getToken());
userTokenData.setUpdatedAt(new Timestamp(System.currentTimeMillis()));
}

userTokenRepo.save(userTokenData);
Expand All @@ -111,7 +149,7 @@
.getRequest();
String jwtTokenFromCookie = cookieUtil.getJwtTokenFromCookie(requestHeader);
return userTokenRepo.findById(Integer.parseInt(jwtUtil.getUserIdFromToken(jwtTokenFromCookie))) // because your userId is Long in DB
.map(UserTokenData::getToken)
.map(UserFcmTokenData::getToken)
.orElse(null); //
}

Expand Down
Loading