From 6964e0bc8578abcbdf7e49ffa36c49197df67787 Mon Sep 17 00:00:00 2001 From: Szymon Szukalski Date: Tue, 23 Jul 2024 21:25:54 +1000 Subject: Enforce text/xml+marker content type for /import endpoint --- .../com/stileeducation/markr/config/WebConfig.java | 17 ++++++ .../markr/controller/TestResultsController.java | 4 +- .../markr/converter/XmlMarkrMessageConverter.java | 43 +++++++++++++++ src/main/resources/application.properties | 2 - .../markr/config/XmlMapperConfig.java | 14 ----- .../controller/TestResultsControllerTest.java | 62 +++++++++++++++++++--- 6 files changed, 117 insertions(+), 25 deletions(-) create mode 100644 src/main/java/com/stileeducation/markr/config/WebConfig.java create mode 100644 src/main/java/com/stileeducation/markr/converter/XmlMarkrMessageConverter.java delete mode 100644 src/test/java/com/stileeducation/markr/config/XmlMapperConfig.java diff --git a/src/main/java/com/stileeducation/markr/config/WebConfig.java b/src/main/java/com/stileeducation/markr/config/WebConfig.java new file mode 100644 index 0000000..6d9c1a0 --- /dev/null +++ b/src/main/java/com/stileeducation/markr/config/WebConfig.java @@ -0,0 +1,17 @@ +package com.stileeducation.markr.config; + +import com.stileeducation.markr.converter.XmlMarkrMessageConverter; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.util.List; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + + @Override + public void extendMessageConverters(List> converters) { + converters.add(new XmlMarkrMessageConverter()); + } +} diff --git a/src/main/java/com/stileeducation/markr/controller/TestResultsController.java b/src/main/java/com/stileeducation/markr/controller/TestResultsController.java index f5f8a44..5667027 100644 --- a/src/main/java/com/stileeducation/markr/controller/TestResultsController.java +++ b/src/main/java/com/stileeducation/markr/controller/TestResultsController.java @@ -20,8 +20,8 @@ public class TestResultsController { this.testResultsService = testResultsService; } - // TODO - update to consume text/xml+markr, consider return value - @PostMapping(value = IMPORT_ENDPOINT, consumes = "application/xml", produces = "application/json") + // TODO consider return value + @PostMapping(value = IMPORT_ENDPOINT, consumes = "text/xml+markr", produces = "application/json") public ResponseEntity handleXmlRequest(@RequestBody MCQTestResultsDTO testResults) { testResultsService.importTestResults(testResults); return new ResponseEntity<>(HttpStatus.NO_CONTENT); diff --git a/src/main/java/com/stileeducation/markr/converter/XmlMarkrMessageConverter.java b/src/main/java/com/stileeducation/markr/converter/XmlMarkrMessageConverter.java new file mode 100644 index 0000000..04207a1 --- /dev/null +++ b/src/main/java/com/stileeducation/markr/converter/XmlMarkrMessageConverter.java @@ -0,0 +1,43 @@ +package com.stileeducation.markr.converter; + +import com.stileeducation.markr.dto.MCQTestResultsDTO; +import jakarta.xml.bind.JAXBContext; +import jakarta.xml.bind.Marshaller; +import jakarta.xml.bind.Unmarshaller; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.converter.xml.AbstractXmlHttpMessageConverter; + +import javax.xml.transform.Result; +import javax.xml.transform.Source; +import java.util.Collections; +import java.util.List; + +public class XmlMarkrMessageConverter extends AbstractXmlHttpMessageConverter { + + public static final MediaType MEDIA_TYPE = new MediaType("text", "xml+markr"); + + @Override + protected MCQTestResultsDTO readFromSource(Class clazz, HttpHeaders headers, Source source) throws Exception { + JAXBContext jaxbContext = JAXBContext.newInstance(MCQTestResultsDTO.class); + Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); + return (MCQTestResultsDTO) unmarshaller.unmarshal(source); + } + + @Override + protected void writeToResult(MCQTestResultsDTO testResultsDTO, HttpHeaders headers, Result result) throws Exception { + JAXBContext jaxbContext = JAXBContext.newInstance(MCQTestResultsDTO.class); + Marshaller marshaller = jaxbContext.createMarshaller(); + marshaller.marshal(testResultsDTO, result); + } + + @Override + protected boolean supports(Class clazz) { + return MCQTestResultsDTO.class.isAssignableFrom(clazz); + } + + @Override + public List getSupportedMediaTypes() { + return Collections.singletonList(MEDIA_TYPE); + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 17a7c25..7e171c2 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,3 +1 @@ spring.application.name=markr -spring.mvc.converters.preferred-json-mapper=jackson -spring.mvc.converters.preferred-xml-mapper=jackson diff --git a/src/test/java/com/stileeducation/markr/config/XmlMapperConfig.java b/src/test/java/com/stileeducation/markr/config/XmlMapperConfig.java deleted file mode 100644 index 470c1f9..0000000 --- a/src/test/java/com/stileeducation/markr/config/XmlMapperConfig.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.stileeducation.markr.config; - -import com.fasterxml.jackson.dataformat.xml.XmlMapper; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class XmlMapperConfig { - - @Bean - public XmlMapper xmlMapper() { - return new XmlMapper(); - } -} diff --git a/src/test/java/com/stileeducation/markr/controller/TestResultsControllerTest.java b/src/test/java/com/stileeducation/markr/controller/TestResultsControllerTest.java index 41a6837..3d21c25 100644 --- a/src/test/java/com/stileeducation/markr/controller/TestResultsControllerTest.java +++ b/src/test/java/com/stileeducation/markr/controller/TestResultsControllerTest.java @@ -1,31 +1,74 @@ package com.stileeducation.markr.controller; -import com.fasterxml.jackson.dataformat.xml.XmlMapper; +import com.stileeducation.markr.converter.XmlMarkrMessageConverter; import com.stileeducation.markr.dto.AggregatedTestResultsDTO; import com.stileeducation.markr.dto.MCQTestResultsDTO; +import jakarta.xml.bind.JAXBContext; +import jakarta.xml.bind.JAXBException; +import jakarta.xml.bind.Marshaller; +import jakarta.xml.bind.Unmarshaller; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.core.io.ClassPathResource; import org.springframework.http.*; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringWriter; + import static com.stileeducation.markr.controller.TestResultsController.IMPORT_ENDPOINT; import static org.assertj.core.api.Assertions.assertThat; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class TestResultsControllerTest { - @Autowired - private XmlMapper xmlMapper; + private static MCQTestResultsDTO sampleResults; @Autowired private TestRestTemplate restTemplate; + @BeforeAll + static void setup() throws JAXBException, IOException { + ClassPathResource resource = new ClassPathResource("sample-results.xml"); + try (InputStream inputStream = resource.getInputStream()) { + JAXBContext jaxbContext = JAXBContext.newInstance(MCQTestResultsDTO.class); + Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); + sampleResults = (MCQTestResultsDTO) unmarshaller.unmarshal(inputStream); + } + } + + private static String toXmlString(MCQTestResultsDTO mcqTestResultsDTO) throws JAXBException { + JAXBContext jaxbContext = JAXBContext.newInstance(MCQTestResultsDTO.class); + Marshaller marshaller = jaxbContext.createMarshaller(); + StringWriter stringWriter = new StringWriter(); + marshaller.marshal(mcqTestResultsDTO, stringWriter); + return stringWriter.toString(); + } + @Test - public void testImport() throws Exception { + void testPost() throws Exception { + // Given + String requestXml = toXmlString(sampleResults); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(XmlMarkrMessageConverter.MEDIA_TYPE); + + // When + HttpEntity entity = new HttpEntity<>(requestXml, headers); + ResponseEntity response = restTemplate.postForEntity(IMPORT_ENDPOINT, entity, String.class); + + // Then + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT); + } + + @Test + void testUnsupportedMediaType() throws Exception { // Given MCQTestResultsDTO request = new MCQTestResultsDTO(); - String requestXml = xmlMapper.writeValueAsString(request); + String requestXml = toXmlString(request); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_XML); @@ -35,7 +78,7 @@ public class TestResultsControllerTest { ResponseEntity response = restTemplate.postForEntity(IMPORT_ENDPOINT, entity, String.class); // Then - assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT); + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.UNSUPPORTED_MEDIA_TYPE); } @Test @@ -45,7 +88,11 @@ public class TestResultsControllerTest { String url = "/results/" + testId + "/aggregate"; // When - ResponseEntity response = restTemplate.getForEntity(url, AggregatedTestResultsDTO.class); + ResponseEntity response = + restTemplate + .getForEntity( + url, + AggregatedTestResultsDTO.class); // Then assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); @@ -62,4 +109,5 @@ public class TestResultsControllerTest { assertThat(results.getCount()).isEqualTo(1); } + } -- cgit v1.2.3