Skip to content

Commit

Permalink
refactoring and documentation of the latest code updates
Browse files Browse the repository at this point in the history
  • Loading branch information
MiguelPelegrina committed Apr 12, 2024
1 parent 689d12f commit 74c26f4
Show file tree
Hide file tree
Showing 8 changed files with 196 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,18 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
* Configuration class for setting up JPAQueryFactory bean.
* Provides bean definitions for creating and configuring JPAQueryFactory instances.
*/
@Configuration
public class JPAQueryFactoryConfig {
/**
* Configures and provides a JPAQueryFactory bean.
*
* @param entityManager The EntityManager instance to be injected into the JPAQueryFactory.
* @return JPAQueryFactory A configured bean of JPAQueryFactory that integrates with JPA.
*/
@Bean
public JPAQueryFactory jpaQueryFactory(@Autowired EntityManager entityManager) {
return new JPAQueryFactory(entityManager);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,17 @@
import org.thymeleaf.spring5.SpringTemplateEngine;
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;

/**
* Configuration class for Thymeleaf template engine setup.
* Provides bean definitions for configuring Thymeleaf template resolution and processing.
*/
@Configuration
public class ThymeleafConfig {
/**
* Configures and provides a ClassLoaderTemplateResolver bean for Thymeleaf.
*
* @return ClassLoaderTemplateResolver A configured template resolver for resolving templates from the classpath.
*/
@Bean
public ClassLoaderTemplateResolver templateResolver() {
ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
Expand All @@ -18,6 +27,11 @@ public ClassLoaderTemplateResolver templateResolver() {
return templateResolver;
}

/**
* Configures and provides a SpringTemplateEngine bean for Thymeleaf.
*
* @return SpringTemplateEngine A configured template engine that integrates with Spring and Thymeleaf.
*/
@Bean
public SpringTemplateEngine templateEngine() {
var templateEngine = new SpringTemplateEngine();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,18 @@

import com.java_school.final_task.domain.book.BookEntity;
import com.java_school.final_task.domain.book.BookRepository;
import com.java_school.final_task.domain.order.OrderEntity;
import com.java_school.final_task.domain.order.OrderRepository;
import com.java_school.final_task.domain.order.OrderService;
import com.java_school.final_task.domain.order.QOrderEntity;
import com.java_school.final_task.domain.order.*;
import com.java_school.final_task.domain.order.dto.OrderDTO;
import com.java_school.final_task.domain.order.dto.OrderRequestDTO;
import com.java_school.final_task.domain.order.dto.SaveOrderDTO;
import com.java_school.final_task.domain.order_book.QOrderBookEntity;
import com.java_school.final_task.domain.user.UserEntity;
import com.java_school.final_task.domain.user.UserService;
import com.java_school.final_task.domain.user.impl.UserServiceImpl;
import com.java_school.final_task.exception.book.ProductNotAvailableException;
import com.java_school.final_task.exception.book.ProductOutOfStockException;
import com.java_school.final_task.exception.user.InactiveUserException;
import com.java_school.final_task.utils.MailService;
import com.java_school.final_task.utils.impl.AbstractServiceImpl;
import com.java_school.final_task.utils.impl.MailServiceImpl;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.dsl.StringExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
Expand All @@ -34,35 +29,33 @@
import java.time.LocalDate;

/**
* Service class responsible for the interaction between the {@link OrderRepository} and the {@link OrderRestControllerImpl}.
* Service class responsible for the interaction between the {@link OrderRepository} and the {@link OrderRestController}.
* Obtains data from the {@link OrderRepository} and returns the object(s) of the {@link OrderEntity} as {@link OrderDTO}
* to the {@link OrderRestControllerImpl}.
* to the {@link OrderRestController}.
*/
@Secured({"ROLE_ADMIN", "EMPLOYEE", "CLIENT"})
@Secured({"ROLE_ADMIN", "ROLE_EMPLOYEE", "ROLE_CLIENT"})
@Service
public class OrderServiceImpl
extends AbstractServiceImpl<OrderRepository, OrderEntity, OrderDTO, Integer>
implements OrderService {
// Fields
private final UserService userService;

private final MailServiceImpl mailService;
private final MailService mailService;
private final BookRepository bookRepository;

private final JPAQueryFactory queryFactory;

/**
* All arguments constructor.
*
* @param repository {@link OrderRepository} of the {@link OrderEntity}.
* @param modelMapper ModelMapper that converts the {@link OrderEntity} instance to {@link OrderDTO}
* @param userService {@link UserServiceImpl} of the {@link UserEntity}.
* @param mailService {@link MailServiceImpl} of the {@link MailService}
* @param userService {@link UserService} of the {@link UserEntity}.
* @param mailService {@link MailService} of the {@link MailService}
* @param bookRepository {@link BookRepository} of the {@link BookEntity}.
* @param queryFactory {@link JPAQueryFactory} for query and DML clause creation.
*/
public OrderServiceImpl(OrderRepository repository, ModelMapper modelMapper, MailServiceImpl mailService,
UserServiceImpl userService, BookRepository bookRepository, JPAQueryFactory queryFactory) {
public OrderServiceImpl(OrderRepository repository, ModelMapper modelMapper, MailService mailService,
UserService userService, BookRepository bookRepository, JPAQueryFactory queryFactory) {
super(repository, modelMapper);
this.userService = userService;
this.mailService = mailService;
Expand Down Expand Up @@ -101,7 +94,7 @@ public BigDecimal calculateTotalRevenue(LocalDate startDate, LocalDate endDate)
}

@Override
@Secured({"ROLE_ADMIN", "EMPLOYEE"})
@Secured({"ROLE_ADMIN", "ROLE_EMPLOYEE"})
public void deleteInstance(Integer integer) {
super.deleteInstance(integer);
}
Expand All @@ -117,11 +110,7 @@ public Page<OrderDTO> getAllInstances(OrderRequestDTO orderRequestDTO) {

// Check if the current user is active
if (currentUser.isActive()) {
// Check which parameters are present and build a query
handleParameter(orderRequestDTO.getDeliveryMethod(), queryBuilder, qOrder.deliveryMethod.name);
handleParameter(orderRequestDTO.getOrderStatus(), queryBuilder, qOrder.orderStatus.name);
handleParameter(orderRequestDTO.getPaymentMethod(), queryBuilder, qOrder.paymentMethod.name);
handleParameter(orderRequestDTO.getPaymentStatus(), queryBuilder, qOrder.paymentStatus.name);
handleQueryParameters(orderRequestDTO, queryBuilder, qOrder);

if (orderRequestDTO.getDate() != null) {
queryBuilder.and(qOrder.date.eq(orderRequestDTO.getDate()));
Expand Down Expand Up @@ -201,7 +190,28 @@ public OrderDTO saveInstance(SaveOrderDTO saveOrderDTO) {
return modelMapper.map(repository.save(newOrder), getDTOClass());
}

private void handleParameter(String parameter, BooleanBuilder queryBuilder, StringExpression expression) {
queryBuilder.and(!"".equals(parameter) ? expression.containsIgnoreCase(parameter) : null);
/**
* Handles a query parameter by adding a condition to the BooleanBuilder.
*
* @param parameter The query parameter to handle.
* @param queryBuilder The BooleanBuilder to which the condition will be added.
* @param expression The StringExpression representing the field to filter on.
*/
private void handleQueryParameter(String parameter, BooleanBuilder queryBuilder, StringExpression expression) {
queryBuilder.and(!parameter.trim().isEmpty() ? expression.containsIgnoreCase(parameter) : null);
}

/**
* Handles multiple query parameters for an order request by delegating to handleQueryParameter for each parameter.
*
* @param orderRequestDTO The OrderRequestDTO containing the query parameters.
* @param queryBuilder The BooleanBuilder to which the conditions will be added.
* @param qOrder The QOrderEntity representing the query entity.
*/
private void handleQueryParameters(OrderRequestDTO orderRequestDTO, BooleanBuilder queryBuilder, QOrderEntity qOrder) {
handleQueryParameter(orderRequestDTO.getDeliveryMethod(), queryBuilder, qOrder.deliveryMethod.name);
handleQueryParameter(orderRequestDTO.getOrderStatus(), queryBuilder, qOrder.orderStatus.name);
handleQueryParameter(orderRequestDTO.getPaymentMethod(), queryBuilder, qOrder.paymentMethod.name);
handleQueryParameter(orderRequestDTO.getPaymentStatus(), queryBuilder, qOrder.paymentStatus.name);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@

import java.util.Optional;

// TODO Too many comments

/**
* Service class responsible for the interaction between the {@link UserRepository} and the
* {@link UserRestControllerImpl}. Obtains data from the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,16 @@

import com.java_school.final_task.domain.order.dto.SaveOrderDTO;

/**
* Interface for sending emails related to orders.
* Defines methods for sending order confirmation emails.
*/
public interface MailService {
/**
* Sends an order confirmation email based on the provided SaveOrderDTO.
*
* @param saveOrderDTO The SaveOrderDTO object containing the details of the order.
*/
void sendOrderConfirmationMail(SaveOrderDTO saveOrderDTO);

//void sendRegistrationConfirmationMail(String to, String text);
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/com/java_school/final_task/utils/PDFService.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

import com.java_school.final_task.domain.order.dto.SaveOrderDTO;

/**
* Interface for PDF generation service.
* Defines methods for generating PDF documents related to orders.
*/
public interface PDFService {
/**
* Generates a PDF document containing order details based on the provided SaveOrderDTO.
*
* @param saveOrderDTO The SaveOrderDTO object containing the details of the order.
* @return byte[] The byte array representing the generated PDF document.
*/
byte[] generateOrderDetailsPDF(SaveOrderDTO saveOrderDTO);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.java_school.final_task.domain.order_book.OrderBookEntity;
import com.java_school.final_task.exception.MessagingExceptionWrapper;
import com.java_school.final_task.utils.MailService;
import com.java_school.final_task.utils.PDFService;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import org.springframework.beans.factory.annotation.Value;
Expand All @@ -18,57 +19,106 @@
import java.util.HashMap;
import java.util.Map;

/**
* {@link MailService} implementation responsible for sending emails.
* This class implements the MailService interface and provides functionality to generate HTML content,
* attach PDF documents, and send emails using Spring's JavaMailSender.
*/
@Service
public class MailServiceImpl implements MailService {
private final JavaMailSender mailSender;

private final SpringTemplateEngine thymeleafTemplateEngine;

private final PDFServiceImpl pdfService;

@Value("${MAIL_USERNAME}")
private String mailFromAddress;

public MailServiceImpl(JavaMailSender mailSender, PDFServiceImpl pdfService, SpringTemplateEngine thymeleafTemplateEngine) {
private final PDFService pdfService;
private final String mailFromAddress;

/**
* Constructs a new MailServiceImpl with the specified dependencies.
*
* @param mailSender The {@link JavaMailSender} instance used to send emails.
* @param pdfService The {@link PDFServiceImpl} instance used to generate PDF documents.
* @param thymeleafTemplateEngine The {@link SpringTemplateEngine} instance used to process HTML templates.
* @param mailFromAddress The email address from which the emails will be sent.
*/
public MailServiceImpl(
JavaMailSender mailSender,
PDFService pdfService,
SpringTemplateEngine thymeleafTemplateEngine,
@Value("${MAIL_USERNAME}") String mailFromAddress
) {
this.mailSender = mailSender;
this.thymeleafTemplateEngine = thymeleafTemplateEngine;
this.pdfService = pdfService;
this.mailFromAddress = mailFromAddress;
}

/**
* Sends an order confirmation email to the user with the order details.
*
* @param saveOrderDTO The {@link SaveOrderDTO} instance containing the order details.
*/
@Override
public void sendOrderConfirmationMail(SaveOrderDTO saveOrderDTO) {
try {
byte[] pdfContent = pdfService.generateOrderDetailsPDF(saveOrderDTO);
byte[] pdfContent = pdfService.generateOrderDetailsPDF(saveOrderDTO);

// Prepare the model for the template
Map<String, Object> templateModel = new HashMap<>();
templateModel.put("customerName", saveOrderDTO.getOrder().getUser().getName());
templateModel.put("orderedBooks", saveOrderDTO.getOrderedBooks());
BigDecimal total = saveOrderDTO.getOrderedBooks().stream().map(OrderBookEntity::getTotal).reduce(BigDecimal.ZERO, BigDecimal::add);
templateModel.put("total", total);
String htmlContent = this.generateHTMLContent(saveOrderDTO);

// Process the template
Context thymeleafContext = new Context();
thymeleafContext.setVariables(templateModel);
String htmlContent = thymeleafTemplateEngine.process("order-confirmation-mail", thymeleafContext);
MimeMessage message = createMimeMessage(saveOrderDTO, htmlContent, pdfContent);

MimeMessage message = mailSender.createMimeMessage();
mailSender.send(message);
}

/**
* Generates HTML content for the order confirmation email.
*
* @param saveOrderDTO The {@code SaveOrderDTO} containing the order details.
* @return The HTML content of the email.
*/
private String generateHTMLContent(SaveOrderDTO saveOrderDTO) {
Map<String, Object> templateModel = this.generateTemplateModel(saveOrderDTO);
Context thymeleafContext = new Context();
thymeleafContext.setVariables(templateModel);
return thymeleafTemplateEngine.process("order-confirmation-mail", thymeleafContext);
}

/**
* Generates a template model for the order confirmation email.
*
* @param saveOrderDTO The {@code SaveOrderDTO} instance containing the order details.
* @return The template model containing data for the email template.
*/
private Map<String, Object> generateTemplateModel(SaveOrderDTO saveOrderDTO) {
Map<String, Object> templateModel = new HashMap<>();
templateModel.put("customerName", saveOrderDTO.getOrder().getUser().getName());
templateModel.put("orderedBooks", saveOrderDTO.getOrderedBooks());
BigDecimal total = saveOrderDTO.getOrderedBooks().stream().map(OrderBookEntity::getTotal).reduce(BigDecimal.ZERO, BigDecimal::add);
templateModel.put("total", total);
return templateModel;
}

/**
* Creates a MimeMessage for the order confirmation email.
*
* @param saveOrderDTO The {@code SaveOrderDTO} instance containing the order details.
* @param htmlContent The HTML content of the email.
* @param pdfContent The content of the PDF attachment.
* @return The MimeMessage for the email.
*/
private MimeMessage createMimeMessage(SaveOrderDTO saveOrderDTO, String htmlContent, byte[] pdfContent) {
MimeMessage message = mailSender.createMimeMessage();
try {
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setTo(saveOrderDTO.getOrder().getUser().getEmail());
helper.setSubject("Order confirmation at Online Bookstore");

// Set the content of the email
helper.setText(htmlContent, true);

// Add the PDF as an attachment
String attachmentName = "order_" + saveOrderDTO.getOrder().getDate() + ".pdf";
helper.addAttachment(attachmentName, new ByteArrayResource(pdfContent));

this.sendMail(message);
message.setFrom(mailFromAddress);
} catch (MessagingException e) {
throw new MessagingExceptionWrapper();
}

return message;
}

/*@Override
Expand All @@ -81,14 +131,4 @@ public void sendRegistrationConfirmationMail(String to, String text) {
this.sendMail(message);
}*/

private void sendMail(MimeMessage message) {
try {
message.setFrom(mailFromAddress);
} catch (MessagingException e) {
throw new MessagingExceptionWrapper();
}

mailSender.send(message);
}
}
Loading

0 comments on commit 74c26f4

Please sign in to comment.