diff --git a/src/main/java/ch/psi/daq/queryrest/config/QueryRestConfig.java b/src/main/java/ch/psi/daq/queryrest/config/QueryRestConfig.java index 0c85ba1..a26fbc3 100644 --- a/src/main/java/ch/psi/daq/queryrest/config/QueryRestConfig.java +++ b/src/main/java/ch/psi/daq/queryrest/config/QueryRestConfig.java @@ -2,6 +2,7 @@ package ch.psi.daq.queryrest.config; import java.util.EnumMap; import java.util.LinkedHashSet; +import java.util.List; import java.util.Set; import java.util.function.Function; @@ -14,8 +15,11 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.PropertySource; import org.springframework.core.env.Environment; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.util.StringUtils; import org.springframework.validation.Validator; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.core.JsonFactory; @@ -42,7 +46,7 @@ import ch.psi.daq.queryrest.response.ResponseStreamWriter; @Configuration @PropertySource(value = {"classpath:queryrest.properties"}) @PropertySource(value = {"file:${user.home}/.config/daq/queryrest.properties"}, ignoreResourceNotFound = true) -public class QueryRestConfig { +public class QueryRestConfig extends WebMvcConfigurerAdapter { private static final String QUERYREST_DEFAULT_RESPONSE_AGGREGATIONS = "queryrest.default.response.aggregations"; @@ -65,6 +69,21 @@ public class QueryRestConfig { @Resource private Environment env; + /** + * {@inheritDoc} + */ + @Override + public void configureMessageConverters(List> converters) { + final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); + /** + * This is necessary so that the message conversion uses the configured object mapper. + * Otherwise, a separate object mapper is instantiated for Springs message conversion. + */ + converter.setObjectMapper(objectMapper()); + converters.add(converter); + super.configureMessageConverters(converters); + } + @Bean public ObjectMapper objectMapper() { ObjectMapper mapper = new ObjectMapper(); @@ -92,7 +111,7 @@ public class QueryRestConfig { return mapper; } - + @Bean public JsonFactory jsonFactory() { return new JsonFactory(); diff --git a/src/main/java/ch/psi/daq/queryrest/controller/QueryRestController.java b/src/main/java/ch/psi/daq/queryrest/controller/QueryRestController.java index d8093f4..9f84211 100644 --- a/src/main/java/ch/psi/daq/queryrest/controller/QueryRestController.java +++ b/src/main/java/ch/psi/daq/queryrest/controller/QueryRestController.java @@ -14,6 +14,9 @@ import javax.validation.Valid; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.MediaType; +import org.springframework.validation.Validator; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @@ -40,6 +43,9 @@ public class QueryRestController { public static final String CHANNELS_REGEX = CHANNELS + "/{regex}"; public static final String QUERY = "query"; + @Resource + private Validator queryValidator; + @Resource private ResponseStreamWriter responseStreamWriter; @@ -55,10 +61,19 @@ public class QueryRestController { @Resource(name = QueryRestConfig.BEAN_NAME_DEFAULT_RESPONSE_AGGREGATIONS) private Set defaultResponseAggregations; + + @InitBinder + protected void initBinder(WebDataBinder binder) { + /* + * This allows to use the @Valid annotation in the methods below. + */ + binder.addValidators(queryValidator); + } + @RequestMapping( value = CHANNELS, - method = RequestMethod.GET, - produces = {MediaType.APPLICATION_JSON_VALUE}) + consumes = {MediaType.APPLICATION_JSON_VALUE}, + method = RequestMethod.GET) public @ResponseBody Collection getChannels() throws Throwable { try { return queryProcessor.getChannels(); @@ -71,8 +86,7 @@ public class QueryRestController { @RequestMapping( value = CHANNELS_REGEX, method = RequestMethod.POST, - consumes = {MediaType.APPLICATION_JSON_VALUE}, - produces = {MediaType.APPLICATION_JSON_VALUE}) + consumes = {MediaType.APPLICATION_JSON_VALUE}) public @ResponseBody Collection getChannels(@RequestBody String regex) throws Throwable { try { return queryProcessor.getChannels(regex); @@ -86,16 +100,13 @@ public class QueryRestController { value = QUERY, method = RequestMethod.POST, consumes = {MediaType.APPLICATION_JSON_VALUE}) -// , produces = {MediaType.APPLICATION_JSON_VALUE}) public void executeQuery(@RequestBody @Valid AbstractQuery query, HttpServletResponse res) throws IOException { try { - LOGGER.debug("Execute query '{}'", query.getClass().getSimpleName()); + LOGGER.debug("Executing query '{}'", query.getClass().getSimpleName()); QueryAnalyzer queryAnalizer = queryAnalizerFactory.apply(query); queryAnalizer.validate(); -// extendQuery(query); - // all the magic happens here Stream>> channelToDataEvents = queryProcessor.process(queryAnalizer); @@ -110,14 +121,6 @@ public class QueryRestController { } } -// private void extendQuery(AbstractQuery query) { -// if (query.getFields() == null || query.getFields().isEmpty()) { -// query.setFields(new LinkedHashSet<>(defaultResponseFields)); -// } -// if(query.getAggregations() == null || query.getAggregations().isEmpty()){ -// query.setAggregations(new LinkedList<>(defaultResponseAggregations)); -// } -// } // ========================================================================================== // TODO: This is simply for initial / rudimentary testing - remove once further evolved diff --git a/src/test/java/ch/psi/daq/test/queryrest/AbstractDaqRestTest.java b/src/test/java/ch/psi/daq/test/queryrest/AbstractDaqRestTest.java index 563a2f9..82e77db 100644 --- a/src/test/java/ch/psi/daq/test/queryrest/AbstractDaqRestTest.java +++ b/src/test/java/ch/psi/daq/test/queryrest/AbstractDaqRestTest.java @@ -5,6 +5,7 @@ import javax.annotation.Resource; import org.junit.Before; import org.junit.runner.RunWith; import org.mockito.MockitoAnnotations; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext.ClassMode; @@ -18,6 +19,7 @@ import org.springframework.web.context.WebApplicationContext; import com.fasterxml.jackson.databind.ObjectMapper; +import ch.psi.daq.queryrest.QueryRestApplication; import ch.psi.daq.queryrest.config.QueryRestConfig; import ch.psi.daq.test.cassandra.CassandraDaqUnitDependencyInjectionTestExecutionListener; @@ -25,13 +27,17 @@ import ch.psi.daq.test.cassandra.CassandraDaqUnitDependencyInjectionTestExecutio @TestExecutionListeners({ CassandraDaqUnitDependencyInjectionTestExecutionListener.class, DependencyInjectionTestExecutionListener.class}) -@SpringApplicationConfiguration(classes = {QueryRestConfig.class, DaqWebMvcConfig.class}) +@SpringApplicationConfiguration(classes = { + QueryRestApplication.class + ,QueryRestConfig.class + ,DaqWebMvcConfig.class +}) @DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD) @WebAppConfiguration @RunWith(SpringJUnit4ClassRunner.class) public abstract class AbstractDaqRestTest { - @Resource + @Autowired protected WebApplicationContext webApplicationContext; protected MockMvc mockMvc; diff --git a/src/test/java/ch/psi/daq/test/queryrest/DaqWebMvcConfig.java b/src/test/java/ch/psi/daq/test/queryrest/DaqWebMvcConfig.java index f70e67e..da82d6e 100644 --- a/src/test/java/ch/psi/daq/test/queryrest/DaqWebMvcConfig.java +++ b/src/test/java/ch/psi/daq/test/queryrest/DaqWebMvcConfig.java @@ -1,6 +1,7 @@ package ch.psi.daq.test.queryrest; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.PropertySource; @@ -17,10 +18,11 @@ import ch.psi.daq.test.query.config.LocalQueryTestConfig; import ch.psi.daq.test.queryrest.query.DummyDataReader; @Configuration +@ComponentScan +@EnableWebMvc @PropertySources(value = { @PropertySource(value = {"classpath:queryrest-test.properties"}) }) -@EnableWebMvc public class DaqWebMvcConfig extends WebMvcConfigurationSupport { // ensure that properties in dispatcher.properties are loaded first and then overwritten by the diff --git a/src/test/java/ch/psi/daq/test/queryrest/controller/DaqRestControllerTest.java b/src/test/java/ch/psi/daq/test/queryrest/controller/DaqRestControllerTest.java index 4cd6a69..269cef1 100644 --- a/src/test/java/ch/psi/daq/test/queryrest/controller/DaqRestControllerTest.java +++ b/src/test/java/ch/psi/daq/test/queryrest/controller/DaqRestControllerTest.java @@ -48,6 +48,23 @@ public class DaqRestControllerTest extends AbstractDaqRestTest { @After public void tearDown() throws Exception {} + @Test + public void testChannelNameQuery() throws Exception { + + this.mockMvc.perform( + MockMvcRequestBuilders + .get("/" + QueryRestController.CHANNELS) + .contentType(MediaType.APPLICATION_JSON)) + .andDo(MockMvcResultHandlers.print()) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.jsonPath("$").isArray()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0]").exists()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0]").value("testChannel1")) + .andExpect(MockMvcResultMatchers.jsonPath("$[1]").exists()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1]").value("testChannel2")) + .andExpect(MockMvcResultMatchers.jsonPath("$[2]").doesNotExist()); + } + @Test public void testPulseRangeQuery() throws Exception { PulseRangeQuery request = new PulseRangeQuery( @@ -56,9 +73,13 @@ public class DaqRestControllerTest extends AbstractDaqRestTest { TEST_CHANNEL_NAMES); String content = mapper.writeValueAsString(request); - + System.out.println(content); + + content = "{\"channels\":[\"testChannel1\",\"testChannel2\"],\"startPulseId\":100,\"endPulseId\":101}"; + this.mockMvc - .perform(MockMvcRequestBuilders.post(QueryRestController.QUERY) + .perform(MockMvcRequestBuilders + .post("/" + QueryRestController.QUERY) .contentType(MediaType.APPLICATION_JSON) .content(content)) @@ -86,8 +107,8 @@ public class DaqRestControllerTest extends AbstractDaqRestTest { String content = mapper.writeValueAsString(request); - this.mockMvc - .perform(MockMvcRequestBuilders.post(QueryRestController.QUERY) + this.mockMvc.perform(MockMvcRequestBuilders + .post("/" + QueryRestController.QUERY) .contentType(MediaType.APPLICATION_JSON) .content(content)) @@ -120,7 +141,8 @@ public class DaqRestControllerTest extends AbstractDaqRestTest { this.mockMvc .perform( - MockMvcRequestBuilders.post(QueryRestController.QUERY) + MockMvcRequestBuilders + .post("/" + QueryRestController.QUERY) .contentType(MediaType.APPLICATION_JSON) .content(content) ) @@ -152,7 +174,7 @@ public class DaqRestControllerTest extends AbstractDaqRestTest { String content = mapper.writeValueAsString(request); this.mockMvc - .perform(MockMvcRequestBuilders.post(QueryRestController.QUERY) + .perform(MockMvcRequestBuilders.post("/" + QueryRestController.QUERY) .contentType(MediaType.APPLICATION_JSON) .content(content)) @@ -179,20 +201,4 @@ public class DaqRestControllerTest extends AbstractDaqRestTest { .andExpect(MockMvcResultMatchers.jsonPath("$.minima.max.event.pulseId").exists()) .andExpect(MockMvcResultMatchers.jsonPath("$.minima.max.event.pulseId").value(101)); } - - @Test - public void testChannelNameQuery() throws Exception { - this.mockMvc - .perform(MockMvcRequestBuilders.get(QueryRestController.CHANNELS) - .contentType(MediaType.APPLICATION_JSON)) - - .andDo(MockMvcResultHandlers.print()) - .andExpect(MockMvcResultMatchers.status().isOk()) - .andExpect(MockMvcResultMatchers.jsonPath("$").isArray()) - .andExpect(MockMvcResultMatchers.jsonPath("$[0]").exists()) - .andExpect(MockMvcResultMatchers.jsonPath("$[0]").value("testChannel1")) - .andExpect(MockMvcResultMatchers.jsonPath("$[1]").exists()) - .andExpect(MockMvcResultMatchers.jsonPath("$[1]").value("testChannel2")) - .andExpect(MockMvcResultMatchers.jsonPath("$[2]").doesNotExist()); - } } diff --git a/src/test/java/ch/psi/daq/test/queryrest/query/DummyDataReader.java b/src/test/java/ch/psi/daq/test/queryrest/query/DummyDataReader.java index d54bd5b..fd67fd9 100644 --- a/src/test/java/ch/psi/daq/test/queryrest/query/DummyDataReader.java +++ b/src/test/java/ch/psi/daq/test/queryrest/query/DummyDataReader.java @@ -1,7 +1,6 @@ package ch.psi.daq.test.queryrest.query; import java.util.List; -import java.util.UUID; import java.util.regex.Pattern; import java.util.stream.LongStream; import java.util.stream.Stream; @@ -52,7 +51,8 @@ public class DummyDataReader implements DataReader { i, i, 0, - "data_" + UUID.randomUUID().toString()); + 123 // dummy value + ); }); } }