diff --git a/application/src/main/java/com/pablotj/restemailbridge/application/usecase/SendEmailUseCase.java b/application/src/main/java/com/pablotj/restemailbridge/application/usecase/SendEmailUseCase.java index da9fd0e..4e91745 100644 --- a/application/src/main/java/com/pablotj/restemailbridge/application/usecase/SendEmailUseCase.java +++ b/application/src/main/java/com/pablotj/restemailbridge/application/usecase/SendEmailUseCase.java @@ -5,6 +5,7 @@ import com.pablotj.restemailbridge.application.port.in.EmailDefaultConfigPort; import com.pablotj.restemailbridge.application.port.out.EmailPort; import com.pablotj.restemailbridge.domain.model.Email; import com.pablotj.restemailbridge.domain.repository.EmailRepository; +import com.pablotj.restemailbridge.domain.service.EmailValidatorService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -18,20 +19,13 @@ public class SendEmailUseCase { private static final Logger log = LoggerFactory.getLogger(SendEmailUseCase.class); + private final EmailValidatorService emailValidatorService; private final EmailDefaultConfigPort emailConfigurationPort; private final EmailPort emailService; private final EmailRepository emailRepository; - /** - * Constructor injecting required ports. - * - * @param emailConfigurationPort Port to retrieve configuration - * @param emailService Service to send emails - * @param emailRepository Repository to persist emails - */ - public SendEmailUseCase(EmailDefaultConfigPort emailConfigurationPort, - EmailPort emailService, - EmailRepository emailRepository) { + public SendEmailUseCase(EmailValidatorService emailValidatorService, EmailDefaultConfigPort emailConfigurationPort, EmailPort emailService, EmailRepository emailRepository) { + this.emailValidatorService = emailValidatorService; this.emailConfigurationPort = emailConfigurationPort; this.emailService = emailService; this.emailRepository = emailRepository; @@ -44,16 +38,19 @@ public class SendEmailUseCase { */ public void handle(EmailDTO emailDTO) { String to = emailConfigurationPort.getDefaultRecipient(); + + Email email = Email.builder() + .from(emailDTO.from()) + .to(to) + .subject(emailDTO.subject()) + .body(emailDTO.body()) + .build(); + + emailValidatorService.validate(email); + log.info("Sending email from {} to {}", emailDTO.from(), to); - Email email = emailService.sendEmail( - Email.builder() - .from(emailDTO.from()) - .to(to) - .subject(emailDTO.subject()) - .body(emailDTO.body()) - .build() - ); + email = emailService.sendEmail(email); emailRepository.save(email); log.info("Email successfully sent and persisted to repository for recipient {}", to); diff --git a/domain/src/main/java/com/pablotj/restemailbridge/domain/service/EmailValidatorService.java b/domain/src/main/java/com/pablotj/restemailbridge/domain/service/EmailValidatorService.java new file mode 100644 index 0000000..2251e83 --- /dev/null +++ b/domain/src/main/java/com/pablotj/restemailbridge/domain/service/EmailValidatorService.java @@ -0,0 +1,22 @@ +package com.pablotj.restemailbridge.domain.service; + +import com.pablotj.restemailbridge.domain.model.Email; + + +public class EmailValidatorService { + + /** + * Validates business rules for Email. + */ + public void validate(Email email) { + if (email == null) throw new IllegalArgumentException("Email cannot be null"); + if (email.getTo() == null || !email.getTo().matches(".+@.+\\..+")) + throw new IllegalArgumentException("Recipient email is invalid"); + if (email.getFrom() == null || email.getFrom().isBlank()) + throw new IllegalArgumentException("Sender email is required"); + if (email.getSubject() == null || email.getSubject().isBlank()) + throw new IllegalArgumentException("Subject is required"); + if (email.getBody() == null || email.getBody().isBlank()) + throw new IllegalArgumentException("Body is required"); + } +} diff --git a/infrastructure/src/main/java/com/pablotj/restemailbridge/infrastructure/config/UseCaseConfig.java b/infrastructure/src/main/java/com/pablotj/restemailbridge/infrastructure/config/UseCaseConfig.java index d9c2096..ae6e903 100644 --- a/infrastructure/src/main/java/com/pablotj/restemailbridge/infrastructure/config/UseCaseConfig.java +++ b/infrastructure/src/main/java/com/pablotj/restemailbridge/infrastructure/config/UseCaseConfig.java @@ -4,6 +4,7 @@ import com.pablotj.restemailbridge.application.port.in.EmailDefaultConfigPort; import com.pablotj.restemailbridge.application.port.out.EmailPort; import com.pablotj.restemailbridge.application.usecase.SendEmailUseCase; import com.pablotj.restemailbridge.domain.repository.EmailRepository; +import com.pablotj.restemailbridge.domain.service.EmailValidatorService; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -12,6 +13,6 @@ public class UseCaseConfig { @Bean public SendEmailUseCase sendEmailUseCase(EmailDefaultConfigPort emailConfigurationPort, EmailPort emailService, EmailRepository emailRepository) { - return new SendEmailUseCase(emailConfigurationPort, emailService, emailRepository); + return new SendEmailUseCase(new EmailValidatorService(), emailConfigurationPort, emailService, emailRepository); } } \ No newline at end of file diff --git a/infrastructure/src/main/java/com/pablotj/restemailbridge/infrastructure/persistence/MailJpa.java b/infrastructure/src/main/java/com/pablotj/restemailbridge/infrastructure/persistence/MailJpa.java index 9684e8d..053bb58 100644 --- a/infrastructure/src/main/java/com/pablotj/restemailbridge/infrastructure/persistence/MailJpa.java +++ b/infrastructure/src/main/java/com/pablotj/restemailbridge/infrastructure/persistence/MailJpa.java @@ -19,15 +19,15 @@ public class MailJpa { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @Column(nullable = false) + @Column(length = 100, nullable = false) private String sender; - @Column(nullable = false) + @Column(length = 100, nullable = false) private String recipient; - @Column(nullable = false) + @Column(length = 50, nullable = false) private String subjet; - @Column(nullable = false) + @Column(length = 40000, nullable = false) private String body; } diff --git a/infrastructure/src/main/java/com/pablotj/restemailbridge/infrastructure/rest/GlobalExceptionHandler.java b/infrastructure/src/main/java/com/pablotj/restemailbridge/infrastructure/rest/GlobalExceptionHandler.java new file mode 100644 index 0000000..2c87116 --- /dev/null +++ b/infrastructure/src/main/java/com/pablotj/restemailbridge/infrastructure/rest/GlobalExceptionHandler.java @@ -0,0 +1,20 @@ +package com.pablotj.restemailbridge.infrastructure.rest; + +import java.util.HashMap; +import java.util.Map; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity> handleValidationExceptions(MethodArgumentNotValidException ex) { + Map errors = new HashMap<>(); + ex.getBindingResult().getFieldErrors().forEach(error -> + errors.put(error.getField(), error.getDefaultMessage())); + return ResponseEntity.badRequest().body(errors); + } +} diff --git a/infrastructure/src/main/java/com/pablotj/restemailbridge/infrastructure/rest/dto/SendMailRequest.java b/infrastructure/src/main/java/com/pablotj/restemailbridge/infrastructure/rest/dto/SendMailRequest.java index ba07d94..11c752d 100644 --- a/infrastructure/src/main/java/com/pablotj/restemailbridge/infrastructure/rest/dto/SendMailRequest.java +++ b/infrastructure/src/main/java/com/pablotj/restemailbridge/infrastructure/rest/dto/SendMailRequest.java @@ -1,8 +1,12 @@ package com.pablotj.restemailbridge.infrastructure.rest.dto; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import org.hibernate.validator.constraints.Length; + public record SendMailRequest( - String from, - String subject, - String body + @NotBlank @Email @Length(min = 4, max = 100) String from, + @NotBlank @Length(min=1, max = 30) String subject, + @NotBlank @Length(min=1, max = 4000) String body ) { -} +} \ No newline at end of file