diff --git a/build.gradle b/build.gradle index 393e73f..7a0f03b 100644 --- a/build.gradle +++ b/build.gradle @@ -2,6 +2,7 @@ plugins { id 'org.springframework.boot' version '3.1.2' id 'io.spring.dependency-management' version '1.1.0' id 'java' + id("io.freefair.lombok") version "8.7.1" } targetCompatibility = '17' @@ -14,6 +15,10 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' testImplementation('org.springframework.boot:spring-boot-starter-test') + + implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.15.2' + implementation 'com.fasterxml.jackson:jackson-bom:2.15.2' +// implementation 'jakarta.xml.bind:jakarta.xml.bind-api:4.0.0' } test { diff --git a/src/main/java/com/wilkins/showcase/ShowcaseApplication.java b/src/main/java/com/wilkins/showcase/ShowcaseApplication.java index e2e2586..a2a21eb 100644 --- a/src/main/java/com/wilkins/showcase/ShowcaseApplication.java +++ b/src/main/java/com/wilkins/showcase/ShowcaseApplication.java @@ -1,12 +1,15 @@ package com.wilkins.showcase; +import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +@Slf4j @SpringBootApplication public class ShowcaseApplication { public static void main(String[] args) { SpringApplication.run(ShowcaseApplication.class, args); } + } diff --git a/src/main/java/com/wilkins/showcase/WebConfig.java b/src/main/java/com/wilkins/showcase/WebConfig.java new file mode 100644 index 0000000..2f58d8e --- /dev/null +++ b/src/main/java/com/wilkins/showcase/WebConfig.java @@ -0,0 +1,15 @@ +package com.wilkins.showcase; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.web.client.RestTemplateCustomizer; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Slf4j +@Configuration +public class WebConfig implements RestTemplateCustomizer { + @Override + public void customize(RestTemplate restTemplate) { + log.info("Rest template message converters: {}", restTemplate.getMessageConverters()); + } +} diff --git a/src/main/java/com/wilkins/showcase/controller/DownloadController.java b/src/main/java/com/wilkins/showcase/controller/DownloadController.java new file mode 100644 index 0000000..47036b4 --- /dev/null +++ b/src/main/java/com/wilkins/showcase/controller/DownloadController.java @@ -0,0 +1,37 @@ +package com.wilkins.showcase.controller; + +import org.springframework.http.ContentDisposition; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; + +import java.io.IOException; +import java.io.OutputStream; + +import static org.springframework.http.HttpHeaders.CONTENT_DISPOSITION; +import static org.springframework.http.ResponseEntity.ok; + +@RestController +@RequestMapping("/download") +public class DownloadController { + @GetMapping(produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) + public ResponseEntity download() { + return ok() + .header(CONTENT_DISPOSITION, ContentDisposition + .builder("pdf") + .filename("demo.pdf") + .build().toString()) + .body(this::writeFileToStream); + } + + private void writeFileToStream(OutputStream outputStream) { + try (var inputStream = DownloadController.class.getClassLoader().getResourceAsStream("Test.pdf")) { + outputStream.write(inputStream.readAllBytes()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/main/java/com/wilkins/showcase/controller/Person.java b/src/main/java/com/wilkins/showcase/controller/Person.java new file mode 100644 index 0000000..088a057 --- /dev/null +++ b/src/main/java/com/wilkins/showcase/controller/Person.java @@ -0,0 +1,6 @@ +package com.wilkins.showcase.controller; + +import java.math.BigDecimal; + +public record Person(String firstName, String lastName, BigDecimal salary) { +} diff --git a/src/main/java/com/wilkins/showcase/controller/UploadController.java b/src/main/java/com/wilkins/showcase/controller/UploadController.java new file mode 100644 index 0000000..eb03080 --- /dev/null +++ b/src/main/java/com/wilkins/showcase/controller/UploadController.java @@ -0,0 +1,33 @@ +package com.wilkins.showcase.controller; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.math.BigDecimal; + +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; +import static org.springframework.http.MediaType.APPLICATION_XML_VALUE; + +@RequestMapping("/upload") +@RestController +@Slf4j +public class UploadController { + + @PostMapping(consumes = {APPLICATION_XML_VALUE, "application/xml;charset=UTF-8"}) + public ResponseEntity uploadXml(@RequestBody Person person) { + log.info("xml person: {}", person); + var personWithSalary = new Person(person.firstName(), person.lastName(), BigDecimal.valueOf(123456789000000.0)); + return ResponseEntity.ok(personWithSalary); + } + + @PostMapping(consumes = APPLICATION_JSON_VALUE, produces = APPLICATION_JSON_VALUE) + public ResponseEntity uploadJson(@RequestBody Person person) { + log.info("json person: {}", person); + var personWithSalary = new Person(person.firstName(), person.lastName(), BigDecimal.valueOf(123456789.0)); + return ResponseEntity.ok(personWithSalary); + } +} diff --git a/src/main/resources/Test.pdf b/src/main/resources/Test.pdf new file mode 100644 index 0000000..cef2df0 Binary files /dev/null and b/src/main/resources/Test.pdf differ diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 8d50475..664b58c 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,3 +1,8 @@ +spring: + jackson: + serialization: + WRITE_BIGDECIMAL_AS_PLAIN: true + server: port: 8080 servlet: diff --git a/src/main/resources/public/index.html b/src/main/resources/public/index.html new file mode 100644 index 0000000..dc15662 --- /dev/null +++ b/src/main/resources/public/index.html @@ -0,0 +1,4 @@ + +

