From 4b238f31dc8050844180f1932cc54af3afdcca51 Mon Sep 17 00:00:00 2001 From: tachtwitch Date: Fri, 28 Feb 2025 14:05:01 +0100 Subject: [PATCH 1/6] Fix(JPA): Invalid repository configuration in newsletterRepository Interface --- Contributors.md | 2 + .../newsletter/entity/Newsletter.java | 4 + .../repository/NewsletterRepository.java | 18 +++- .../newsletter/service/NewsletterService.java | 15 ++++ .../V49__alter_newsletter_product_table.sql | 3 + .../newsletter/unit_test/NewsletterTest.java | 84 +++++++++++++++++++ 6 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 Contributors.md create mode 100644 src/main/resources/db/migration/V49__alter_newsletter_product_table.sql create mode 100644 src/test/java/hng_java_boilerplate/newsletter/unit_test/NewsletterTest.java diff --git a/Contributors.md b/Contributors.md new file mode 100644 index 000000000..f907da08c --- /dev/null +++ b/Contributors.md @@ -0,0 +1,2 @@ + +Tachtwitch diff --git a/src/main/java/hng_java_boilerplate/newsletter/entity/Newsletter.java b/src/main/java/hng_java_boilerplate/newsletter/entity/Newsletter.java index 6fb9f98d1..17eaaa755 100644 --- a/src/main/java/hng_java_boilerplate/newsletter/entity/Newsletter.java +++ b/src/main/java/hng_java_boilerplate/newsletter/entity/Newsletter.java @@ -18,6 +18,10 @@ public class Newsletter { private String id; @Column(nullable = false) private String userId; + @Column(nullable = false) + private String title; + @Column(nullable = false) + private String content; @CreationTimestamp @Column(nullable = false) private LocalDateTime createdAt; diff --git a/src/main/java/hng_java_boilerplate/newsletter/repository/NewsletterRepository.java b/src/main/java/hng_java_boilerplate/newsletter/repository/NewsletterRepository.java index f2468f39f..40c75037c 100644 --- a/src/main/java/hng_java_boilerplate/newsletter/repository/NewsletterRepository.java +++ b/src/main/java/hng_java_boilerplate/newsletter/repository/NewsletterRepository.java @@ -2,6 +2,22 @@ import hng_java_boilerplate.newsletter.entity.Newsletter; 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 java.time.LocalDateTime; +import java.util.List; public interface NewsletterRepository extends JpaRepository { -} + + @Query("SELECT n FROM Newsletter n WHERE n.userId = :userId") + List findNewsletterByUserId(@Param("userId") String userId); + + @Query("SELECT n FROM Newsletter n WHERE n.createdAt > :date") + List findNewsletterByCreatedAtAfter(@Param("date") LocalDateTime date); + + @Modifying + @Query("DELETE FROM Newsletter n WHERE n.userId = :userId") + void deleteNewsletterByUserId(@Param("userId") String userId); +} \ No newline at end of file diff --git a/src/main/java/hng_java_boilerplate/newsletter/service/NewsletterService.java b/src/main/java/hng_java_boilerplate/newsletter/service/NewsletterService.java index 889fcbce9..33988bc16 100644 --- a/src/main/java/hng_java_boilerplate/newsletter/service/NewsletterService.java +++ b/src/main/java/hng_java_boilerplate/newsletter/service/NewsletterService.java @@ -5,6 +5,7 @@ import hng_java_boilerplate.newsletter.dto.SubscribeResponse; import hng_java_boilerplate.newsletter.entity.Newsletter; import hng_java_boilerplate.newsletter.repository.NewsletterRepository; +import hng_java_boilerplate.user.dto.response.Response; import hng_java_boilerplate.user.entity.User; import hng_java_boilerplate.user.repository.UserRepository; import hng_java_boilerplate.user.serviceImpl.EmailServiceImpl; @@ -12,6 +13,7 @@ import org.springframework.stereotype.Service; import java.time.LocalDateTime; +import java.util.List; @Service @RequiredArgsConstructor @@ -34,4 +36,17 @@ public SubscribeResponse subscribeToNewsletter(SubscribeRequest request) { return new SubscribeResponse(201, "subscription successful"); } + + public List findNewsletterByUserId(String userId){ + return newsletterRepository.findNewsletterByUserId(userId); + } + + public List findNewsletterByCreatedAtAfter(LocalDateTime date){ + return newsletterRepository.findNewsletterByCreatedAtAfter(date); + } + + public Response deleteNewsletterByUserId(String userId){ + newsletterRepository.deleteNewsletterByUserId(userId); + return Response.builder().status_code("success").message("Newsletter deleted successfully.").build(); + } } diff --git a/src/main/resources/db/migration/V49__alter_newsletter_product_table.sql b/src/main/resources/db/migration/V49__alter_newsletter_product_table.sql new file mode 100644 index 000000000..814f85feb --- /dev/null +++ b/src/main/resources/db/migration/V49__alter_newsletter_product_table.sql @@ -0,0 +1,3 @@ +ALTER TABLE newsletters +ADD COLUMN title VARCHAR(255), +ADD COLUMN content TEXT \ No newline at end of file diff --git a/src/test/java/hng_java_boilerplate/newsletter/unit_test/NewsletterTest.java b/src/test/java/hng_java_boilerplate/newsletter/unit_test/NewsletterTest.java new file mode 100644 index 000000000..5fb002b1a --- /dev/null +++ b/src/test/java/hng_java_boilerplate/newsletter/unit_test/NewsletterTest.java @@ -0,0 +1,84 @@ +package hng_java_boilerplate.newsletter.unit_test; + +import hng_java_boilerplate.newsletter.entity.Newsletter; +import hng_java_boilerplate.newsletter.repository.NewsletterRepository; +import hng_java_boilerplate.newsletter.service.NewsletterService; +import hng_java_boilerplate.user.dto.response.Response; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.Mockito.*; + +public class NewsletterTest { + + @InjectMocks + private NewsletterService newsletterService; + @Mock + private NewsletterRepository newsletterRepository; + + private Newsletter newsletter1; + private Newsletter newsletter2; + + @BeforeEach + void setup() { + MockitoAnnotations.openMocks(this); + newsletter1 = new Newsletter(); + newsletter1.setUserId("U1"); + newsletter1.setCreatedAt(LocalDateTime.now()); + newsletter1.setId("1"); + newsletter1.setTitle("Newsletter test"); + newsletter1.setUpdatedAt(LocalDateTime.now()); + newsletter1.setContent("this a test content for the newsletter"); + + newsletter2 = new Newsletter(); + newsletter2.setUserId("U1"); + newsletter2.setCreatedAt(LocalDateTime.now()); + newsletter2.setId("2"); + newsletter2.setUpdatedAt(LocalDateTime.now()); + newsletter2.setTitle("Newsletter test2"); + newsletter2.setContent("this a second test content for the newsletter"); + } + + @Test + void testFindByUserId(){ + + List newsletters = Arrays.asList(newsletter1,newsletter2); + + when(newsletterRepository.findNewsletterByUserId("U1")).thenReturn(newsletters); + + List result = newsletterService.findNewsletterByUserId(newsletter1.getUserId()); + + assertNotNull(result); + assertEquals(newsletter1.getUserId(),result.get(0).getUserId()); + verify(newsletterRepository, times(1)).findNewsletterByUserId(newsletter1.getUserId()); + } + + @Test + void testFindByCreatedAfter(){ + LocalDateTime date = LocalDateTime.parse("2025-02-28T11:44:32.180026100"); + List result = newsletterService.findNewsletterByCreatedAtAfter(date); + + assertNotNull(result); + verify(newsletterRepository, times(1)).findNewsletterByCreatedAtAfter(date); + } + + @Test + void testDeleteByUserId(){ + String userId = newsletter1.getUserId(); + + Response response = newsletterService.deleteNewsletterByUserId(userId); + + assertEquals("success", response.getStatus_code()); + assertEquals("Newsletter deleted successfully.", response.getMessage()); + verify(newsletterRepository, times(1)).deleteNewsletterByUserId(userId); + } +} From 1cf1e0e1f61b87fc487045ca6e97d995e8931b9b Mon Sep 17 00:00:00 2001 From: tachtwitch Date: Fri, 28 Feb 2025 18:09:17 +0100 Subject: [PATCH 2/6] Fix: Incorrect newsletter entity relations and added newsletterRepository Interface endpoints --- .../controller/NewsletterController.java | 29 +++++++++++++--- .../newsletter/dto/DeleteRequest.java | 12 +++++++ .../newsletter/entity/Newsletter.java | 20 ++++++++--- .../newsletter/entity/Subscriber.java | 34 +++++++++++++++++++ .../repository/NewsletterRepository.java | 8 ++--- .../repository/SubscriberRepository.java | 7 ++++ .../newsletter/service/NewsletterService.java | 7 ++-- .../user/entity/User.java | 6 ++++ .../newsletter/unit_test/NewsletterTest.java | 23 ++++++++----- 9 files changed, 121 insertions(+), 25 deletions(-) create mode 100644 src/main/java/hng_java_boilerplate/newsletter/dto/DeleteRequest.java create mode 100644 src/main/java/hng_java_boilerplate/newsletter/entity/Subscriber.java create mode 100644 src/main/java/hng_java_boilerplate/newsletter/repository/SubscriberRepository.java diff --git a/src/main/java/hng_java_boilerplate/newsletter/controller/NewsletterController.java b/src/main/java/hng_java_boilerplate/newsletter/controller/NewsletterController.java index dc791ccff..9d85d0cc6 100644 --- a/src/main/java/hng_java_boilerplate/newsletter/controller/NewsletterController.java +++ b/src/main/java/hng_java_boilerplate/newsletter/controller/NewsletterController.java @@ -1,16 +1,19 @@ package hng_java_boilerplate.newsletter.controller; +import hng_java_boilerplate.newsletter.dto.DeleteRequest; import hng_java_boilerplate.newsletter.dto.SubscribeRequest; import hng_java_boilerplate.newsletter.dto.SubscribeResponse; import hng_java_boilerplate.newsletter.service.NewsletterService; +import hng_java_boilerplate.user.dto.response.Response; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; 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.security.access.prepost.PreAuthorize; +import org.springframework.security.core.Authentication; +import org.springframework.web.bind.annotation.*; + +import java.time.LocalDateTime; @RestController @RequiredArgsConstructor @@ -23,4 +26,22 @@ public ResponseEntity subscribe(@RequestBody @Valid Subscribe return ResponseEntity.status(HttpStatus.CREATED) .body(newsletterService.subscribeToNewsletter(request)); } + + @GetMapping("/{userId}") + public ResponseEntity getNewslettersByUserId(@PathVariable String userId) { + return ResponseEntity.ok(newsletterService.findNewsletterByUserId(userId)); + } + + @GetMapping("/{date}") + public ResponseEntity getNewsletterAfterDate(@PathVariable LocalDateTime date) { + return ResponseEntity.ok(newsletterService.findNewsletterByCreatedAtAfter(date)); + } + + @PreAuthorize("hasRole('ROLE_SUPER_ADMIN')") + @DeleteMapping("/delete") + public ResponseEntity deleteNewsletterById(@Valid @RequestBody DeleteRequest request) { + String user_id = request.getUser_id(); + Response response = newsletterService.deleteNewsletterByUserId(user_id); + return ResponseEntity.status(HttpStatus.OK).body(response); + } } diff --git a/src/main/java/hng_java_boilerplate/newsletter/dto/DeleteRequest.java b/src/main/java/hng_java_boilerplate/newsletter/dto/DeleteRequest.java new file mode 100644 index 000000000..c5c1dc549 --- /dev/null +++ b/src/main/java/hng_java_boilerplate/newsletter/dto/DeleteRequest.java @@ -0,0 +1,12 @@ +package hng_java_boilerplate.newsletter.dto; + +import jakarta.validation.constraints.NotBlank; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class DeleteRequest { + @NotBlank(message = "user_id is required") + private String user_id; +} diff --git a/src/main/java/hng_java_boilerplate/newsletter/entity/Newsletter.java b/src/main/java/hng_java_boilerplate/newsletter/entity/Newsletter.java index 17eaaa755..2c4fb7300 100644 --- a/src/main/java/hng_java_boilerplate/newsletter/entity/Newsletter.java +++ b/src/main/java/hng_java_boilerplate/newsletter/entity/Newsletter.java @@ -1,5 +1,6 @@ package hng_java_boilerplate.newsletter.entity; +import hng_java_boilerplate.user.entity.User; import jakarta.persistence.*; import lombok.Getter; import lombok.Setter; @@ -7,25 +8,36 @@ import org.hibernate.annotations.UpdateTimestamp; import java.time.LocalDateTime; +import java.util.List; @Getter @Setter @Entity @Table(name = "newsletters") public class Newsletter { + @Id @GeneratedValue(strategy = GenerationType.UUID) private String id; - @Column(nullable = false) - private String userId; + + @ManyToOne + @JoinColumn(name = "user_id") + private User user; + @Column(nullable = false) private String title; + @Column(nullable = false) private String content; + + @OneToMany(mappedBy = "newsletter",cascade = CascadeType.ALL) + private List subscribers; + @CreationTimestamp - @Column(nullable = false) + @Column(name = "created_at",nullable = false) private LocalDateTime createdAt; - @Column + + @Column(name = "updated_at") @UpdateTimestamp private LocalDateTime updatedAt; } diff --git a/src/main/java/hng_java_boilerplate/newsletter/entity/Subscriber.java b/src/main/java/hng_java_boilerplate/newsletter/entity/Subscriber.java new file mode 100644 index 000000000..2d8f98498 --- /dev/null +++ b/src/main/java/hng_java_boilerplate/newsletter/entity/Subscriber.java @@ -0,0 +1,34 @@ +package hng_java_boilerplate.newsletter.entity; + +import hng_java_boilerplate.user.entity.User; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.CreationTimestamp; + +import java.time.LocalDateTime; + +@Data +@Entity +@NoArgsConstructor +@AllArgsConstructor +@Table(name = "subscribers") +public class Subscriber { + + @Id + @GeneratedValue(strategy = GenerationType.UUID) + private String id; + + @ManyToOne + @JoinColumn(name = "user_id") + private User user; + + @ManyToOne + @JoinColumn(name = "newsletter_id") + private Newsletter newsletter; + + @Column(name = "created_at", updatable = false) + @CreationTimestamp + private LocalDateTime subscribedAt; +} diff --git a/src/main/java/hng_java_boilerplate/newsletter/repository/NewsletterRepository.java b/src/main/java/hng_java_boilerplate/newsletter/repository/NewsletterRepository.java index 40c75037c..53ffc9c2a 100644 --- a/src/main/java/hng_java_boilerplate/newsletter/repository/NewsletterRepository.java +++ b/src/main/java/hng_java_boilerplate/newsletter/repository/NewsletterRepository.java @@ -2,7 +2,6 @@ import hng_java_boilerplate.newsletter.entity.Newsletter; 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; @@ -11,13 +10,10 @@ public interface NewsletterRepository extends JpaRepository { - @Query("SELECT n FROM Newsletter n WHERE n.userId = :userId") - List findNewsletterByUserId(@Param("userId") String userId); + List findByUser_Id(String userId); @Query("SELECT n FROM Newsletter n WHERE n.createdAt > :date") List findNewsletterByCreatedAtAfter(@Param("date") LocalDateTime date); - @Modifying - @Query("DELETE FROM Newsletter n WHERE n.userId = :userId") - void deleteNewsletterByUserId(@Param("userId") String userId); + void deleteByUser_Id(String userId); } \ No newline at end of file diff --git a/src/main/java/hng_java_boilerplate/newsletter/repository/SubscriberRepository.java b/src/main/java/hng_java_boilerplate/newsletter/repository/SubscriberRepository.java new file mode 100644 index 000000000..cf3e532ed --- /dev/null +++ b/src/main/java/hng_java_boilerplate/newsletter/repository/SubscriberRepository.java @@ -0,0 +1,7 @@ +package hng_java_boilerplate.newsletter.repository; + +import hng_java_boilerplate.newsletter.entity.Subscriber; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface SubscriberRepository extends JpaRepository { +} diff --git a/src/main/java/hng_java_boilerplate/newsletter/service/NewsletterService.java b/src/main/java/hng_java_boilerplate/newsletter/service/NewsletterService.java index 33988bc16..3896fe680 100644 --- a/src/main/java/hng_java_boilerplate/newsletter/service/NewsletterService.java +++ b/src/main/java/hng_java_boilerplate/newsletter/service/NewsletterService.java @@ -10,6 +10,7 @@ import hng_java_boilerplate.user.repository.UserRepository; import hng_java_boilerplate.user.serviceImpl.EmailServiceImpl; import lombok.RequiredArgsConstructor; +import org.springframework.security.core.Authentication; import org.springframework.stereotype.Service; import java.time.LocalDateTime; @@ -27,7 +28,7 @@ public SubscribeResponse subscribeToNewsletter(SubscribeRequest request) { .orElseThrow(() -> new NotFoundException("user not found with email")); Newsletter newsletter = new Newsletter(); - newsletter.setUserId(user.getId()); + newsletter.setUser(user); newsletter.setCreatedAt(LocalDateTime.now()); newsletter.setUpdatedAt(LocalDateTime.now()); newsletterRepository.saveAndFlush(newsletter); @@ -38,7 +39,7 @@ public SubscribeResponse subscribeToNewsletter(SubscribeRequest request) { } public List findNewsletterByUserId(String userId){ - return newsletterRepository.findNewsletterByUserId(userId); + return newsletterRepository.findByUser_Id(userId); } public List findNewsletterByCreatedAtAfter(LocalDateTime date){ @@ -46,7 +47,7 @@ public List findNewsletterByCreatedAtAfter(LocalDateTime date){ } public Response deleteNewsletterByUserId(String userId){ - newsletterRepository.deleteNewsletterByUserId(userId); + newsletterRepository.deleteByUser_Id(userId); return Response.builder().status_code("success").message("Newsletter deleted successfully.").build(); } } diff --git a/src/main/java/hng_java_boilerplate/user/entity/User.java b/src/main/java/hng_java_boilerplate/user/entity/User.java index 79fb26215..8f00ab7e1 100644 --- a/src/main/java/hng_java_boilerplate/user/entity/User.java +++ b/src/main/java/hng_java_boilerplate/user/entity/User.java @@ -1,6 +1,8 @@ package hng_java_boilerplate.user.entity; import com.fasterxml.jackson.annotation.JsonIgnore; +import hng_java_boilerplate.newsletter.entity.Newsletter; +import hng_java_boilerplate.newsletter.entity.Subscriber; import hng_java_boilerplate.organisation.entity.Organisation; import hng_java_boilerplate.plans.entity.Plan; import hng_java_boilerplate.product.entity.Product; @@ -122,4 +124,8 @@ public boolean isCredentialsNonExpired() { public boolean isEnabled() { return this.isEnabled; } + + @OneToMany(mappedBy = "user",cascade = CascadeType.ALL) + private List newsletters; + } diff --git a/src/test/java/hng_java_boilerplate/newsletter/unit_test/NewsletterTest.java b/src/test/java/hng_java_boilerplate/newsletter/unit_test/NewsletterTest.java index 5fb002b1a..4f0e5a84e 100644 --- a/src/test/java/hng_java_boilerplate/newsletter/unit_test/NewsletterTest.java +++ b/src/test/java/hng_java_boilerplate/newsletter/unit_test/NewsletterTest.java @@ -4,6 +4,7 @@ import hng_java_boilerplate.newsletter.repository.NewsletterRepository; import hng_java_boilerplate.newsletter.service.NewsletterService; import hng_java_boilerplate.user.dto.response.Response; +import hng_java_boilerplate.user.entity.User; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; @@ -31,8 +32,14 @@ public class NewsletterTest { @BeforeEach void setup() { MockitoAnnotations.openMocks(this); + User user = new User(); + user.setId("U1"); + user.setName("John Doe"); + user.setEmail("johndoe@example.com"); + user.setCreatedAt(LocalDateTime.now()); + newsletter1 = new Newsletter(); - newsletter1.setUserId("U1"); + newsletter1.setUser(user); newsletter1.setCreatedAt(LocalDateTime.now()); newsletter1.setId("1"); newsletter1.setTitle("Newsletter test"); @@ -40,7 +47,7 @@ void setup() { newsletter1.setContent("this a test content for the newsletter"); newsletter2 = new Newsletter(); - newsletter2.setUserId("U1"); + newsletter1.setUser(user); newsletter2.setCreatedAt(LocalDateTime.now()); newsletter2.setId("2"); newsletter2.setUpdatedAt(LocalDateTime.now()); @@ -53,13 +60,13 @@ void testFindByUserId(){ List newsletters = Arrays.asList(newsletter1,newsletter2); - when(newsletterRepository.findNewsletterByUserId("U1")).thenReturn(newsletters); + when(newsletterRepository.findByUser_Id("U1")).thenReturn(newsletters); - List result = newsletterService.findNewsletterByUserId(newsletter1.getUserId()); + List result = newsletterService.findNewsletterByUserId(newsletter1.getUser().getId()); assertNotNull(result); - assertEquals(newsletter1.getUserId(),result.get(0).getUserId()); - verify(newsletterRepository, times(1)).findNewsletterByUserId(newsletter1.getUserId()); + assertEquals(newsletter1.getUser().getId(),result.get(0).getUser().getId()); + verify(newsletterRepository, times(1)).findByUser_Id(newsletter1.getUser().getId()); } @Test @@ -73,12 +80,12 @@ void testFindByCreatedAfter(){ @Test void testDeleteByUserId(){ - String userId = newsletter1.getUserId(); + String userId = newsletter1.getUser().getId(); Response response = newsletterService.deleteNewsletterByUserId(userId); assertEquals("success", response.getStatus_code()); assertEquals("Newsletter deleted successfully.", response.getMessage()); - verify(newsletterRepository, times(1)).deleteNewsletterByUserId(userId); + verify(newsletterRepository, times(1)).deleteByUser_Id(userId); } } From 7152dc83f9efd30520970122265a892026dcf6b9 Mon Sep 17 00:00:00 2001 From: tachtwitch Date: Sat, 1 Mar 2025 02:01:35 +0100 Subject: [PATCH 3/6] Fix: Incorrect newsletter entity relations and added newsletterRepository Interface endpoints with pagination --- .../controller/NewsletterController.java | 27 +++++++++------ .../newsletter/dto/DeleteRequest.java | 2 -- .../newsletter/entity/Newsletter.java | 4 --- .../newsletter/entity/Subscriber.java | 34 ------------------- .../repository/NewsletterRepository.java | 8 +++-- .../repository/SubscriberRepository.java | 7 ---- .../newsletter/service/NewsletterService.java | 13 ++++--- .../user/entity/User.java | 11 ++++-- .../migration/V50__alter_newsletter_table.sql | 2 ++ .../V51__create_subscription_table.sql | 15 ++++++++ .../newsletter/unit_test/NewsletterTest.java | 24 +++++++++---- 11 files changed, 73 insertions(+), 74 deletions(-) delete mode 100644 src/main/java/hng_java_boilerplate/newsletter/entity/Subscriber.java delete mode 100644 src/main/java/hng_java_boilerplate/newsletter/repository/SubscriberRepository.java create mode 100644 src/main/resources/db/migration/V50__alter_newsletter_table.sql create mode 100644 src/main/resources/db/migration/V51__create_subscription_table.sql diff --git a/src/main/java/hng_java_boilerplate/newsletter/controller/NewsletterController.java b/src/main/java/hng_java_boilerplate/newsletter/controller/NewsletterController.java index 9d85d0cc6..f0022c38e 100644 --- a/src/main/java/hng_java_boilerplate/newsletter/controller/NewsletterController.java +++ b/src/main/java/hng_java_boilerplate/newsletter/controller/NewsletterController.java @@ -3,21 +3,22 @@ import hng_java_boilerplate.newsletter.dto.DeleteRequest; import hng_java_boilerplate.newsletter.dto.SubscribeRequest; import hng_java_boilerplate.newsletter.dto.SubscribeResponse; +import hng_java_boilerplate.newsletter.entity.Newsletter; import hng_java_boilerplate.newsletter.service.NewsletterService; import hng_java_boilerplate.user.dto.response.Response; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.*; import java.time.LocalDateTime; @RestController @RequiredArgsConstructor -@RequestMapping("/api/v1/newsletter-subscription") +@RequestMapping("/api/v1/newsletter") public class NewsletterController { private final NewsletterService newsletterService; @@ -27,21 +28,25 @@ public ResponseEntity subscribe(@RequestBody @Valid Subscribe .body(newsletterService.subscribeToNewsletter(request)); } - @GetMapping("/{userId}") - public ResponseEntity getNewslettersByUserId(@PathVariable String userId) { - return ResponseEntity.ok(newsletterService.findNewsletterByUserId(userId)); + @GetMapping("/{userId}/{pageNumber}/{pageSize}") + public ResponseEntity> getNewslettersByUserId(@PathVariable String userId,@PathVariable int pageNumber,@PathVariable int pageSize) { + return ResponseEntity.ok(newsletterService.findNewsletterByUserId(userId,pageNumber,pageSize)); } - @GetMapping("/{date}") - public ResponseEntity getNewsletterAfterDate(@PathVariable LocalDateTime date) { - return ResponseEntity.ok(newsletterService.findNewsletterByCreatedAtAfter(date)); + @GetMapping("/{date}/{pageNumber}/{pageSize}") + public ResponseEntity> getNewsletterAfterDate(@PathVariable LocalDateTime date, @PathVariable int pageNumber,@PathVariable int pageSize) { + return ResponseEntity.ok(newsletterService.findNewsletterByCreatedAtAfter(date,pageNumber,pageSize)); } @PreAuthorize("hasRole('ROLE_SUPER_ADMIN')") - @DeleteMapping("/delete") + @DeleteMapping("/{userId}") public ResponseEntity deleteNewsletterById(@Valid @RequestBody DeleteRequest request) { String user_id = request.getUser_id(); - Response response = newsletterService.deleteNewsletterByUserId(user_id); - return ResponseEntity.status(HttpStatus.OK).body(response); + if(user_id.isEmpty()){ + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + }else { + Response response = newsletterService.deleteNewsletterByUserId(user_id); + return ResponseEntity.status(HttpStatus.OK).body(response); + } } } diff --git a/src/main/java/hng_java_boilerplate/newsletter/dto/DeleteRequest.java b/src/main/java/hng_java_boilerplate/newsletter/dto/DeleteRequest.java index c5c1dc549..cba800327 100644 --- a/src/main/java/hng_java_boilerplate/newsletter/dto/DeleteRequest.java +++ b/src/main/java/hng_java_boilerplate/newsletter/dto/DeleteRequest.java @@ -1,12 +1,10 @@ package hng_java_boilerplate.newsletter.dto; -import jakarta.validation.constraints.NotBlank; import lombok.Getter; import lombok.Setter; @Getter @Setter public class DeleteRequest { - @NotBlank(message = "user_id is required") private String user_id; } diff --git a/src/main/java/hng_java_boilerplate/newsletter/entity/Newsletter.java b/src/main/java/hng_java_boilerplate/newsletter/entity/Newsletter.java index 2c4fb7300..3a6476db4 100644 --- a/src/main/java/hng_java_boilerplate/newsletter/entity/Newsletter.java +++ b/src/main/java/hng_java_boilerplate/newsletter/entity/Newsletter.java @@ -8,7 +8,6 @@ import org.hibernate.annotations.UpdateTimestamp; import java.time.LocalDateTime; -import java.util.List; @Getter @Setter @@ -30,9 +29,6 @@ public class Newsletter { @Column(nullable = false) private String content; - @OneToMany(mappedBy = "newsletter",cascade = CascadeType.ALL) - private List subscribers; - @CreationTimestamp @Column(name = "created_at",nullable = false) private LocalDateTime createdAt; diff --git a/src/main/java/hng_java_boilerplate/newsletter/entity/Subscriber.java b/src/main/java/hng_java_boilerplate/newsletter/entity/Subscriber.java deleted file mode 100644 index 2d8f98498..000000000 --- a/src/main/java/hng_java_boilerplate/newsletter/entity/Subscriber.java +++ /dev/null @@ -1,34 +0,0 @@ -package hng_java_boilerplate.newsletter.entity; - -import hng_java_boilerplate.user.entity.User; -import jakarta.persistence.*; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -import org.hibernate.annotations.CreationTimestamp; - -import java.time.LocalDateTime; - -@Data -@Entity -@NoArgsConstructor -@AllArgsConstructor -@Table(name = "subscribers") -public class Subscriber { - - @Id - @GeneratedValue(strategy = GenerationType.UUID) - private String id; - - @ManyToOne - @JoinColumn(name = "user_id") - private User user; - - @ManyToOne - @JoinColumn(name = "newsletter_id") - private Newsletter newsletter; - - @Column(name = "created_at", updatable = false) - @CreationTimestamp - private LocalDateTime subscribedAt; -} diff --git a/src/main/java/hng_java_boilerplate/newsletter/repository/NewsletterRepository.java b/src/main/java/hng_java_boilerplate/newsletter/repository/NewsletterRepository.java index 53ffc9c2a..879628cab 100644 --- a/src/main/java/hng_java_boilerplate/newsletter/repository/NewsletterRepository.java +++ b/src/main/java/hng_java_boilerplate/newsletter/repository/NewsletterRepository.java @@ -1,19 +1,21 @@ package hng_java_boilerplate.newsletter.repository; + import hng_java_boilerplate.newsletter.entity.Newsletter; +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.Query; import org.springframework.data.repository.query.Param; import java.time.LocalDateTime; -import java.util.List; public interface NewsletterRepository extends JpaRepository { - List findByUser_Id(String userId); + Page findByUser_Id(String userId, Pageable page); @Query("SELECT n FROM Newsletter n WHERE n.createdAt > :date") - List findNewsletterByCreatedAtAfter(@Param("date") LocalDateTime date); + Page findNewsletterByCreatedAtAfter(@Param("date") LocalDateTime date, Pageable page); void deleteByUser_Id(String userId); } \ No newline at end of file diff --git a/src/main/java/hng_java_boilerplate/newsletter/repository/SubscriberRepository.java b/src/main/java/hng_java_boilerplate/newsletter/repository/SubscriberRepository.java deleted file mode 100644 index cf3e532ed..000000000 --- a/src/main/java/hng_java_boilerplate/newsletter/repository/SubscriberRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package hng_java_boilerplate.newsletter.repository; - -import hng_java_boilerplate.newsletter.entity.Subscriber; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface SubscriberRepository extends JpaRepository { -} diff --git a/src/main/java/hng_java_boilerplate/newsletter/service/NewsletterService.java b/src/main/java/hng_java_boilerplate/newsletter/service/NewsletterService.java index 3896fe680..b0549aa53 100644 --- a/src/main/java/hng_java_boilerplate/newsletter/service/NewsletterService.java +++ b/src/main/java/hng_java_boilerplate/newsletter/service/NewsletterService.java @@ -10,6 +10,9 @@ import hng_java_boilerplate.user.repository.UserRepository; import hng_java_boilerplate.user.serviceImpl.EmailServiceImpl; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.security.core.Authentication; import org.springframework.stereotype.Service; @@ -38,12 +41,14 @@ public SubscribeResponse subscribeToNewsletter(SubscribeRequest request) { return new SubscribeResponse(201, "subscription successful"); } - public List findNewsletterByUserId(String userId){ - return newsletterRepository.findByUser_Id(userId); + public Page findNewsletterByUserId(String userId, int pageNumber, int pageSize){ + Pageable pageable = PageRequest.of(pageNumber, pageSize); + return newsletterRepository.findByUser_Id(userId,pageable); } - public List findNewsletterByCreatedAtAfter(LocalDateTime date){ - return newsletterRepository.findNewsletterByCreatedAtAfter(date); + public Page findNewsletterByCreatedAtAfter(LocalDateTime date, int pageNumber, int pageSize){ + Pageable pageable = PageRequest.of(pageNumber, pageSize); + return newsletterRepository.findNewsletterByCreatedAtAfter(date,pageable); } public Response deleteNewsletterByUserId(String userId){ diff --git a/src/main/java/hng_java_boilerplate/user/entity/User.java b/src/main/java/hng_java_boilerplate/user/entity/User.java index 8f00ab7e1..3a6808004 100644 --- a/src/main/java/hng_java_boilerplate/user/entity/User.java +++ b/src/main/java/hng_java_boilerplate/user/entity/User.java @@ -2,7 +2,6 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import hng_java_boilerplate.newsletter.entity.Newsletter; -import hng_java_boilerplate.newsletter.entity.Subscriber; import hng_java_boilerplate.organisation.entity.Organisation; import hng_java_boilerplate.plans.entity.Plan; import hng_java_boilerplate.product.entity.Product; @@ -125,7 +124,15 @@ public boolean isEnabled() { return this.isEnabled; } + @OneToMany(mappedBy = "user",cascade = CascadeType.ALL) - private List newsletters; + private List myNewsletters; + @ManyToMany + @JoinTable( + name = "subscribers", + joinColumns = @JoinColumn(name = "user_id"), + inverseJoinColumns = @JoinColumn(name = "newsletter_id") + ) + private List newsletters; } diff --git a/src/main/resources/db/migration/V50__alter_newsletter_table.sql b/src/main/resources/db/migration/V50__alter_newsletter_table.sql new file mode 100644 index 000000000..6a6615d55 --- /dev/null +++ b/src/main/resources/db/migration/V50__alter_newsletter_table.sql @@ -0,0 +1,2 @@ +ALTER TABLE newsletters +DROP COLUMN user_id diff --git a/src/main/resources/db/migration/V51__create_subscription_table.sql b/src/main/resources/db/migration/V51__create_subscription_table.sql new file mode 100644 index 000000000..37c5ab95c --- /dev/null +++ b/src/main/resources/db/migration/V51__create_subscription_table.sql @@ -0,0 +1,15 @@ +ALTER TABLE newsletters +ADD COLUMN user_id VARCHAR(50), +ADD CONSTRAINT fk_user_id +FOREIGN KEY (user_id) +REFERENCES users(id) +ON DELETE CASCADE +ON UPDATE CASCADE; + +CREATE TABLE subscribers( + user_id VARCHAR(50), + newsletter_id VARCHAR(50), + PRIMARY KEY (user_id, newsletter_id), + FOREIGN KEY (user_id) REFERENCES users(id), + FOREIGN KEY (newsletter_id) REFERENCES newsletters(id), + subscribed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP); \ No newline at end of file diff --git a/src/test/java/hng_java_boilerplate/newsletter/unit_test/NewsletterTest.java b/src/test/java/hng_java_boilerplate/newsletter/unit_test/NewsletterTest.java index 4f0e5a84e..6818d64ff 100644 --- a/src/test/java/hng_java_boilerplate/newsletter/unit_test/NewsletterTest.java +++ b/src/test/java/hng_java_boilerplate/newsletter/unit_test/NewsletterTest.java @@ -10,6 +10,10 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import java.time.LocalDateTime; import java.util.Arrays; @@ -57,25 +61,31 @@ void setup() { @Test void testFindByUserId(){ - List newsletters = Arrays.asList(newsletter1,newsletter2); + Page page = new PageImpl<>(newsletters); + Pageable pageable = PageRequest.of(0,1); - when(newsletterRepository.findByUser_Id("U1")).thenReturn(newsletters); + when(newsletterRepository.findByUser_Id("U1",pageable)).thenReturn(page); - List result = newsletterService.findNewsletterByUserId(newsletter1.getUser().getId()); + Page result = newsletterService.findNewsletterByUserId(newsletter1.getUser().getId(),pageable.getPageNumber(),pageable.getPageSize()); assertNotNull(result); - assertEquals(newsletter1.getUser().getId(),result.get(0).getUser().getId()); - verify(newsletterRepository, times(1)).findByUser_Id(newsletter1.getUser().getId()); + assertEquals(1,result.getTotalPages()); + System.out.println(page); + verify(newsletterRepository, times(1)).findByUser_Id(newsletter1.getUser().getId(),pageable); } @Test void testFindByCreatedAfter(){ + List newsletters = Arrays.asList(newsletter1,newsletter2); + Page page = new PageImpl<>(newsletters,PageRequest.of(0,1),2); LocalDateTime date = LocalDateTime.parse("2025-02-28T11:44:32.180026100"); - List result = newsletterService.findNewsletterByCreatedAtAfter(date); + when(newsletterRepository.findNewsletterByCreatedAtAfter(date,page.getPageable())).thenReturn(page); + + Page result = newsletterService.findNewsletterByCreatedAtAfter(date,0,1); assertNotNull(result); - verify(newsletterRepository, times(1)).findNewsletterByCreatedAtAfter(date); + verify(newsletterRepository, times(1)).findNewsletterByCreatedAtAfter(date,page.getPageable()); } @Test From 9335765fa035080fe39485300060b0bf1910176d Mon Sep 17 00:00:00 2001 From: tachtwitch Date: Sat, 1 Mar 2025 09:43:06 +0100 Subject: [PATCH 4/6] Fix: Incorrect newsletter entity relations and added newsletterRepository Interface endpoints with pagination #2 --- .../controller/NewsletterController.java | 20 +++++++++++-------- .../newsletter/service/NewsletterService.java | 9 ++------- .../newsletter/unit_test/NewsletterTest.java | 5 ++--- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src/main/java/hng_java_boilerplate/newsletter/controller/NewsletterController.java b/src/main/java/hng_java_boilerplate/newsletter/controller/NewsletterController.java index f0022c38e..d5771800e 100644 --- a/src/main/java/hng_java_boilerplate/newsletter/controller/NewsletterController.java +++ b/src/main/java/hng_java_boilerplate/newsletter/controller/NewsletterController.java @@ -1,5 +1,6 @@ package hng_java_boilerplate.newsletter.controller; +import hng_java_boilerplate.exception.BadRequestException; import hng_java_boilerplate.newsletter.dto.DeleteRequest; import hng_java_boilerplate.newsletter.dto.SubscribeRequest; import hng_java_boilerplate.newsletter.dto.SubscribeResponse; @@ -9,6 +10,9 @@ import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.web.PageableDefault; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; @@ -28,22 +32,22 @@ public ResponseEntity subscribe(@RequestBody @Valid Subscribe .body(newsletterService.subscribeToNewsletter(request)); } - @GetMapping("/{userId}/{pageNumber}/{pageSize}") - public ResponseEntity> getNewslettersByUserId(@PathVariable String userId,@PathVariable int pageNumber,@PathVariable int pageSize) { - return ResponseEntity.ok(newsletterService.findNewsletterByUserId(userId,pageNumber,pageSize)); + @GetMapping("/{userId}") + public ResponseEntity> getNewslettersByUserId(@PathVariable String userId, @PageableDefault(sort = "user_id", direction = Sort.Direction.DESC)Pageable pageable) { + return ResponseEntity.ok(newsletterService.findNewsletterByUserId(userId,pageable)); } - @GetMapping("/{date}/{pageNumber}/{pageSize}") - public ResponseEntity> getNewsletterAfterDate(@PathVariable LocalDateTime date, @PathVariable int pageNumber,@PathVariable int pageSize) { - return ResponseEntity.ok(newsletterService.findNewsletterByCreatedAtAfter(date,pageNumber,pageSize)); + @GetMapping("/{date}") + public ResponseEntity> getNewslettersAfterDate(@PathVariable LocalDateTime date, @PageableDefault(sort = "created_at",direction = Sort.Direction.DESC)Pageable pageable) { + return ResponseEntity.ok(newsletterService.findNewsletterByCreatedAtAfter(date,pageable)); } @PreAuthorize("hasRole('ROLE_SUPER_ADMIN')") @DeleteMapping("/{userId}") - public ResponseEntity deleteNewsletterById(@Valid @RequestBody DeleteRequest request) { + public ResponseEntity deleteNewslettersById(@Valid @RequestBody DeleteRequest request) { String user_id = request.getUser_id(); if(user_id.isEmpty()){ - return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + throw new BadRequestException("user id is required"); }else { Response response = newsletterService.deleteNewsletterByUserId(user_id); return ResponseEntity.status(HttpStatus.OK).body(response); diff --git a/src/main/java/hng_java_boilerplate/newsletter/service/NewsletterService.java b/src/main/java/hng_java_boilerplate/newsletter/service/NewsletterService.java index b0549aa53..19f7316be 100644 --- a/src/main/java/hng_java_boilerplate/newsletter/service/NewsletterService.java +++ b/src/main/java/hng_java_boilerplate/newsletter/service/NewsletterService.java @@ -11,13 +11,10 @@ import hng_java_boilerplate.user.serviceImpl.EmailServiceImpl; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; -import org.springframework.security.core.Authentication; import org.springframework.stereotype.Service; import java.time.LocalDateTime; -import java.util.List; @Service @RequiredArgsConstructor @@ -41,13 +38,11 @@ public SubscribeResponse subscribeToNewsletter(SubscribeRequest request) { return new SubscribeResponse(201, "subscription successful"); } - public Page findNewsletterByUserId(String userId, int pageNumber, int pageSize){ - Pageable pageable = PageRequest.of(pageNumber, pageSize); + public Page findNewsletterByUserId(String userId, Pageable pageable){ return newsletterRepository.findByUser_Id(userId,pageable); } - public Page findNewsletterByCreatedAtAfter(LocalDateTime date, int pageNumber, int pageSize){ - Pageable pageable = PageRequest.of(pageNumber, pageSize); + public Page findNewsletterByCreatedAtAfter(LocalDateTime date, Pageable pageable){ return newsletterRepository.findNewsletterByCreatedAtAfter(date,pageable); } diff --git a/src/test/java/hng_java_boilerplate/newsletter/unit_test/NewsletterTest.java b/src/test/java/hng_java_boilerplate/newsletter/unit_test/NewsletterTest.java index 6818d64ff..087a0b973 100644 --- a/src/test/java/hng_java_boilerplate/newsletter/unit_test/NewsletterTest.java +++ b/src/test/java/hng_java_boilerplate/newsletter/unit_test/NewsletterTest.java @@ -67,11 +67,10 @@ void testFindByUserId(){ when(newsletterRepository.findByUser_Id("U1",pageable)).thenReturn(page); - Page result = newsletterService.findNewsletterByUserId(newsletter1.getUser().getId(),pageable.getPageNumber(),pageable.getPageSize()); + Page result = newsletterService.findNewsletterByUserId(newsletter1.getUser().getId(),pageable); assertNotNull(result); assertEquals(1,result.getTotalPages()); - System.out.println(page); verify(newsletterRepository, times(1)).findByUser_Id(newsletter1.getUser().getId(),pageable); } @@ -82,7 +81,7 @@ void testFindByCreatedAfter(){ LocalDateTime date = LocalDateTime.parse("2025-02-28T11:44:32.180026100"); when(newsletterRepository.findNewsletterByCreatedAtAfter(date,page.getPageable())).thenReturn(page); - Page result = newsletterService.findNewsletterByCreatedAtAfter(date,0,1); + Page result = newsletterService.findNewsletterByCreatedAtAfter(date,page.getPageable()); assertNotNull(result); verify(newsletterRepository, times(1)).findNewsletterByCreatedAtAfter(date,page.getPageable()); From 739c0f14f5a493fdc0927a07f1c8e433a80e7382 Mon Sep 17 00:00:00 2001 From: tachtwitch <72201236+tachtwitch@users.noreply.github.com> Date: Sun, 2 Mar 2025 16:58:06 +0100 Subject: [PATCH 5/6] Update User.java --- src/main/java/hng_java_boilerplate/user/entity/User.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/hng_java_boilerplate/user/entity/User.java b/src/main/java/hng_java_boilerplate/user/entity/User.java index 3a6808004..2afe620d4 100644 --- a/src/main/java/hng_java_boilerplate/user/entity/User.java +++ b/src/main/java/hng_java_boilerplate/user/entity/User.java @@ -124,7 +124,7 @@ public boolean isEnabled() { return this.isEnabled; } - + @JsonIgnore @OneToMany(mappedBy = "user",cascade = CascadeType.ALL) private List myNewsletters; From 1cd7e1c9a88dab9cb7891e567e687dc3c320bfef Mon Sep 17 00:00:00 2001 From: tachtwitch <72201236+tachtwitch@users.noreply.github.com> Date: Sun, 2 Mar 2025 17:00:17 +0100 Subject: [PATCH 6/6] FIX[BUG]: Fix redudant endpoint bug in newsletterController --- .../newsletter/controller/NewsletterController.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/hng_java_boilerplate/newsletter/controller/NewsletterController.java b/src/main/java/hng_java_boilerplate/newsletter/controller/NewsletterController.java index ba39b28de..8fbcdecb5 100644 --- a/src/main/java/hng_java_boilerplate/newsletter/controller/NewsletterController.java +++ b/src/main/java/hng_java_boilerplate/newsletter/controller/NewsletterController.java @@ -66,18 +66,18 @@ public ResponseEntity subscribe(@RequestBody @Valid Subscribe } - @GetMapping("/{userId}") + @GetMapping("/user/{userId}") public ResponseEntity> getNewslettersByUserId(@PathVariable String userId, @PageableDefault(sort = "user_id", direction = Sort.Direction.DESC)Pageable pageable) { return ResponseEntity.ok(newsletterService.findNewsletterByUserId(userId,pageable)); } - @GetMapping("/{date}") + @GetMapping("/date/{date}") public ResponseEntity> getNewslettersAfterDate(@PathVariable LocalDateTime date, @PageableDefault(sort = "created_at",direction = Sort.Direction.DESC)Pageable pageable) { return ResponseEntity.ok(newsletterService.findNewsletterByCreatedAtAfter(date,pageable)); } @PreAuthorize("hasRole('ROLE_SUPER_ADMIN')") - @DeleteMapping("/{userId}") + @DeleteMapping("/delete/{userId}") public ResponseEntity deleteNewslettersById(@Valid @RequestBody DeleteRequest request) { String user_id = request.getUser_id(); if(user_id.isEmpty()){