Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Backend Functionality for Email Sending #89

Merged
merged 5 commits into from
Oct 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,20 +51,20 @@
<scope>runtime</scope>
<optional>true</optional>
</dependency>

<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>

<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>

<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
Expand Down Expand Up @@ -93,13 +93,13 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-client</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableAsync
@EnableAsync(proxyTargetClass = true)
@EnableScheduling
@EnableCaching
@EnableCaching(proxyTargetClass = true)
public class LibrarymanApiApplication {

public static void main(String[] args) {
SpringApplication.run(LibrarymanApiApplication.class, args);
}

}
}
2 changes: 1 addition & 1 deletion src/main/java/com/libraryman_api/book/BookController.java
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,4 @@ public BookDto updateBook(@PathVariable int id, @RequestBody BookDto bookDtoDeta
public void deleteBook(@PathVariable int id) {
bookService.deleteBook(id);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -349,4 +349,4 @@ public BorrowingsDto EntityToDto(Borrowings borrowings) {
borrowingsDto.setBook(bookService.EntityToDto(borrowings.getBook()));
return borrowingsDto;
}
}
}
72 changes: 49 additions & 23 deletions src/main/java/com/libraryman_api/email/EmailService.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,62 +17,88 @@
import org.springframework.stereotype.Service;

/**
* Service class for sending emails asynchronously.
* This class handles the construction and sending of MIME email messages.
* Unified service class for sending emails asynchronously.
* Handles both general email sending and notifications.
*/
@Service
public class EmailService implements EmailSender {

private static final Logger LOGGER = LoggerFactory.getLogger(EmailService.class);

private final NotificationRepository notificationRepository;
private final JavaMailSender mailSender;
@Value("${spring.mail.properties.domain_name}")

@Value("${spring.mail.properties.domain_name}") // Domain name from application properties
private String domainName;

/**
* Constructs a new {@code EmailService} with the specified {@link NotificationRepository} and {@link JavaMailSender}.
*
* @param notificationRepository the repository for managing notification entities
* @param mailSender the mail sender for sending email messages
*/
public EmailService(NotificationRepository notificationRepository, JavaMailSender mailSender) {
this.notificationRepository = notificationRepository;
this.mailSender = mailSender;
}

/**
* Sends an email asynchronously to the specified recipient.
* If the email is successfully sent, the notification status is updated to SENT.
* If the email fails to send, the notification status is updated to FAILED and an exception is thrown.
* Sends a general email asynchronously.
*
* @param to the recipient's email address
* @param email the content of the email to send
* @param subject the subject of the email
* @param notification the {@link Notifications} object representing the email notification
* @param to recipient's email
* @param body email content (HTML supported)
* @param subject subject of the email
*/
@Override
@Async
public void send(String to, String email, String subject, Notifications notification) {
public void sendEmail(String to, String body, String subject) {
sendEmail(to, body, subject, null); // Default 'from' to null
}

/**
* Sends a general email asynchronously.
*
* @param to recipient's email
* @param body email content (HTML supported)
* @param subject subject of the email
* @param from sender's email address (overrides default if provided)
*/
@Async
public void sendEmail(String to, String body, String subject, String from) {
try {
MimeMessage mimeMessage = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, "utf-8");
helper.setText(email, true);

helper.setText(body, true); // true = enable HTML content
helper.setTo(to);
helper.setSubject(subject);
helper.setFrom(domainName);
helper.setFrom(from != null ? from : domainName); // Use provided sender or default domain

mailSender.send(mimeMessage);
} catch (MessagingException e) {
LOGGER.error("Failed to send email", e);
throw new IllegalStateException("Failed to send email", e);
}
}

/**
* Sends a notification email and updates notification status.
*
* @param to recipient's email
* @param email email content
* @param subject subject of the email
* @param notification notification entity to update status
*/
@Override
@Async
public void send(String to, String email, String subject, Notifications notification) {
try {
sendEmail(to, email, subject); // Reuse sendEmail method for notifications

// Update notification status to SENT
notification.setNotificationStatus(NotificationStatus.SENT);
notificationRepository.save(notification);
} catch (MessagingException e) {
LOGGER.error("Failed to send email", e);
} catch (Exception e) {
LOGGER.error("Failed to send notification email", e);

// Update notification status to FAILED
notification.setNotificationStatus(NotificationStatus.FAILED);
notificationRepository.save(notification);

throw new IllegalStateException("Failed to send email", e);
throw new IllegalStateException("Failed to send notification email", e);
}
}
}
2 changes: 1 addition & 1 deletion src/main/java/com/libraryman_api/member/MemberService.java
Original file line number Diff line number Diff line change
Expand Up @@ -225,4 +225,4 @@ public MembersDto EntityToDto(Members members) {
membersDto.setMembershipDate(members.getMembershipDate());
return membersDto;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,44 +1,71 @@
package com.libraryman_api.newsletter;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

@RestController
@RequestMapping("/api/newsletter")
public class NewsletterController {

@Autowired
private NewsletterService newsletterService;
private final NewsletterService newsletterService;

public NewsletterController(NewsletterService newsletterService) {
this.newsletterService = newsletterService;
}

// Subscribe endpoint
// Subscribe Endpoint
@PostMapping("/subscribe")
public ResponseEntity<String> subscribe(@RequestBody Map<String, String> requestBody) {
String email = requestBody.get("email");
public ResponseEntity<String> subscribe(@RequestParam String email) {
try {
String result = newsletterService.subscribe(email);

// Call the service to handle subscription
String response = newsletterService.subscribe(email);
switch (result) {
case "Invalid email format.":
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(result); // 400 Bad Request

// Return response from the service
if (response.equals("Invalid email format.") || response.equals("Email is already subscribed.")) {
return ResponseEntity.badRequest().body(response);
}
case "Email is already subscribed.":
return ResponseEntity.status(HttpStatus.CONFLICT).body(result); // 409 Conflict

return ResponseEntity.ok(response);
}
case "You have successfully subscribed!":
return ResponseEntity.status(HttpStatus.CREATED).body(result); // 201 Created

// Unsubscribe endpoint using token
@GetMapping("/unsubscribe/{token}")
public ResponseEntity<String> unsubscribe(@PathVariable String token) {
String response = newsletterService.unsubscribe(token);
case "You have successfully re-subscribed!":
return ResponseEntity.status(HttpStatus.OK).body(result); // 200 OK

// Check if the response indicates an error
if (response.equals("Invalid or expired token.") || response.equals("You are already unsubscribed.")) {
return ResponseEntity.badRequest().body(response);
default:
return ResponseEntity.status(HttpStatus.OK).body(result); // Default 200 OK
}
} catch (Exception e) {
// Handle unexpected errors
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("An error occurred while processing your subscription.");
}
}

return ResponseEntity.ok(response);
// Unsubscribe Endpoint
@GetMapping("/unsubscribe")
public ResponseEntity<String> unsubscribe(@RequestParam String token) {
try {
String result = newsletterService.unsubscribe(token);

switch (result) {
case "Invalid or expired token.":
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(result); // 404 Not Found

case "You are already unsubscribed.":
return ResponseEntity.status(HttpStatus.CONFLICT).body(result); // 409 Conflict

case "You have successfully unsubscribed!":
return ResponseEntity.status(HttpStatus.OK).body(result); // 200 OK

default:
return ResponseEntity.status(HttpStatus.OK).body(result); // Default 200 OK
}
} catch (Exception e) {
// Handle unexpected errors
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("An error occurred while processing your unsubscription.");
}
}
}
Loading
Loading