Demo

+Download + \ No newline at end of file diff --git a/src/test/java/com/wilkins/showcase/controller/DownloadControllerTest.java b/src/test/java/com/wilkins/showcase/controller/DownloadControllerTest.java new file mode 100644 index 0000000..06574c2 --- /dev/null +++ b/src/test/java/com/wilkins/showcase/controller/DownloadControllerTest.java @@ -0,0 +1,30 @@ +package com.wilkins.showcase.controller; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.log; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class DownloadControllerTest { + + MockMvc mockMvc; + + @BeforeEach + void beforeEach() { + mockMvc = MockMvcBuilders.standaloneSetup(new DownloadController()).build(); + } + + @Test + void canDownloadFile() throws Exception { + var result = mockMvc.perform(MockMvcRequestBuilders.get("/download")) + .andExpect(status().isOk()) + .andExpect(request().asyncStarted()) + .andDo(log()) + .andReturn(); + } +} \ No newline at end of file diff --git a/src/test/java/com/wilkins/showcase/controller/UploadControllerTest.java b/src/test/java/com/wilkins/showcase/controller/UploadControllerTest.java new file mode 100644 index 0000000..6b1efec --- /dev/null +++ b/src/test/java/com/wilkins/showcase/controller/UploadControllerTest.java @@ -0,0 +1,69 @@ +package com.wilkins.showcase.controller; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.dataformat.xml.XmlMapper; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.http.MediaType; +import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class UploadControllerTest { + + MockMvc mockMvc; + + @BeforeEach + void setUp() { + var messageConverter = new MappingJackson2XmlHttpMessageConverter(); + var xmlMapper = new XmlMapper(); + xmlMapper.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN); + xmlMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); + messageConverter.setObjectMapper(xmlMapper); + mockMvc = MockMvcBuilders + .standaloneSetup(new UploadController()) + .setMessageConverters(messageConverter) + .build(); + } + + @Test + void canUploadXml() throws Exception { + var response = mockMvc.perform(post("/upload") + .contentType(MediaType.APPLICATION_XML) + .content(""" + + Bob + Fish + + """)) + .andExpect(status().isOk()) + .andReturn() + .getResponse() + .getContentAsString(); + + assertThat(response).doesNotContain("E+"); + } + + @Test + void canUploadXmlWithEmptyValues() throws Exception { + var response = mockMvc.perform(post("/upload") + .contentType(MediaType.APPLICATION_XML) + .content(""" + + Bob + + + """)) + .andExpect(status().isOk()) + .andReturn() + .getResponse() + .getContentAsString(); + + assertThat(response).doesNotContain("E+"); + } +} \ No newline at end of file