From 3457fd5f04ec294b133ef417d76dc5261da6a066 Mon Sep 17 00:00:00 2001 From: shreya kore Date: Tue, 15 Oct 2024 13:39:40 +0530 Subject: [PATCH 1/3] Add backend functionalities for newsletter signup - Implemented NewsletterSubscriber entity to represent subscribers in the database. - Created NewsletterService to handle business logic for email subscriptions. - Developed NewsletterController to manage incoming subscription requests and validation. - Added NewsletterRepository for database interactions related to email subscriptions. - Improved error handling for invalid email formats. --- .../newsletter/NewsletterController.java | 32 +++++++++++++++ .../newsletter/NewsletterService.java | 41 +++++++++++++++++++ .../newsletter/NewsletterSubscriber.java | 39 ++++++++++++++++++ .../NewsletterSubscriberRepository.java | 10 +++++ .../application-development.properties | 5 +-- 5 files changed, 123 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/libraryman_api/newsletter/NewsletterController.java create mode 100644 src/main/java/com/libraryman_api/newsletter/NewsletterService.java create mode 100644 src/main/java/com/libraryman_api/newsletter/NewsletterSubscriber.java create mode 100644 src/main/java/com/libraryman_api/newsletter/NewsletterSubscriberRepository.java diff --git a/src/main/java/com/libraryman_api/newsletter/NewsletterController.java b/src/main/java/com/libraryman_api/newsletter/NewsletterController.java new file mode 100644 index 0000000..137eaf1 --- /dev/null +++ b/src/main/java/com/libraryman_api/newsletter/NewsletterController.java @@ -0,0 +1,32 @@ +package com.libraryman_api.newsletter; + + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Map; + +@RestController +@RequestMapping("/api/newsletter") +public class NewsletterController { + @Autowired + private NewsletterService newsletterService; + + @PostMapping("/subscribe") + public ResponseEntity subscribe(@RequestBody Map requestBody) { + String email = requestBody.get("email"); + + // Validate the email field + if (email == null || !newsletterService.isValidEmail(email)) { + return ResponseEntity.badRequest().body("Invalid email format"); + } + + // Process the subscription + String response = newsletterService.subscribe(email); + return ResponseEntity.ok(response); + } +} diff --git a/src/main/java/com/libraryman_api/newsletter/NewsletterService.java b/src/main/java/com/libraryman_api/newsletter/NewsletterService.java new file mode 100644 index 0000000..a5ae68b --- /dev/null +++ b/src/main/java/com/libraryman_api/newsletter/NewsletterService.java @@ -0,0 +1,41 @@ +package com.libraryman_api.newsletter; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@Service +public class NewsletterService { + + @Autowired + private NewsletterSubscriberRepository subscriberRepository; + + public String subscribe(String email) { + // Validate email format + if (!isValidEmail(email)) { + return "Invalid email format."; + } + + // Check if the email already exists + if (subscriberRepository.findByEmail(email).isPresent()) { + return "Email already subscribed."; + } + + // Save new subscriber + NewsletterSubscriber subscriber = new NewsletterSubscriber(email); + subscriberRepository.save(subscriber); + return "You have successfully signed up for the newsletter!"; // Confirmation message + } + + public boolean isValidEmail(String email) { + if (email == null) return false; // Handle null case + email = email.trim(); + // Regular expression for validating an email address + String emailRegex = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"; + Pattern pattern = Pattern.compile(emailRegex); + Matcher matcher = pattern.matcher(email); + return matcher.matches(); + } +} diff --git a/src/main/java/com/libraryman_api/newsletter/NewsletterSubscriber.java b/src/main/java/com/libraryman_api/newsletter/NewsletterSubscriber.java new file mode 100644 index 0000000..6fdaa34 --- /dev/null +++ b/src/main/java/com/libraryman_api/newsletter/NewsletterSubscriber.java @@ -0,0 +1,39 @@ +package com.libraryman_api.newsletter; + +import jakarta.persistence.*; + +@Entity +@Table(name = "newsletter_subscribers") +public class NewsletterSubscriber { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false, unique = true) + private String email; + + // Constructors, getters, and setters + + public NewsletterSubscriber() { + } + + public NewsletterSubscriber(String email) { + this.email = email; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } +} diff --git a/src/main/java/com/libraryman_api/newsletter/NewsletterSubscriberRepository.java b/src/main/java/com/libraryman_api/newsletter/NewsletterSubscriberRepository.java new file mode 100644 index 0000000..5006267 --- /dev/null +++ b/src/main/java/com/libraryman_api/newsletter/NewsletterSubscriberRepository.java @@ -0,0 +1,10 @@ +package com.libraryman_api.newsletter; + +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + public interface NewsletterSubscriberRepository extends JpaRepository { + Optional findByEmail(String email); + + } + diff --git a/src/main/resources/application-development.properties b/src/main/resources/application-development.properties index aedaf67..c3cf3e5 100644 --- a/src/main/resources/application-development.properties +++ b/src/main/resources/application-development.properties @@ -42,7 +42,4 @@ spring.mail.username=Add_Your_Mail_Service_Username spring.mail.password=Add_Your_Mail_Service_Password spring.mail.properties.mail.smtp.auth=Add_Your_Mail_Service_SMTP spring.mail.properties.mail.starttls.enable=Add_Your_Mail_Service_Start_TLS -spring.mail.properties.domain_name=Add_Your_Mail_Service_Domain_Name - - - +spring.mail.properties.domain_name=Add_Your_Mail_Service_Domain_Name \ No newline at end of file From 8fb452aa8b06b239256096a524f63e5a648889af Mon Sep 17 00:00:00 2001 From: shreya kore Date: Thu, 17 Oct 2024 12:55:46 +0530 Subject: [PATCH 2/3] Implement backend functionality for newsletter subscription and unsubscription - Added the ability to subscribe users to the newsletter, ensuring that email validation is performed. - Implemented logic to handle resubscription for users who previously unsubscribed, generating a new unsubscribe token for them. - Established an unsubscribe process that sets the subscriber's status to inactive while retaining the record in the database. - Enhanced the database model to include an active status flag, which is updated based on subscription actions. - Included comprehensive error handling for scenarios such as invalid email formats and expired tokens during unsubscribe requests. - Updated the controller to handle subscription and unsubscription requests effectively, providing clear responses to the client. These enhancements improve the overall user experience by providing seamless management of newsletter subscriptions. --- .../newsletter/NewsletterController.java | 32 ++++++++---- .../newsletter/NewsletterService.java | 51 +++++++++++++++---- .../newsletter/NewsletterSubscriber.java | 30 ++++++++++- .../NewsletterSubscriberRepository.java | 10 ++-- .../application-development.properties | 2 +- .../application-production.properties | 2 +- 6 files changed, 102 insertions(+), 25 deletions(-) diff --git a/src/main/java/com/libraryman_api/newsletter/NewsletterController.java b/src/main/java/com/libraryman_api/newsletter/NewsletterController.java index 137eaf1..fe038fe 100644 --- a/src/main/java/com/libraryman_api/newsletter/NewsletterController.java +++ b/src/main/java/com/libraryman_api/newsletter/NewsletterController.java @@ -1,32 +1,44 @@ package com.libraryman_api.newsletter; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import java.util.Map; @RestController @RequestMapping("/api/newsletter") public class NewsletterController { + @Autowired private NewsletterService newsletterService; + // Subscribe endpoint @PostMapping("/subscribe") public ResponseEntity subscribe(@RequestBody Map requestBody) { String email = requestBody.get("email"); - // Validate the email field - if (email == null || !newsletterService.isValidEmail(email)) { - return ResponseEntity.badRequest().body("Invalid email format"); + // Call the service to handle subscription + String response = newsletterService.subscribe(email); + + // Return response from the service + if (response.equals("Invalid email format.") || response.equals("Email is already subscribed.")) { + return ResponseEntity.badRequest().body(response); + } + + return ResponseEntity.ok(response); + } + + // Unsubscribe endpoint using token + @GetMapping("/unsubscribe/{token}") + public ResponseEntity unsubscribe(@PathVariable String token) { + String response = newsletterService.unsubscribe(token); + + // 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); } - // Process the subscription - String response = newsletterService.subscribe(email); return ResponseEntity.ok(response); } } diff --git a/src/main/java/com/libraryman_api/newsletter/NewsletterService.java b/src/main/java/com/libraryman_api/newsletter/NewsletterService.java index a5ae68b..034fd78 100644 --- a/src/main/java/com/libraryman_api/newsletter/NewsletterService.java +++ b/src/main/java/com/libraryman_api/newsletter/NewsletterService.java @@ -3,6 +3,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -12,27 +13,59 @@ public class NewsletterService { @Autowired private NewsletterSubscriberRepository subscriberRepository; + // Subscribe user after validating email public String subscribe(String email) { - // Validate email format if (!isValidEmail(email)) { return "Invalid email format."; } // Check if the email already exists - if (subscriberRepository.findByEmail(email).isPresent()) { - return "Email already subscribed."; + Optional optionalSubscriber = subscriberRepository.findByEmail(email); + + if (optionalSubscriber.isPresent()) { + NewsletterSubscriber subscriber = optionalSubscriber.get(); + + // If the subscriber is inactive, reactivate them + if (!subscriber.isActive()) { + subscriber.setActive(true); // Reactivate the subscriber + subscriber.regenerateToken(); // Generate a new token + subscriberRepository.save(subscriber); // Save the updated subscriber + return "You have successfully re-subscribed!"; + } else { + return "Email is already subscribed."; + } } - // Save new subscriber + // Save new subscriber if not present NewsletterSubscriber subscriber = new NewsletterSubscriber(email); subscriberRepository.save(subscriber); - return "You have successfully signed up for the newsletter!"; // Confirmation message + return "You have successfully subscribed!"; } - public boolean isValidEmail(String email) { - if (email == null) return false; // Handle null case - email = email.trim(); - // Regular expression for validating an email address + // Unsubscribe user using the token + public String unsubscribe(String token) { + Optional optionalSubscriber = subscriberRepository.findByUnsubscribeToken(token); + + if (optionalSubscriber.isEmpty()) { + return "Invalid or expired token."; + } + + NewsletterSubscriber subscriber = optionalSubscriber.get(); + + if (!subscriber.isActive()) { + return "You are already unsubscribed."; + } + + subscriber.setActive(false); // Set active to false + subscriberRepository.save(subscriber); // Save the updated subscriber + return "You have successfully unsubscribed!"; + } + + // Email validation logic + private boolean isValidEmail(String email) { + if (email == null || email.trim().isEmpty()) { + return false; + } String emailRegex = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"; Pattern pattern = Pattern.compile(emailRegex); Matcher matcher = pattern.matcher(email); diff --git a/src/main/java/com/libraryman_api/newsletter/NewsletterSubscriber.java b/src/main/java/com/libraryman_api/newsletter/NewsletterSubscriber.java index 6fdaa34..75412b5 100644 --- a/src/main/java/com/libraryman_api/newsletter/NewsletterSubscriber.java +++ b/src/main/java/com/libraryman_api/newsletter/NewsletterSubscriber.java @@ -1,10 +1,12 @@ package com.libraryman_api.newsletter; import jakarta.persistence.*; +import java.util.UUID; @Entity @Table(name = "newsletter_subscribers") public class NewsletterSubscriber { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @@ -12,15 +14,24 @@ public class NewsletterSubscriber { @Column(nullable = false, unique = true) private String email; - // Constructors, getters, and setters + @Column(nullable = false) + private boolean active = true; // Manage subscription status + + @Column(name = "unsubscribe_token", nullable = false, unique = true) + private String unsubscribeToken; // Token for unsubscribing + // Default constructor that initializes the token public NewsletterSubscriber() { + this.unsubscribeToken = UUID.randomUUID().toString(); // Generate token by default } + // Constructor to initialize with email public NewsletterSubscriber(String email) { + this(); this.email = email; } + // Getters and setters public Long getId() { return id; } @@ -36,4 +47,21 @@ public String getEmail() { public void setEmail(String email) { this.email = email; } + + public boolean isActive() { + return active; + } + + public void setActive(boolean active) { + this.active = active; + } + + public String getUnsubscribeToken() { + return unsubscribeToken; + } + + // Method to regenerate a new token + public void regenerateToken() { + this.unsubscribeToken = UUID.randomUUID().toString(); + } } diff --git a/src/main/java/com/libraryman_api/newsletter/NewsletterSubscriberRepository.java b/src/main/java/com/libraryman_api/newsletter/NewsletterSubscriberRepository.java index 5006267..1e6d215 100644 --- a/src/main/java/com/libraryman_api/newsletter/NewsletterSubscriberRepository.java +++ b/src/main/java/com/libraryman_api/newsletter/NewsletterSubscriberRepository.java @@ -3,8 +3,12 @@ import org.springframework.data.jpa.repository.JpaRepository; import java.util.Optional; - public interface NewsletterSubscriberRepository extends JpaRepository { - Optional findByEmail(String email); - } +public interface NewsletterSubscriberRepository extends JpaRepository { + // Find a subscriber by email + Optional findByEmail(String email); + + // Find a subscriber by unsubscribe token + Optional findByUnsubscribeToken(String unsubscribeToken); +} diff --git a/src/main/resources/application-development.properties b/src/main/resources/application-development.properties index c3cf3e5..95f73f2 100644 --- a/src/main/resources/application-development.properties +++ b/src/main/resources/application-development.properties @@ -42,4 +42,4 @@ spring.mail.username=Add_Your_Mail_Service_Username spring.mail.password=Add_Your_Mail_Service_Password spring.mail.properties.mail.smtp.auth=Add_Your_Mail_Service_SMTP spring.mail.properties.mail.starttls.enable=Add_Your_Mail_Service_Start_TLS -spring.mail.properties.domain_name=Add_Your_Mail_Service_Domain_Name \ No newline at end of file +spring.mail.properties.domain_name=Add_Your_Mail_Service_Domain_Name diff --git a/src/main/resources/application-production.properties b/src/main/resources/application-production.properties index 6b8edc4..4d50b9f 100644 --- a/src/main/resources/application-production.properties +++ b/src/main/resources/application-production.properties @@ -14,4 +14,4 @@ spring.mail.username=${MAIL_SERVICE_USERNAME} spring.mail.password=${MAIL_SERVICE_PASSWORD} spring.mail.properties.mail.smtp.auth=${MAIL_SERVICE_SMTP} spring.mail.properties.mail.starttls.enable=${MAIL_SERVICE_STARTTLS} -spring.mail.properties.domain_name=${MAIL_SERVICE_DOMAIN_NAME} \ No newline at end of file +spring.mail.properties.domain_name=${MAIL_SERVICE_DOMAIN_NAME} From 8ee919fca54fdb2043561ef872c037368f7dd7b9 Mon Sep 17 00:00:00 2001 From: shreya kore Date: Thu, 17 Oct 2024 13:47:26 +0530 Subject: [PATCH 3/3] Implement backend functionality for newsletter subscription and unsubscription - Added the ability to subscribe users to the newsletter, ensuring that email validation is performed. - Implemented logic to handle resubscription for users who previously unsubscribed, generating a new unsubscribe token for them. - Established an unsubscribe process that sets the subscriber's status to inactive while retaining the record in the database. - Enhanced the database model to include an active status flag, which is updated based on subscription actions. - Included comprehensive error handling for scenarios such as invalid email formats and expired tokens during unsubscribe requests. - Updated the controller to handle subscription and unsubscription requests effectively, providing clear responses to the client. These enhancements improve the overall user experience by providing seamless management of newsletter subscriptions. --- src/main/resources/application-production.properties | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/resources/application-production.properties b/src/main/resources/application-production.properties index 79c1161..3e13ee0 100644 --- a/src/main/resources/application-production.properties +++ b/src/main/resources/application-production.properties @@ -14,3 +14,9 @@ spring.mail.password=${MAIL_SERVICE_PASSWORD} spring.mail.properties.mail.smtp.auth=${MAIL_SERVICE_SMTP} spring.mail.properties.mail.starttls.enable=${MAIL_SERVICE_STARTTLS} spring.mail.properties.domain_name=${MAIL_SERVICE_DOMAIN_NAME} + +# --- Oauth 2.0 Configurations --- +spring.security.oauth2.client.registration.google.client-name=google +spring.security.oauth2.client.registration.google.client-id=${YOUR_CLIENT_ID} +spring.security.oauth2.client.registration.google.client-secret=${YOUR_SECRET_KEY} +spring.security.oauth2.client.registration.google.scope=email,profile \ No newline at end of file