mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-15 11:00:47 +02:00
Add Attachments Feature (#3781)
# Description of Changes Added a new feature to add attachments to a PDF document. ### Added: - `AttachmentController`: Endpoint for adding attachments at `/add-attachments` with parameters `fileInput` for the PDF and `attachments` as a list of files to attach - `AttachmentServiceInterface` - `AttachmentService`: Handles the logic of adding attachments to the PDF - `AttachmentUtils`: to handle setting the catalog viewer preferences in the viewer - Add Attachments page - Tests for new feature ### Changes: - `EmlToPdf`: Moved setting of viewer preferences to `AttachmentUtils` - `EndpointConfiguration: Included '/add-attachments' - Updated language files with attachments copy - General clean up Closes #1259 --- ## 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) - [x] 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 - [x] 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) - [x] 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)     - [x] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [x] 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:
+133
@@ -0,0 +1,133 @@
|
||||
package stirling.software.SPDF.controller.api.misc;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import org.mockito.MockedStatic;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
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.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import stirling.software.SPDF.model.api.misc.AddAttachmentRequest;
|
||||
import stirling.software.SPDF.service.AttachmentServiceInterface;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
import stirling.software.common.util.WebResponseUtils;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class AttachmentControllerTest {
|
||||
|
||||
@Mock
|
||||
private CustomPDFDocumentFactory pdfDocumentFactory;
|
||||
|
||||
@Mock
|
||||
private AttachmentServiceInterface pdfAttachmentService;
|
||||
|
||||
@InjectMocks
|
||||
private AttachmentController attachmentController;
|
||||
|
||||
private MockMultipartFile pdfFile;
|
||||
private MockMultipartFile attachment1;
|
||||
private MockMultipartFile attachment2;
|
||||
private AddAttachmentRequest request;
|
||||
private PDDocument mockDocument;
|
||||
private PDDocument modifiedMockDocument;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
pdfFile = new MockMultipartFile("fileInput", "test.pdf", "application/pdf", "PDF content".getBytes());
|
||||
attachment1 = new MockMultipartFile("attachment1", "file1.txt", "text/plain", "File 1 content".getBytes());
|
||||
attachment2 = new MockMultipartFile("attachment2", "file2.jpg", "image/jpeg", "Image content".getBytes());
|
||||
request = new AddAttachmentRequest();
|
||||
mockDocument = mock(PDDocument.class);
|
||||
modifiedMockDocument = mock(PDDocument.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void addAttachments_Success() throws IOException {
|
||||
List<MultipartFile> attachments = List.of(attachment1, attachment2);
|
||||
request.setAttachments(attachments);
|
||||
request.setFileInput(pdfFile);
|
||||
ResponseEntity<byte[]> expectedResponse = ResponseEntity.ok("modified PDF content".getBytes());
|
||||
|
||||
when(pdfDocumentFactory.load(pdfFile, false)).thenReturn(mockDocument);
|
||||
when(pdfAttachmentService.addAttachment(mockDocument, attachments)).thenReturn(modifiedMockDocument);
|
||||
|
||||
try (MockedStatic<WebResponseUtils> mockedWebResponseUtils = mockStatic(WebResponseUtils.class)) {
|
||||
mockedWebResponseUtils.when(() -> WebResponseUtils.pdfDocToWebResponse(eq(modifiedMockDocument), eq("test_with_attachments.pdf")))
|
||||
.thenReturn(expectedResponse);
|
||||
|
||||
ResponseEntity<byte[]> response = attachmentController.addAttachments(request);
|
||||
|
||||
assertNotNull(response);
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertNotNull(response.getBody());
|
||||
verify(pdfDocumentFactory).load(pdfFile, false);
|
||||
verify(pdfAttachmentService).addAttachment(mockDocument, attachments);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void addAttachments_SingleAttachment() throws IOException {
|
||||
List<MultipartFile> attachments = List.of(attachment1);
|
||||
request.setAttachments(attachments);
|
||||
request.setFileInput(pdfFile);
|
||||
ResponseEntity<byte[]> expectedResponse = ResponseEntity.ok("modified PDF content".getBytes());
|
||||
|
||||
when(pdfDocumentFactory.load(pdfFile, false)).thenReturn(mockDocument);
|
||||
when(pdfAttachmentService.addAttachment(mockDocument, attachments)).thenReturn(modifiedMockDocument);
|
||||
|
||||
try (MockedStatic<WebResponseUtils> mockedWebResponseUtils = mockStatic(WebResponseUtils.class)) {
|
||||
mockedWebResponseUtils.when(() -> WebResponseUtils.pdfDocToWebResponse(eq(modifiedMockDocument), eq("test_with_attachments.pdf")))
|
||||
.thenReturn(expectedResponse);
|
||||
|
||||
ResponseEntity<byte[]> response = attachmentController.addAttachments(request);
|
||||
|
||||
assertNotNull(response);
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertNotNull(response.getBody());
|
||||
verify(pdfDocumentFactory).load(pdfFile, false);
|
||||
verify(pdfAttachmentService).addAttachment(mockDocument, attachments);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void addAttachments_IOExceptionFromPDFLoad() throws IOException {
|
||||
List<MultipartFile> attachments = List.of(attachment1);
|
||||
request.setAttachments(attachments);
|
||||
request.setFileInput(pdfFile);
|
||||
IOException ioException = new IOException("Failed to load PDF");
|
||||
|
||||
when(pdfDocumentFactory.load(pdfFile, false)).thenThrow(ioException);
|
||||
|
||||
assertThrows(IOException.class, () -> attachmentController.addAttachments(request));
|
||||
verify(pdfDocumentFactory).load(pdfFile, false);
|
||||
verifyNoInteractions(pdfAttachmentService);
|
||||
}
|
||||
|
||||
@Test
|
||||
void addAttachments_IOExceptionFromAttachmentService() throws IOException {
|
||||
List<MultipartFile> attachments = List.of(attachment1);
|
||||
request.setAttachments(attachments);
|
||||
request.setFileInput(pdfFile);
|
||||
IOException ioException = new IOException("Failed to add attachment");
|
||||
|
||||
when(pdfDocumentFactory.load(pdfFile, false)).thenReturn(mockDocument);
|
||||
when(pdfAttachmentService.addAttachment(mockDocument, attachments)).thenThrow(ioException);
|
||||
|
||||
assertThrows(IOException.class, () -> attachmentController.addAttachments(request));
|
||||
verify(pdfAttachmentService).addAttachment(mockDocument, attachments);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package stirling.software.SPDF.service;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class AttachmentServiceTest {
|
||||
|
||||
private AttachmentService attachmentService;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
attachmentService = new AttachmentService();
|
||||
}
|
||||
|
||||
@Test
|
||||
void addAttachmentToPDF() throws IOException {
|
||||
try (var document = new PDDocument()) {
|
||||
document.setDocumentId(100L);
|
||||
var attachments = List.of(mock(MultipartFile.class));
|
||||
|
||||
when(attachments.get(0).getOriginalFilename()).thenReturn("test.txt");
|
||||
when(attachments.get(0).getInputStream()).thenReturn(
|
||||
new ByteArrayInputStream("Test content".getBytes()));
|
||||
when(attachments.get(0).getSize()).thenReturn(12L);
|
||||
when(attachments.get(0).getContentType()).thenReturn("text/plain");
|
||||
|
||||
PDDocument result = attachmentService.addAttachment(document, attachments);
|
||||
|
||||
assertNotNull(result);
|
||||
assertEquals(document.getDocumentId(), result.getDocumentId());
|
||||
assertNotNull(result.getDocumentCatalog().getNames());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void addAttachmentToPDF_MultipleAttachments() throws IOException {
|
||||
try (var document = new PDDocument()) {
|
||||
document.setDocumentId(100L);
|
||||
var attachment1 = mock(MultipartFile.class);
|
||||
var attachment2 = mock(MultipartFile.class);
|
||||
var attachments = List.of(attachment1, attachment2);
|
||||
|
||||
when(attachment1.getOriginalFilename()).thenReturn("document.pdf");
|
||||
when(attachment1.getInputStream()).thenReturn(
|
||||
new ByteArrayInputStream("PDF content".getBytes()));
|
||||
when(attachment1.getSize()).thenReturn(15L);
|
||||
when(attachment1.getContentType()).thenReturn("application/pdf");
|
||||
|
||||
when(attachment2.getOriginalFilename()).thenReturn("image.jpg");
|
||||
when(attachment2.getInputStream()).thenReturn(
|
||||
new ByteArrayInputStream("Image content".getBytes()));
|
||||
when(attachment2.getSize()).thenReturn(20L);
|
||||
when(attachment2.getContentType()).thenReturn("image/jpeg");
|
||||
|
||||
PDDocument result = attachmentService.addAttachment(document, attachments);
|
||||
|
||||
assertNotNull(result);
|
||||
assertNotNull(result.getDocumentCatalog().getNames());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void addAttachmentToPDF_WithBlankContentType() throws IOException {
|
||||
try (var document = new PDDocument()) {
|
||||
document.setDocumentId(100L);
|
||||
var attachments = List.of(mock(MultipartFile.class));
|
||||
|
||||
when(attachments.get(0).getOriginalFilename()).thenReturn("image.jpg");
|
||||
when(attachments.get(0).getInputStream()).thenReturn(
|
||||
new ByteArrayInputStream("Image content".getBytes()));
|
||||
when(attachments.get(0).getSize()).thenReturn(25L);
|
||||
when(attachments.get(0).getContentType()).thenReturn("");
|
||||
|
||||
PDDocument result = attachmentService.addAttachment(document, attachments);
|
||||
|
||||
assertNotNull(result);
|
||||
assertNotNull(result.getDocumentCatalog().getNames());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void addAttachmentToPDF_AttachmentInputStreamThrowsIOException() throws IOException {
|
||||
try (var document = new PDDocument()) {
|
||||
var attachments = List.of(mock(MultipartFile.class));
|
||||
var ioException = new IOException("Failed to read attachment stream");
|
||||
|
||||
when(attachments.get(0).getOriginalFilename()).thenReturn("test.txt");
|
||||
when(attachments.get(0).getInputStream()).thenThrow(ioException);
|
||||
when(attachments.get(0).getSize()).thenReturn(10L);
|
||||
|
||||
PDDocument result = attachmentService.addAttachment(document, attachments);
|
||||
|
||||
assertNotNull(result);
|
||||
assertNotNull(result.getDocumentCatalog().getNames());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user