From 97548f34602602db4553728591b143617b04508a Mon Sep 17 00:00:00 2001 From: Ankur Jhawar Date: Wed, 3 Mar 2021 15:41:58 +0530 Subject: [PATCH] Allow bcc and adding search feature --- .../fakesmtp/controller/EmailController.java | 37 +++++++++++++++++++ .../de/gessnerfl/fakesmtp/model/Email.java | 12 ++++++ .../fakesmtp/model/EmailSpecification.java | 23 ++++++++++++ .../fakesmtp/repository/EmailRepository.java | 5 +++ .../V1_0_0__initial_table_structure.sql | 1 + src/main/resources/templates/email-list.html | 14 ++++++- src/main/resources/templates/email.html | 4 ++ 7 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 src/main/java/de/gessnerfl/fakesmtp/model/EmailSpecification.java diff --git a/src/main/java/de/gessnerfl/fakesmtp/controller/EmailController.java b/src/main/java/de/gessnerfl/fakesmtp/controller/EmailController.java index 9adbca83..56ecec1c 100644 --- a/src/main/java/de/gessnerfl/fakesmtp/controller/EmailController.java +++ b/src/main/java/de/gessnerfl/fakesmtp/controller/EmailController.java @@ -1,11 +1,16 @@ package de.gessnerfl.fakesmtp.controller; import de.gessnerfl.fakesmtp.model.Email; +import de.gessnerfl.fakesmtp.model.EmailSpecification; import de.gessnerfl.fakesmtp.repository.EmailRepository; + +import java.util.Set; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.info.BuildProperties; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Controller; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @@ -23,6 +28,7 @@ public class EmailController { static final String SINGLE_EMAIL_VIEW = "email"; static final String SINGLE_EMAIL_MODEL_NAME = "mail"; static final String REDIRECT_EMAIL_LIST_VIEW = "redirect:/email"; + private static final Set FIELDS_TO_LOOKUP = Set.of("fromAddress", "toAddress", "bccAddress", "subject"); private final EmailRepository emailRepository; private final BuildProperties buildProperties; @@ -38,6 +44,37 @@ public String getAll(@RequestParam(value = "page", defaultValue = "0") int page, return getAllEmailsPaged(page, size, model); } + @GetMapping({ "/search" }) + public String search(@RequestParam(value = "page", defaultValue = "0") int page, + @RequestParam(value = "size", defaultValue = "" + DEFAULT_PAGE_SIZE) int size, + @RequestParam(value = "q", defaultValue = "") String searchQuery, Model model) { + return searchedEmailsPaged(page, size, searchQuery, model); + } + + private String searchedEmailsPaged(int page, int size, String searchQuery, Model model) { + if (page < 0 || size <= 0) { + return REDIRECT_EMAIL_LIST_VIEW; + } + if (searchQuery == null || searchQuery.isEmpty()) { + var result = emailRepository.findAll(PageRequest.of(page, size, DEFAULT_SORT)); + if (result.getNumber() != 0 && result.getNumber() >= result.getTotalPages()) { + return REDIRECT_EMAIL_LIST_VIEW; + } + model.addAttribute(EMAIL_LIST_MODEL_NAME, result); + } else { + model.addAttribute("q", searchQuery); + var result = emailRepository.findAll( + Specification.where(EmailSpecification.textInAllColumns(searchQuery, FIELDS_TO_LOOKUP)), + PageRequest.of(page, size, DEFAULT_SORT)); + if (result.getNumber() != 0 && result.getNumber() >= result.getTotalPages()) { + return REDIRECT_EMAIL_LIST_VIEW; + } + model.addAttribute(EMAIL_LIST_MODEL_NAME, result); + } + addApplicationVersion(model); + return EMAIL_LIST_VIEW; + } + private String getAllEmailsPaged(int page, int size, Model model) { if(page < 0 || size <= 0){ return REDIRECT_EMAIL_LIST_VIEW; diff --git a/src/main/java/de/gessnerfl/fakesmtp/model/Email.java b/src/main/java/de/gessnerfl/fakesmtp/model/Email.java index 8de0dc8b..ceea5a0a 100644 --- a/src/main/java/de/gessnerfl/fakesmtp/model/Email.java +++ b/src/main/java/de/gessnerfl/fakesmtp/model/Email.java @@ -24,6 +24,10 @@ public class Email { @Basic(optional = false) private String toAddress; + @Column(name = "bcc_address", length = 255, nullable = true) + @Basic(optional = true) + private String bccAddress; + @Lob @Column(name="subject", nullable = false) @Basic(optional = false) @@ -69,6 +73,14 @@ public void setToAddress(String toAddress) { this.toAddress = toAddress; } + public String getBccAddress() { + return bccAddress; + } + + public void setBccAddress(String bccAddress) { + this.bccAddress = bccAddress; + } + public String getSubject() { return subject; } diff --git a/src/main/java/de/gessnerfl/fakesmtp/model/EmailSpecification.java b/src/main/java/de/gessnerfl/fakesmtp/model/EmailSpecification.java new file mode 100644 index 00000000..d5b4c733 --- /dev/null +++ b/src/main/java/de/gessnerfl/fakesmtp/model/EmailSpecification.java @@ -0,0 +1,23 @@ +package de.gessnerfl.fakesmtp.model; + +import java.util.Set; + +import javax.persistence.criteria.Predicate; + +import org.springframework.data.jpa.domain.Specification; + +public class EmailSpecification { + + public static Specification textInAllColumns(String text, Set fields) { + if (!text.contains("%")) { + text = "%" + text + "%"; + } + final String finalText = text; + + return (root, query, builder) -> builder + .or(root.getModel().getDeclaredSingularAttributes().stream().filter(a -> { + return fields.contains(a.getName()); + }).map(a -> builder.like(builder.lower(root.get(a.getName())), finalText.toLowerCase())).toArray(Predicate[]::new)); + } + +} diff --git a/src/main/java/de/gessnerfl/fakesmtp/repository/EmailRepository.java b/src/main/java/de/gessnerfl/fakesmtp/repository/EmailRepository.java index bb3aa412..8a102d3b 100644 --- a/src/main/java/de/gessnerfl/fakesmtp/repository/EmailRepository.java +++ b/src/main/java/de/gessnerfl/fakesmtp/repository/EmailRepository.java @@ -1,6 +1,9 @@ package de.gessnerfl.fakesmtp.repository; import de.gessnerfl.fakesmtp.model.Email; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; @@ -14,4 +17,6 @@ public interface EmailRepository extends JpaRepository{ @Modifying @Query(value = "DELETE email o WHERE o.id IN ( SELECT i.id FROM email i ORDER BY i.received_on DESC OFFSET ?1)", nativeQuery = true) int deleteEmailsExceedingDateRetentionLimit(int maxNumber); + + Page findAll(Specification spec, Pageable pageable); } diff --git a/src/main/resources/db/migration/V1_0_0__initial_table_structure.sql b/src/main/resources/db/migration/V1_0_0__initial_table_structure.sql index 494ae671..a44bfe7e 100644 --- a/src/main/resources/db/migration/V1_0_0__initial_table_structure.sql +++ b/src/main/resources/db/migration/V1_0_0__initial_table_structure.sql @@ -2,6 +2,7 @@ CREATE TABLE email ( id BIGINT GENERATED BY DEFAULT AS IDENTITY, from_address VARCHAR(255) NOT NULL, to_address VARCHAR(255) NOT NULL, + bcc_address VARCHAR(5000), subject TEXT NOT NULL, received_on TIMESTAMP NOT NULL, raw_data TEXT NOT NULL diff --git a/src/main/resources/templates/email-list.html b/src/main/resources/templates/email-list.html index b1be8fa7..4cd71afe 100644 --- a/src/main/resources/templates/email-list.html +++ b/src/main/resources/templates/email-list.html @@ -15,13 +15,24 @@

inboxInbox

- +
+
+
+ + +
+
+
+ + @@ -32,6 +43,7 @@

inboxInbox

+ + + + +
ID From ToBcc Received On Subject Details Id From ToBcc Received On diff --git a/src/main/resources/templates/email.html b/src/main/resources/templates/email.html index 18074631..454339bc 100644 --- a/src/main/resources/templates/email.html +++ b/src/main/resources/templates/email.html @@ -20,6 +20,10 @@ To: To
Bcc:To
Received On: Received On