mirror of
				https://github.com/Frooodle/Stirling-PDF.git
				synced 2025-11-01 01:21:18 +01:00 
			
		
		
		
	Add Email Sending Service with Attachment Support (#3455)
# Description of Changes Please provide a summary of the changes, including: - **What was changed** - Introduced a new `EmailService` for asynchronous email delivery with attachment support. - Added `MailConfig` to configure a `JavaMailSender` bean using SMTP settings from `ApplicationProperties`. - Created `EmailController` endpoint (`/api/v1/general/send-email`) to accept multipart/form-data requests for sending emails. - Defined an `Email` API model to encapsulate recipient, subject, body, and file input. - Extended `ApplicationProperties` to include a nested `Mail` class for SMTP host, port, username/password, and sender address. - Updated `settings.yml.template` to include SMTP configuration placeholders. - Enhanced `.github/labeler-config.yml` to cover all new security- and API-related source files for automated labeling. - **Why the change was made** - To enable Stirling-PDF to notify users via email—particularly useful for sending generated PDFs or alerts—directly from the application. - To centralize mail server configuration in application properties and streamline onboarding for new environments. --- ## Checklist ### General - [x] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [x] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md) (if applicable) - [x] I have performed a self-review of my own code - [x] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing) for more details.
This commit is contained in:
		
							parent
							
								
									2ac606608a
								
							
						
					
					
						commit
						5b0eaec436
					
				
							
								
								
									
										18
									
								
								.github/labeler-config.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										18
									
								
								.github/labeler-config.yml
									
									
									
									
										vendored
									
									
								
							@ -27,18 +27,34 @@ Back End:
 | 
			
		||||
 | 
			
		||||
Security:
 | 
			
		||||
  - changed-files:
 | 
			
		||||
    - any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/config/interfaces/DatabaseInterface.java'
 | 
			
		||||
    - any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/config/security/**/*'
 | 
			
		||||
    - any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/controller/api/DatabaseController.java'
 | 
			
		||||
    - any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/controller/api/EmailController.java'
 | 
			
		||||
    - any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/controller/api/H2SQLController.java'
 | 
			
		||||
    - any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/controller/web/AccountWebController.java'
 | 
			
		||||
    - any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/controller/web/DatabaseWebController.java'
 | 
			
		||||
    - any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/controller/api/UserController.java'
 | 
			
		||||
    - any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/model/api/Email.java'
 | 
			
		||||
    - any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/model/exception/BackupNotFoundException.java'
 | 
			
		||||
    - any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/model/exception/NoProviderFoundExceptionjava'
 | 
			
		||||
    - any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/model/provider/**/*'
 | 
			
		||||
    - any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/model/AuthenticationType.java'
 | 
			
		||||
    - any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/model/BackupNotFoundException.java'
 | 
			
		||||
    - any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/model/ApiKeyAuthenticationToken.java'
 | 
			
		||||
    - any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/model/AttemptCounter.java'
 | 
			
		||||
    - any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/model/Authority.java'
 | 
			
		||||
    - any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/model/PersistentLogin.java'
 | 
			
		||||
    - any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/model/SessionEntity.java'
 | 
			
		||||
    - any-glob-to-any-file: 'scripts/download-security-jar.sh'
 | 
			
		||||
    - any-glob-to-any-file: '.github/workflows/dependency-review.yml'
 | 
			
		||||
    - any-glob-to-any-file: '.github/workflows/scorecards.yml'
 | 
			
		||||
 | 
			
		||||
API:
 | 
			
		||||
  - changed-files:
 | 
			
		||||
    - any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/config/OpenApiConfig.java'
 | 
			
		||||
    - any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/controller/web/MetricsController.java'
 | 
			
		||||
    - any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/controller/api/**/*'
 | 
			
		||||
    - any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/model/api/**/*'
 | 
			
		||||
    - any-glob-to-any-file: 'scripts/png_to_webp.py'
 | 
			
		||||
    - any-glob-to-any-file: 'split_photos.py'
 | 
			
		||||
    - any-glob-to-any-file: '.github/workflows/swagger.yml'
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										19
									
								
								build.gradle
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								build.gradle
									
									
									
									
									
								
							@ -51,16 +51,20 @@ sourceSets {
 | 
			
		||||
    main {
 | 
			
		||||
        java {
 | 
			
		||||
            if (System.getenv("DOCKER_ENABLE_SECURITY") == "false") {
 | 
			
		||||
                exclude "stirling/software/SPDF/config/interfaces/DatabaseInterface.java"
 | 
			
		||||
                exclude "stirling/software/SPDF/config/security/**"
 | 
			
		||||
                exclude "stirling/software/SPDF/controller/api/DatabaseController.java"
 | 
			
		||||
                exclude "stirling/software/SPDF/controller/api/UserController.java"
 | 
			
		||||
                exclude "stirling/software/SPDF/controller/api/EmailController.java"
 | 
			
		||||
                exclude "stirling/software/SPDF/controller/api/H2SQLCondition.java"
 | 
			
		||||
                exclude "stirling/software/SPDF/controller/api/UserController.java"
 | 
			
		||||
                exclude "stirling/software/SPDF/controller/web/AccountWebController.java"
 | 
			
		||||
                exclude "stirling/software/SPDF/controller/web/DatabaseWebController.java"
 | 
			
		||||
                exclude "stirling/software/SPDF/model/api/Email.java"
 | 
			
		||||
                exclude "stirling/software/SPDF/model/ApiKeyAuthenticationToken.java"
 | 
			
		||||
                exclude "stirling/software/SPDF/model/AttemptCounter.java"
 | 
			
		||||
                exclude "stirling/software/SPDF/model/Authority.java"
 | 
			
		||||
                exclude "stirling/software/SPDF/model/BackupNotFoundException.java"
 | 
			
		||||
                exclude "stirling/software/SPDF/model/exception/BackupNotFoundException.java"
 | 
			
		||||
                exclude "stirling/software/SPDF/model/exception/NoProviderFoundException.java"
 | 
			
		||||
                exclude "stirling/software/SPDF/model/PersistentLogin.java"
 | 
			
		||||
                exclude "stirling/software/SPDF/model/SessionEntity.java"
 | 
			
		||||
                exclude "stirling/software/SPDF/model/User.java"
 | 
			
		||||
@ -78,16 +82,8 @@ sourceSets {
 | 
			
		||||
        java {
 | 
			
		||||
            if (System.getenv("DOCKER_ENABLE_SECURITY") == "false") {
 | 
			
		||||
                exclude "stirling/software/SPDF/config/security/**"
 | 
			
		||||
                exclude "stirling/software/SPDF/controller/api/UserControllerTest.java"
 | 
			
		||||
                exclude "stirling/software/SPDF/controller/api/DatabaseControllerTest.java"
 | 
			
		||||
                exclude "stirling/software/SPDF/controller/web/AccountWebControllerTest.java"
 | 
			
		||||
                exclude "stirling/software/SPDF/controller/web/DatabaseWebControllerTest.java"
 | 
			
		||||
                exclude "stirling/software/SPDF/model/ApiKeyAuthenticationTokenTest.java"
 | 
			
		||||
                exclude "stirling/software/SPDF/model/AttemptCounterTest.java"
 | 
			
		||||
                exclude "stirling/software/SPDF/model/AuthorityTest.java"
 | 
			
		||||
                exclude "stirling/software/SPDF/model/PersistentLoginTest.java"
 | 
			
		||||
                exclude "stirling/software/SPDF/model/SessionEntityTest.java"
 | 
			
		||||
                exclude "stirling/software/SPDF/model/UserTest.java"
 | 
			
		||||
                exclude "stirling/software/SPDF/controller/api/EmailControllerTest.java"
 | 
			
		||||
                exclude "stirling/software/SPDF/repository/**"
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -459,6 +455,7 @@ dependencies {
 | 
			
		||||
        implementation "org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.1.3.RELEASE"
 | 
			
		||||
        implementation "org.springframework.boot:spring-boot-starter-data-jpa:$springBootVersion"
 | 
			
		||||
        implementation "org.springframework.boot:spring-boot-starter-oauth2-client:$springBootVersion"
 | 
			
		||||
        implementation "org.springframework.boot:spring-boot-starter-mail:$springBootVersion"
 | 
			
		||||
 | 
			
		||||
        implementation "org.springframework.session:spring-session-core:3.4.3"
 | 
			
		||||
        implementation "org.springframework:spring-jdbc:6.2.6"
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,63 @@
 | 
			
		||||
package stirling.software.SPDF.config.security.mail;
 | 
			
		||||
 | 
			
		||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 | 
			
		||||
import org.springframework.mail.javamail.JavaMailSender;
 | 
			
		||||
import org.springframework.mail.javamail.MimeMessageHelper;
 | 
			
		||||
import org.springframework.scheduling.annotation.Async;
 | 
			
		||||
import org.springframework.stereotype.Service;
 | 
			
		||||
import org.springframework.web.multipart.MultipartFile;
 | 
			
		||||
 | 
			
		||||
import jakarta.mail.MessagingException;
 | 
			
		||||
import jakarta.mail.internet.MimeMessage;
 | 
			
		||||
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
 | 
			
		||||
import stirling.software.SPDF.model.ApplicationProperties;
 | 
			
		||||
import stirling.software.SPDF.model.api.Email;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Service class responsible for sending emails, including those with attachments. It uses
 | 
			
		||||
 * JavaMailSender to send the email and is designed to handle both the message content and file
 | 
			
		||||
 * attachments.
 | 
			
		||||
 */
 | 
			
		||||
@Service
 | 
			
		||||
@RequiredArgsConstructor
 | 
			
		||||
@ConditionalOnProperty(value = "mail.enabled", havingValue = "true", matchIfMissing = false)
 | 
			
		||||
public class EmailService {
 | 
			
		||||
 | 
			
		||||
    private final JavaMailSender mailSender;
 | 
			
		||||
    private final ApplicationProperties applicationProperties;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sends an email with an attachment asynchronously. This method is annotated with @Async, which
 | 
			
		||||
     * means it will be executed asynchronously.
 | 
			
		||||
     *
 | 
			
		||||
     * @param email The Email object containing the recipient, subject, body, and file attachment.
 | 
			
		||||
     * @throws MessagingException If there is an issue with creating or sending the email.
 | 
			
		||||
     */
 | 
			
		||||
    @Async
 | 
			
		||||
    public void sendEmailWithAttachment(Email email) throws MessagingException {
 | 
			
		||||
        ApplicationProperties.Mail mailProperties = applicationProperties.getMail();
 | 
			
		||||
        MultipartFile file = email.getFileInput();
 | 
			
		||||
 | 
			
		||||
        // Creates a MimeMessage to represent the email
 | 
			
		||||
        MimeMessage message = mailSender.createMimeMessage();
 | 
			
		||||
 | 
			
		||||
        // Helper class to set up the message content and attachments
 | 
			
		||||
        MimeMessageHelper helper = new MimeMessageHelper(message, true);
 | 
			
		||||
 | 
			
		||||
        // Sets the recipient, subject, body, and sender email
 | 
			
		||||
        helper.addTo(email.getTo());
 | 
			
		||||
        helper.setSubject(email.getSubject());
 | 
			
		||||
        helper.setText(
 | 
			
		||||
                email.getBody(),
 | 
			
		||||
                true); // The "true" here indicates that the body contains HTML content.
 | 
			
		||||
        helper.setFrom(mailProperties.getFrom());
 | 
			
		||||
 | 
			
		||||
        // Adds the attachment to the email
 | 
			
		||||
        helper.addAttachment(file.getOriginalFilename(), file);
 | 
			
		||||
 | 
			
		||||
        // Sends the email via the configured mail sender
 | 
			
		||||
        mailSender.send(message);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,54 @@
 | 
			
		||||
package stirling.software.SPDF.config.security.mail;
 | 
			
		||||
 | 
			
		||||
import java.util.Properties;
 | 
			
		||||
 | 
			
		||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 | 
			
		||||
import org.springframework.context.annotation.Bean;
 | 
			
		||||
import org.springframework.context.annotation.Configuration;
 | 
			
		||||
import org.springframework.mail.javamail.JavaMailSender;
 | 
			
		||||
import org.springframework.mail.javamail.JavaMailSenderImpl;
 | 
			
		||||
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
 | 
			
		||||
import stirling.software.SPDF.model.ApplicationProperties;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This configuration class provides the JavaMailSender bean, which is used to send emails. It reads
 | 
			
		||||
 * email server settings from the configuration (ApplicationProperties) and configures the mail
 | 
			
		||||
 * client (JavaMailSender).
 | 
			
		||||
 */
 | 
			
		||||
@Configuration
 | 
			
		||||
@Slf4j
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
@ConditionalOnProperty(value = "mail.enabled", havingValue = "true", matchIfMissing = false)
 | 
			
		||||
public class MailConfig {
 | 
			
		||||
 | 
			
		||||
    private final ApplicationProperties applicationProperties;
 | 
			
		||||
 | 
			
		||||
    @Bean
 | 
			
		||||
    public JavaMailSender javaMailSender() {
 | 
			
		||||
 | 
			
		||||
        ApplicationProperties.Mail mailProperties = applicationProperties.getMail();
 | 
			
		||||
 | 
			
		||||
        // Creates a new instance of JavaMailSenderImpl, which is a Spring implementation
 | 
			
		||||
        JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
 | 
			
		||||
        mailSender.setHost(mailProperties.getHost());
 | 
			
		||||
        mailSender.setPort(mailProperties.getPort());
 | 
			
		||||
        mailSender.setUsername(mailProperties.getUsername());
 | 
			
		||||
        mailSender.setPassword(mailProperties.getPassword());
 | 
			
		||||
        mailSender.setDefaultEncoding("UTF-8");
 | 
			
		||||
 | 
			
		||||
        // Retrieves the JavaMail properties to configure additional SMTP parameters
 | 
			
		||||
        Properties props = mailSender.getJavaMailProperties();
 | 
			
		||||
 | 
			
		||||
        // Enables SMTP authentication
 | 
			
		||||
        props.put("mail.smtp.auth", "true");
 | 
			
		||||
 | 
			
		||||
        // Enables STARTTLS to encrypt the connection if supported by the SMTP server
 | 
			
		||||
        props.put("mail.smtp.starttls.enable", "true");
 | 
			
		||||
 | 
			
		||||
        // Returns the configured mail sender, ready to send emails
 | 
			
		||||
        return mailSender;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,57 @@
 | 
			
		||||
package stirling.software.SPDF.controller.api;
 | 
			
		||||
 | 
			
		||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 | 
			
		||||
import org.springframework.http.HttpStatus;
 | 
			
		||||
import org.springframework.http.ResponseEntity;
 | 
			
		||||
import org.springframework.web.bind.annotation.ModelAttribute;
 | 
			
		||||
import org.springframework.web.bind.annotation.PostMapping;
 | 
			
		||||
import org.springframework.web.bind.annotation.RequestMapping;
 | 
			
		||||
import org.springframework.web.bind.annotation.RestController;
 | 
			
		||||
 | 
			
		||||
import io.swagger.v3.oas.annotations.tags.Tag;
 | 
			
		||||
 | 
			
		||||
import jakarta.mail.MessagingException;
 | 
			
		||||
import jakarta.validation.Valid;
 | 
			
		||||
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
 | 
			
		||||
import stirling.software.SPDF.config.security.mail.EmailService;
 | 
			
		||||
import stirling.software.SPDF.model.api.Email;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Controller for handling email-related API requests. This controller exposes an endpoint for
 | 
			
		||||
 * sending emails with attachments.
 | 
			
		||||
 */
 | 
			
		||||
@RestController
 | 
			
		||||
@RequestMapping("/api/v1/general")
 | 
			
		||||
@RequiredArgsConstructor
 | 
			
		||||
@Slf4j
 | 
			
		||||
@Tag(name = "General", description = "General APIs")
 | 
			
		||||
@ConditionalOnProperty(value = "mail.enabled", havingValue = "true", matchIfMissing = false)
 | 
			
		||||
public class EmailController {
 | 
			
		||||
    private final EmailService emailService;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Endpoint to send an email with an attachment. This method consumes a multipart/form-data
 | 
			
		||||
     * request containing the email details and attachment.
 | 
			
		||||
     *
 | 
			
		||||
     * @param email The Email object containing recipient address, subject, body, and file
 | 
			
		||||
     *     attachment.
 | 
			
		||||
     * @return ResponseEntity with success or error message.
 | 
			
		||||
     */
 | 
			
		||||
    @PostMapping(consumes = "multipart/form-data", value = "/send-email")
 | 
			
		||||
    public ResponseEntity<String> sendEmailWithAttachment(@Valid @ModelAttribute Email email) {
 | 
			
		||||
        try {
 | 
			
		||||
            // Calls the service to send the email with attachment
 | 
			
		||||
            emailService.sendEmailWithAttachment(email);
 | 
			
		||||
            return ResponseEntity.ok("Email sent successfully");
 | 
			
		||||
        } catch (MessagingException e) {
 | 
			
		||||
            // Catches any messaging exception (e.g., invalid email address, SMTP server issues)
 | 
			
		||||
            String errorMsg = "Failed to send email: " + e.getMessage();
 | 
			
		||||
            log.error(errorMsg, e); // Logging the detailed error
 | 
			
		||||
            // Returns an error response with status 500 (Internal Server Error)
 | 
			
		||||
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorMsg);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -82,6 +82,8 @@ public class ApplicationProperties {
 | 
			
		||||
    private Metrics metrics = new Metrics();
 | 
			
		||||
    private AutomaticallyGenerated automaticallyGenerated = new AutomaticallyGenerated();
 | 
			
		||||
 | 
			
		||||
    private Mail mail = new Mail();
 | 
			
		||||
 | 
			
		||||
    private Premium premium = new Premium();
 | 
			
		||||
    private EnterpriseEdition enterpriseEdition = new EnterpriseEdition();
 | 
			
		||||
    private AutoPipeline autoPipeline = new AutoPipeline();
 | 
			
		||||
@ -420,6 +422,16 @@ public class ApplicationProperties {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Data
 | 
			
		||||
    public static class Mail {
 | 
			
		||||
        private boolean enabled;
 | 
			
		||||
        private String host;
 | 
			
		||||
        private int port;
 | 
			
		||||
        private String username;
 | 
			
		||||
        @ToString.Exclude private String password;
 | 
			
		||||
        private String from;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Data
 | 
			
		||||
    public static class Premium {
 | 
			
		||||
        private boolean enabled;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										39
									
								
								src/main/java/stirling/software/SPDF/model/api/Email.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/main/java/stirling/software/SPDF/model/api/Email.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,39 @@
 | 
			
		||||
package stirling.software.SPDF.model.api;
 | 
			
		||||
 | 
			
		||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 | 
			
		||||
 | 
			
		||||
import io.swagger.v3.oas.annotations.media.Schema;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.EqualsAndHashCode;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
 | 
			
		||||
@Data
 | 
			
		||||
@NoArgsConstructor
 | 
			
		||||
@EqualsAndHashCode(callSuper = true)
 | 
			
		||||
@ConditionalOnProperty(value = "mail.enabled", havingValue = "true", matchIfMissing = false)
 | 
			
		||||
public class Email extends GeneralFile {
 | 
			
		||||
 | 
			
		||||
    @Schema(
 | 
			
		||||
            description = "The recipient's email address",
 | 
			
		||||
            requiredMode = Schema.RequiredMode.REQUIRED,
 | 
			
		||||
            format = "email")
 | 
			
		||||
    private String to;
 | 
			
		||||
 | 
			
		||||
    @Schema(
 | 
			
		||||
            description = "The subject of the email",
 | 
			
		||||
            defaultValue = "Stirling Software PDF Notification",
 | 
			
		||||
            requiredMode = Schema.RequiredMode.NOT_REQUIRED)
 | 
			
		||||
    private String subject;
 | 
			
		||||
 | 
			
		||||
    @Schema(
 | 
			
		||||
            description = "The body of the email",
 | 
			
		||||
            requiredMode = Schema.RequiredMode.NOT_REQUIRED,
 | 
			
		||||
            defaultValue =
 | 
			
		||||
                    "This message was automatically generated by Stirling-PDF, an innovative"
 | 
			
		||||
                            + " solution from Stirling Software. For more information, visit our <a"
 | 
			
		||||
                            + " href=\"https://stirling-software.com\">website</a>.<br><br>Please do"
 | 
			
		||||
                            + " not reply directly to this email.")
 | 
			
		||||
    private String body;
 | 
			
		||||
}
 | 
			
		||||
@ -76,6 +76,14 @@ premium:
 | 
			
		||||
      apiKey: ''
 | 
			
		||||
      appId: ''
 | 
			
		||||
 | 
			
		||||
mail:
 | 
			
		||||
  enabled: true # set to 'true' to enable sending emails
 | 
			
		||||
  host: smtp.example.com # SMTP server hostname
 | 
			
		||||
  port: 587 # SMTP server port
 | 
			
		||||
  username: '' # SMTP server username
 | 
			
		||||
  password: '' # SMTP server password
 | 
			
		||||
  from: '' # sender email address
 | 
			
		||||
 | 
			
		||||
legal:
 | 
			
		||||
  termsAndConditions: https://www.stirlingpdf.com/terms-and-conditions # URL to the terms and conditions of your application (e.g. https://example.com/terms). Empty string to disable or filename to load from local file in static folder
 | 
			
		||||
  privacyPolicy: https://www.stirlingpdf.com/privacy-policy # URL to the privacy policy of your application (e.g. https://example.com/privacy). Empty string to disable or filename to load from local file in static folder
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,64 @@
 | 
			
		||||
package stirling.software.SPDF.config.security.mail;
 | 
			
		||||
 | 
			
		||||
import static org.mockito.Mockito.*;
 | 
			
		||||
 | 
			
		||||
import jakarta.mail.MessagingException;
 | 
			
		||||
import jakarta.mail.internet.MimeMessage;
 | 
			
		||||
import org.junit.jupiter.api.Test;
 | 
			
		||||
import org.junit.jupiter.api.extension.ExtendWith;
 | 
			
		||||
import org.mockito.InjectMocks;
 | 
			
		||||
import org.mockito.Mock;
 | 
			
		||||
import org.mockito.junit.jupiter.MockitoExtension;
 | 
			
		||||
import org.springframework.mail.javamail.JavaMailSender;
 | 
			
		||||
import org.springframework.mail.javamail.MimeMessageHelper;
 | 
			
		||||
import org.springframework.web.multipart.MultipartFile;
 | 
			
		||||
import stirling.software.SPDF.model.ApplicationProperties;
 | 
			
		||||
import stirling.software.SPDF.model.api.Email;
 | 
			
		||||
 | 
			
		||||
@ExtendWith(MockitoExtension.class)
 | 
			
		||||
public class EmailServiceTest {
 | 
			
		||||
 | 
			
		||||
    @Mock
 | 
			
		||||
    private JavaMailSender mailSender;
 | 
			
		||||
 | 
			
		||||
    @Mock
 | 
			
		||||
    private ApplicationProperties applicationProperties;
 | 
			
		||||
 | 
			
		||||
    @Mock
 | 
			
		||||
    private ApplicationProperties.Mail mailProperties;
 | 
			
		||||
 | 
			
		||||
    @Mock
 | 
			
		||||
    private MultipartFile fileInput;
 | 
			
		||||
 | 
			
		||||
    @InjectMocks
 | 
			
		||||
    private EmailService emailService;
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    void testSendEmailWithAttachment() throws MessagingException {
 | 
			
		||||
        // Mock the values returned by ApplicationProperties
 | 
			
		||||
        when(applicationProperties.getMail()).thenReturn(mailProperties);
 | 
			
		||||
        when(mailProperties.getFrom()).thenReturn("no-reply@stirling-software.com");
 | 
			
		||||
 | 
			
		||||
        // Create a mock Email object
 | 
			
		||||
        Email email = new Email();
 | 
			
		||||
        email.setTo("test@example.com");
 | 
			
		||||
        email.setSubject("Test Email");
 | 
			
		||||
        email.setBody("This is a test email.");
 | 
			
		||||
        email.setFileInput(fileInput);
 | 
			
		||||
 | 
			
		||||
        // Mock MultipartFile behavior
 | 
			
		||||
        when(fileInput.getOriginalFilename()).thenReturn("testFile.txt");
 | 
			
		||||
 | 
			
		||||
        // Mock MimeMessage
 | 
			
		||||
        MimeMessage mimeMessage = mock(MimeMessage.class);
 | 
			
		||||
 | 
			
		||||
        // Configure mailSender to return the mocked MimeMessage
 | 
			
		||||
        when(mailSender.createMimeMessage()).thenReturn(mimeMessage);
 | 
			
		||||
 | 
			
		||||
        // Call the service method
 | 
			
		||||
        emailService.sendEmailWithAttachment(email);
 | 
			
		||||
 | 
			
		||||
        // Verify that the email was sent using mailSender
 | 
			
		||||
        verify(mailSender).send(mimeMessage);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,84 @@
 | 
			
		||||
package stirling.software.SPDF.controller.api;
 | 
			
		||||
 | 
			
		||||
import static org.mockito.Mockito.*;
 | 
			
		||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
 | 
			
		||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
 | 
			
		||||
 | 
			
		||||
import jakarta.mail.MessagingException;
 | 
			
		||||
import org.junit.jupiter.api.BeforeEach;
 | 
			
		||||
import org.junit.jupiter.api.Test;
 | 
			
		||||
import org.junit.jupiter.api.extension.ExtendWith;
 | 
			
		||||
import org.mockito.InjectMocks;
 | 
			
		||||
import org.mockito.Mock;
 | 
			
		||||
import org.mockito.junit.jupiter.MockitoExtension;
 | 
			
		||||
import org.springframework.test.web.servlet.MockMvc;
 | 
			
		||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
 | 
			
		||||
import org.springframework.web.multipart.MultipartFile;
 | 
			
		||||
import stirling.software.SPDF.config.security.mail.EmailService;
 | 
			
		||||
import stirling.software.SPDF.model.api.Email;
 | 
			
		||||
 | 
			
		||||
@ExtendWith(MockitoExtension.class)
 | 
			
		||||
public class EmailControllerTest {
 | 
			
		||||
 | 
			
		||||
    private MockMvc mockMvc;
 | 
			
		||||
 | 
			
		||||
    @Mock private EmailService emailService;
 | 
			
		||||
 | 
			
		||||
    @InjectMocks private EmailController emailController;
 | 
			
		||||
 | 
			
		||||
    @Mock private MultipartFile fileInput;
 | 
			
		||||
 | 
			
		||||
    @BeforeEach
 | 
			
		||||
    void setUp() {
 | 
			
		||||
        // Set up the MockMvc instance for testing
 | 
			
		||||
        mockMvc = MockMvcBuilders.standaloneSetup(emailController).build();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    void testSendEmailWithAttachmentSuccess() throws Exception {
 | 
			
		||||
        // Create a mock Email object
 | 
			
		||||
        Email email = new Email();
 | 
			
		||||
        email.setTo("test@example.com");
 | 
			
		||||
        email.setSubject("Test Email");
 | 
			
		||||
        email.setBody("This is a test email.");
 | 
			
		||||
        email.setFileInput(fileInput);
 | 
			
		||||
 | 
			
		||||
        // Mock the service to not throw any exception
 | 
			
		||||
        doNothing().when(emailService).sendEmailWithAttachment(any(Email.class));
 | 
			
		||||
 | 
			
		||||
        // Perform the request and verify the response
 | 
			
		||||
        mockMvc.perform(
 | 
			
		||||
                        multipart("/api/v1/general/send-email")
 | 
			
		||||
                                .file("fileInput", "dummy-content".getBytes())
 | 
			
		||||
                                .param("to", email.getTo())
 | 
			
		||||
                                .param("subject", email.getSubject())
 | 
			
		||||
                                .param("body", email.getBody()))
 | 
			
		||||
                .andExpect(status().isOk())
 | 
			
		||||
                .andExpect(content().string("Email sent successfully"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    void testSendEmailWithAttachmentFailure() throws Exception {
 | 
			
		||||
        // Create a mock Email object
 | 
			
		||||
        Email email = new Email();
 | 
			
		||||
        email.setTo("test@example.com");
 | 
			
		||||
        email.setSubject("Test Email");
 | 
			
		||||
        email.setBody("This is a test email.");
 | 
			
		||||
        email.setFileInput(fileInput);
 | 
			
		||||
 | 
			
		||||
        // Mock the service to throw a MessagingException
 | 
			
		||||
        doThrow(new MessagingException("Failed to send email"))
 | 
			
		||||
                .when(emailService)
 | 
			
		||||
                .sendEmailWithAttachment(any(Email.class));
 | 
			
		||||
 | 
			
		||||
        // Perform the request and verify the response
 | 
			
		||||
        mockMvc.perform(
 | 
			
		||||
                        multipart("/api/v1/general/send-email")
 | 
			
		||||
                                .file("fileInput", "dummy-content".getBytes())
 | 
			
		||||
                                .param("to", email.getTo())
 | 
			
		||||
                                .param("subject", email.getSubject())
 | 
			
		||||
                                .param("body", email.getBody()))
 | 
			
		||||
                .andExpect(status().isInternalServerError())
 | 
			
		||||
                .andExpect(content().string("Failed to send email: Failed to send email"));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user