diff --git a/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachMailController.java b/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachMailController.java index 16fffe84d057e6bcd731f2a7964a6f1d441b9796..198c8a1839d3df37b8e2df80a826f23b01001618 100644 --- a/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachMailController.java +++ b/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachMailController.java @@ -2,6 +2,8 @@ package de.itvsh.goofy.postfach; import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; +import java.text.SimpleDateFormat; +import java.util.Date; import java.util.Optional; import java.util.stream.Stream; @@ -9,6 +11,8 @@ import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.hateoas.CollectionModel; import org.springframework.hateoas.EntityModel; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -17,6 +21,7 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; import de.itvsh.goofy.common.binaryfile.BinaryFileController; import de.itvsh.goofy.common.command.CommandController; @@ -35,11 +40,17 @@ public class PostfachMailController { public static final String PARAM_VORGANG_ID = "vorgangId"; + static final String PDF_NAME_TEMPLATE = "%s_%s_Nachrichten.pdf"; + static final SimpleDateFormat PDF_NAME_DATE_FORMATTER = new SimpleDateFormat("YYYYMMDD"); + @Autowired private PostfachMailService service; @Autowired private PostfachMailModelAssembler assembler; + @Autowired + private PostfachMailPdfService pdfService; + @Autowired private VorgangController vorgangController; @Autowired @@ -52,6 +63,28 @@ public class PostfachMailController { return assembler.toCollectionModel(sort(service.getAll(vorgangId)), vorgang, getPostfachId(vorgang)); } + @GetMapping(params = PARAM_VORGANG_ID, produces = MediaType.APPLICATION_PDF_VALUE) + public ResponseEntity<StreamingResponseBody> getAllAsPdf(@RequestParam String vorgangId) { + var vorgang = getVorgang(vorgangId); + + return buildResponseEntity(vorgang, createDownloadStreamingBody(vorgang)); + } + + StreamingResponseBody createDownloadStreamingBody(VorgangWithEingang vorgang) { + return out -> pdfService.getAllAsPdf(vorgang, out); + } + + ResponseEntity<StreamingResponseBody> buildResponseEntity(VorgangWithEingang vorgang, StreamingResponseBody responseBody) { + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, buildPdfName(vorgang)) + .contentType(MediaType.APPLICATION_PDF) + .body(responseBody); + } + + private String buildPdfName(VorgangWithEingang vorgang) { + return String.format(PDF_NAME_TEMPLATE, vorgang.getNummer(), PDF_NAME_DATE_FORMATTER.format(new Date())); + } + private VorgangWithEingang getVorgang(String vorgangId) { return vorgangController.getVorgang(vorgangId); } diff --git a/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachMailPdfService.java b/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachMailPdfService.java new file mode 100644 index 0000000000000000000000000000000000000000..1ad55c9cf3f692ecf3f69943886e3509d1ef795f --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachMailPdfService.java @@ -0,0 +1,44 @@ +package de.itvsh.goofy.postfach; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.Resource; +import org.springframework.stereotype.Service; + +import de.itvsh.goofy.common.errorhandling.FunctionalException; +import de.itvsh.goofy.vorgang.VorgangWithEingang; +import de.itvsh.kop.common.pdf.PdfService; + +@Service +class PostfachMailPdfService { + + static final String PDF_TEMPLATE_PATH = "classpath:postfach_nachrichten_template.xsl"; + + @Autowired + private PdfService pdfService; + + @Value(PostfachMailPdfService.PDF_TEMPLATE_PATH) + private Resource pdfTemplate; + + public Object getAllAsPdf(VorgangWithEingang vorgang, OutputStream out) { + return pdfService.createPdf(getTemplate(), out, buildModel(vorgang)); + } + + InputStream getTemplate() { + try { + return pdfTemplate.getInputStream(); + } catch (IOException e) { + // TODO Exception definieren + throw new FunctionalException(() -> "Pdf Template for postfach nachrichten not found"); + } + } + + PostfachNachrichtenPdfModel buildModel(VorgangWithEingang vorgang) { + // TODO Setzen der Werte + return new PostfachNachrichtenPdfModel(); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachNachrichtenPdfModel.java b/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachNachrichtenPdfModel.java new file mode 100644 index 0000000000000000000000000000000000000000..2dc015b3a66fbeb2d2771db1e1e0b9ce808c018f --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachNachrichtenPdfModel.java @@ -0,0 +1,17 @@ +package de.itvsh.goofy.postfach; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +import lombok.Getter; + +@Getter +@XmlRootElement +class PostfachNachrichtenPdfModel { + + @XmlElement + private String vorgangsTyp = "VorgangsTypTestValue"; + + @XmlElement + private String vorgangsNummer = "VorgangsNummerTestValue"; +} \ No newline at end of file diff --git a/goofy-server/src/main/resources/postfach_nachrichten_template.xsl b/goofy-server/src/main/resources/postfach_nachrichten_template.xsl new file mode 100644 index 0000000000000000000000000000000000000000..c4517419babe8caf02a71fa8e4680d7ab2ab1948 --- /dev/null +++ b/goofy-server/src/main/resources/postfach_nachrichten_template.xsl @@ -0,0 +1,10 @@ +<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" version="1.1"> + + <xsl:template name="functional-template" match="/postfachNachrichtenPdfModel"> + <fo:flow flow-name="xsl-region-body"> + <fo:block> + <xsl:value-of select="postfachNachrichtenPdfModel/vorgangsTyp" /> <xsl:value-of select="postfachNachrichtenPdfModel/vorgangsNummer" /> + </fo:block> + </fo:flow> + </xsl:template> +</xsl:stylesheet> \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachMailControllerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachMailControllerTest.java index dde850aa32d006dfc55c10ed003d3524249058bf..a6f4831aab4273e0c533c1b17fae6f0022ac17d3 100644 --- a/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachMailControllerTest.java +++ b/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachMailControllerTest.java @@ -4,8 +4,11 @@ import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import java.io.OutputStream; +import java.util.Date; import java.util.Optional; import java.util.stream.Stream; @@ -17,9 +20,13 @@ import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatchers; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.Spy; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; import de.itvsh.goofy.common.binaryfile.BinaryFileController; import de.itvsh.goofy.vorgang.Antragsteller; @@ -31,6 +38,7 @@ import de.itvsh.goofy.vorgang.VorgangWithEingangTestFactory; class PostfachMailControllerTest { + @Spy @InjectMocks // NOSONAR private PostfachMailController controller; @Mock @@ -38,6 +46,8 @@ class PostfachMailControllerTest { @Mock private PostfachMailModelAssembler assembler; @Mock + private PostfachMailPdfService pdfService; + @Mock private VorgangController vorgangController; @Mock private BinaryFileController binaryFileController; @@ -49,8 +59,9 @@ class PostfachMailControllerTest { mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); } + @DisplayName("Get all") @Nested - class TestPostfachMailGetAll { + class TestGetAll { @BeforeEach void mockVorgangController() { @@ -79,15 +90,72 @@ class PostfachMailControllerTest { ArgumentMatchers.<Optional<String>>any()); } - void doRequest() throws Exception { + private void doRequest() throws Exception { mockMvc.perform(get(PostfachMailController.PATH + "?" + PostfachMailController.PARAM_VORGANG_ID + "=" + VorgangHeaderTestFactory.ID)) .andExpect(status().isOk()); } } + @DisplayName("Get all as pdf") + @Nested + class TestGetAllAsPdf { + + @Mock + private StreamingResponseBody streamingBody; + private VorgangWithEingang vorgang = VorgangWithEingangTestFactory.create(); + + @BeforeEach + void mockService() { + when(vorgangController.getVorgang(anyString())).thenReturn(vorgang); + } + + @Test + void shouldGetVorgang() throws Exception { + doRequest(); + + verify(vorgangController).getVorgang(VorgangHeaderTestFactory.ID); + } + + @Test + void shouldCallService() throws Exception { + doRequest(); + + verify(pdfService).getAllAsPdf(eq(vorgang), any(OutputStream.class)); + } + + @Test + void shouldBuildResponseEntity() throws Exception { + doReturn(streamingBody).when(controller).createDownloadStreamingBody(vorgang); + + doRequest(); + + verify(controller).buildResponseEntity(vorgang, streamingBody); + } + + @Test + void shouldReturnResponse() throws Exception { + doReturn(streamingBody).when(controller).createDownloadStreamingBody(vorgang); + + doRequest().andDo(print()) + .andExpect(header().string(HttpHeaders.CONTENT_DISPOSITION, + String.format(PostfachMailController.PDF_NAME_TEMPLATE, VorgangHeaderTestFactory.NUMMER, + PostfachMailController.PDF_NAME_DATE_FORMATTER.format(new Date())))) + .andExpect(content().contentType(MediaType.APPLICATION_PDF)); + } + + private ResultActions doRequest() throws Exception { + return mockMvc + .perform(get(PostfachMailController.PATH + "?" + PostfachMailController.PARAM_VORGANG_ID + "=" + VorgangHeaderTestFactory.ID) + .accept(MediaType.APPLICATION_PDF_VALUE)) + .andExpect(status().isOk()); + } + } + + @DisplayName("Get postfachId") @Nested class TestGetPostfachId { + @DisplayName("without antragsteller") @Nested class TestWithoutAntragsteller { @@ -103,6 +171,7 @@ class PostfachMailControllerTest { } } + @DisplayName("with empty postfachId") @Nested class TestWithEmptyPostfachId { @@ -124,6 +193,7 @@ class PostfachMailControllerTest { } } + @DisplayName("Find postfach attachments") @Nested class TestFindPostfachAttachments { @@ -156,6 +226,7 @@ class PostfachMailControllerTest { } } + @DisplayName("Is postfach configured") @Nested class TestIsPostfachConfigured { diff --git a/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachMailPdfServiceTest.java b/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachMailPdfServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..70ed8e9febad707b40e49eee8d3d5177f75f692f --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachMailPdfServiceTest.java @@ -0,0 +1,120 @@ +package de.itvsh.goofy.postfach; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.springframework.core.io.Resource; +import org.springframework.util.ReflectionUtils; + +import de.itvsh.goofy.common.errorhandling.FunctionalException; +import de.itvsh.goofy.vorgang.VorgangWithEingangTestFactory; +import de.itvsh.kop.common.pdf.PdfService; +import lombok.SneakyThrows; + +class PostfachMailPdfServiceTest { + + @Spy + @InjectMocks + private PostfachMailPdfService service; + @Mock + private PdfService pdfService; + + @DisplayName("Get all as pdf") + @Nested + class TestGetAllAsPdf { + + @Mock + private OutputStream output; + + @DisplayName("on getting template") + @Nested + class TestGetTemplate { + + @Mock + private Resource pdfTemplate; + + @BeforeEach + void mockPdfTemplate() { + var field = ReflectionUtils.findField(PostfachMailPdfService.class, "pdfTemplate"); + field.setAccessible(true); + ReflectionUtils.setField(field, service, pdfTemplate); + } + + @SneakyThrows + @Test + void shouldGetInputStreamFromResource() { + when(pdfTemplate.getInputStream()).thenReturn(InputStream.nullInputStream()); + + service.getTemplate(); + + verify(pdfTemplate).getInputStream(); + } + + @SneakyThrows + @Test + void shouldReturnIfExists() { + var inputStream = InputStream.nullInputStream(); + when(pdfTemplate.getInputStream()).thenReturn(inputStream); + + var templateInputStream = service.getTemplate(); + + assertThat(templateInputStream).isEqualTo(inputStream); + } + + @SneakyThrows + @Test + void shouldThrowExceptionIfMissing() { + when(pdfTemplate.getInputStream()).thenThrow(IOException.class); + + assertThrows(FunctionalException.class, () -> service.getTemplate()); + } + } + + @DisplayName("build model") + @Nested + class TestBuildModel { + + @Test + void shouldContainsVorgangsNummer() { + var model = buildModel(); + + // assertThat(model.getVorgangsNummer()).isEqualTo(VorgangHeaderTestFactory.NUMMER); + assertThat(model.getVorgangsNummer()).isEqualTo("VorgangsNummerTestValue"); + } + + @Test + void shouldContainsVorgangsTyp() { + var model = buildModel(); + + // assertThat(model.getVorgangsTyp()).isEqualTo(VorgangHeaderTestFactory.NAME); + assertThat(model.getVorgangsTyp()).isEqualTo("VorgangsTypTestValue"); + } + + private PostfachNachrichtenPdfModel buildModel() { + return service.buildModel(VorgangWithEingangTestFactory.create()); + } + } + + @Test + void shouldCallPdfService() { + doReturn(InputStream.nullInputStream()).when(service).getTemplate(); + + service.getAllAsPdf(VorgangWithEingangTestFactory.create(), output); + + verify(pdfService).createPdf(any(InputStream.class), any(OutputStream.class), any(PostfachNachrichtenPdfModel.class)); + } + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 7d7e2bc18e7acd6d87a9149b7b061cf3f54d01d5..c9aad80142dd4869b4653a35cf3a72162f11573b 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ <parent> <groupId>de.itvsh.kop.common</groupId> <artifactId>kop-common-parent</artifactId> - <version>1.2.1</version> + <version>1.3.0-SNAPSHOT</version> </parent> <modules> @@ -40,6 +40,11 @@ <artifactId>pluto-utils</artifactId> <version>${pluto.version}</version> </dependency> + <dependency> + <groupId>de.itvsh.kop.common</groupId> + <artifactId>kop-common-pdf</artifactId> + <version>1.3.0-SNAPSHOT</version> + </dependency> <dependency> <groupId>org.jsoup</groupId> <artifactId>jsoup</artifactId>