diff --git a/Readme.md b/Readme.md index 0144065..a0b5b0a 100644 --- a/Readme.md +++ b/Readme.md @@ -326,6 +326,7 @@ A request is performed by sending a valid JSON object in the HTTP request body. - **channels**: Array of channels to be queried (see [here](Readme.md#query_channel_names) and [here](Readme.md#define_channel_names)). - **range**: The range of the query (see [here](Readme.md#query_range)). +- **limit**: An optional limit for the number of elements to retrieve. Limit together with aggregation does not make sense and thus is not supported. - **ordering**: The ordering of the data (see [here](Readme.md#data_ordering)). - **fields**: Array of requested fields (see [here](Readme.md#requested_fields)). - **aggregation**: Setting this attribute activates data aggregation (see [here](Readme.md#data_aggregation) for its specification). @@ -374,12 +375,16 @@ Queries are applied to a range. The following types of ranges are supported. ```json "range":{ "startPulseId":0, - "endPulseId":100 + "startInclusive":true, + "endPulseId":100, + "endInclusive":true } ``` - **startPulseId**: The start pulse-id of the range request. +- **startInclusive**: Defines if the start should be considered inclusive (values: **true**|false). - **endPulseId**: The end pulse-id of the range request. +- **endInclusive**: Defines if the end should be considered inclusive (values: **true**|false). #### By Date @@ -387,12 +392,16 @@ Queries are applied to a range. The following types of ranges are supported. ```json "range":{ "startDate":"2015-08-06T18:00:00.000", - "endDate":"2015-08-06T18:59:59.999" + "startInclusive":true, + "endDate":"2015-08-06T18:59:59.999", + "endInclusive":true } ``` - **startDate**: The start date of the time range in the ISO8601 format (such as 1997-07-16T19:20:30.123+02:00 or 1997-07-16T19:20:30.123456789+02:00 (omitting +02:00 falls back to the server's time zone)). +- **startInclusive**: Defines if the start should be considered inclusive (values: **true**|false). - **endDate**: The end date of the time range. +- **endInclusive**: Defines if the end should be considered inclusive (values: **true**|false). #### By Time @@ -400,12 +409,16 @@ Queries are applied to a range. The following types of ranges are supported. ```json "range":{ "startSeconds":"0.0", - "endSeconds":"1.000999999" + "startInclusive":true, + "endSeconds":"1.000999999", + "endInclusive":true } ``` - **startSeconds**: The start time of the range in seconds since midnight, January 1, 1970 UTC (the UNIX epoch) as a decimal value including fractional seconds. +- **startInclusive**: Defines if the start should be considered inclusive (values: **true**|false). - **endSeconds**: The end time of the range in seconds. +- **endInclusive**: Defines if the end should be considered inclusive (values: **true**|false). diff --git a/src/main/java/ch/psi/daq/queryrest/controller/validator/ConfigQueryValidator.java b/src/main/java/ch/psi/daq/queryrest/controller/validator/ConfigQueryValidator.java index ac33545..639bc1d 100644 --- a/src/main/java/ch/psi/daq/queryrest/controller/validator/ConfigQueryValidator.java +++ b/src/main/java/ch/psi/daq/queryrest/controller/validator/ConfigQueryValidator.java @@ -1,5 +1,6 @@ package ch.psi.daq.queryrest.controller.validator; +import java.math.BigDecimal; import java.util.LinkedHashSet; import java.util.Set; @@ -9,15 +10,20 @@ import org.springframework.context.ApplicationContextAware; import org.springframework.validation.Errors; import org.springframework.validation.Validator; +import ch.psi.daq.common.ordering.Ordering; import ch.psi.daq.domain.backend.Backend; import ch.psi.daq.domain.config.DomainConfig; import ch.psi.daq.domain.query.DAQConfigQuery; import ch.psi.daq.domain.query.operation.ConfigField; +import ch.psi.daq.domain.request.range.RequestRange; import ch.psi.daq.queryrest.config.QueryRestConfig; public class ConfigQueryValidator implements Validator, ApplicationContextAware { private Set queryResponseFields; + private long maxPulseRange; + private BigDecimal maxTimeRange; + @SuppressWarnings("unchecked") @Override public void setApplicationContext(ApplicationContext context) throws BeansException { @@ -25,6 +31,9 @@ public class ConfigQueryValidator implements Validator, ApplicationContextAware context = backend.getApplicationContext(); queryResponseFields = context.getBean(QueryRestConfig.BEAN_NAME_CONFIG_RESPONSE_FIELDS_QUERY, Set.class); + + maxPulseRange = context.getBean(DomainConfig.BEAN_NAME_QUERY_MAX_PULSE_RANGE, Long.class); + maxTimeRange = context.getBean(DomainConfig.BEAN_NAME_QUERY_MAX_TIME_RANGE, BigDecimal.class); } @Override @@ -44,5 +53,30 @@ public class ConfigQueryValidator implements Validator, ApplicationContextAware if (query.getFields() == null || query.getFields().isEmpty()) { query.setFields(new LinkedHashSet<>(queryResponseFields)); } + + RequestRange range = query.getRange(); + if (query.hasLimit()) { + if (Ordering.asc.equals(query.getOrdering())) { + if (range.isPulseIdRangeDefined() && range.getStartPulseId() < 0) { + errors.reject("limit", + "Pulse based query with limit and asc order requires start pulse-id to be defined."); + } + if (range.isTimeRangeDefined() && range.getStartTime() == null) { + errors.reject("limit", + "Time based query with limit and asc order requires start time to be defined."); + } + } else if (Ordering.desc.equals(query.getOrdering())) { + if (range.isPulseIdRangeDefined() && range.getEndPulseId() <= 0) { + errors.reject("limit", + "Pulse based query with limit and desc order requires end pulse-id to be defined."); + } + if (range.isTimeRangeDefined() && range.getEndTime() == null) { + errors.reject("limit", "Time based query with limit and desc order requires end time to be defined."); + } + } + } + + range.validate(maxPulseRange, maxTimeRange, query.getOrdering(), query.getLimit()); + query.setRange(range); } } diff --git a/src/main/java/ch/psi/daq/queryrest/controller/validator/EventQueryValidator.java b/src/main/java/ch/psi/daq/queryrest/controller/validator/EventQueryValidator.java index 82d2346..81c3f33 100644 --- a/src/main/java/ch/psi/daq/queryrest/controller/validator/EventQueryValidator.java +++ b/src/main/java/ch/psi/daq/queryrest/controller/validator/EventQueryValidator.java @@ -1,5 +1,6 @@ package ch.psi.daq.queryrest.controller.validator; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.Set; @@ -10,6 +11,7 @@ import org.springframework.context.ApplicationContextAware; import org.springframework.validation.Errors; import org.springframework.validation.Validator; +import ch.psi.daq.common.ordering.Ordering; import ch.psi.daq.domain.backend.Backend; import ch.psi.daq.domain.config.DomainConfig; import ch.psi.daq.domain.query.DAQQueries; @@ -20,11 +22,14 @@ import ch.psi.daq.domain.query.operation.EventField; import ch.psi.daq.domain.query.transform.ExecutionEnvironment; import ch.psi.daq.domain.query.transform.ValueTransformationSequence; import ch.psi.daq.domain.request.Request; +import ch.psi.daq.domain.request.range.RequestRange; import ch.psi.daq.queryrest.config.QueryRestConfig; public class EventQueryValidator implements Validator, ApplicationContextAware { private Set defaultResponseFields; private Set defaultResponseAggregations; + private long maxPulseRange; + private BigDecimal maxTimeRange; @SuppressWarnings("unchecked") @Override @@ -33,7 +38,11 @@ public class EventQueryValidator implements Validator, ApplicationContextAware { context = backend.getApplicationContext(); defaultResponseFields = context.getBean(QueryRestConfig.BEAN_NAME_DEFAULT_EVENT_RESPONSE_FIELDS, Set.class); - defaultResponseAggregations = context.getBean(QueryRestConfig.BEAN_NAME_DEFAULT_EVENT_RESPONSE_AGGREGATIONS, Set.class); + defaultResponseAggregations = + context.getBean(QueryRestConfig.BEAN_NAME_DEFAULT_EVENT_RESPONSE_AGGREGATIONS, Set.class); + + maxPulseRange = context.getBean(DomainConfig.BEAN_NAME_QUERY_MAX_PULSE_RANGE, Long.class); + maxTimeRange = context.getBean(DomainConfig.BEAN_NAME_QUERY_MAX_TIME_RANGE, BigDecimal.class); } @Override @@ -59,6 +68,31 @@ public class EventQueryValidator implements Validator, ApplicationContextAware { query.setFields(new LinkedHashSet<>(defaultResponseFields)); } + RequestRange range = query.getRange(); + if (query.hasLimit()) { + if (Ordering.asc.equals(query.getOrdering())) { + if (range.isPulseIdRangeDefined() && range.getStartPulseId() < 0) { + errors.reject("limit", + "Pulse based query with limit and asc order requires start pulse-id to be defined."); + } + if (range.isTimeRangeDefined() && range.getStartTime() == null) { + errors.reject("limit", + "Time based query with limit and asc order requires start time to be defined."); + } + } else if (Ordering.desc.equals(query.getOrdering())) { + if (range.isPulseIdRangeDefined() && range.getEndPulseId() <= 0) { + errors.reject("limit", + "Pulse based query with limit and desc order requires end pulse-id to be defined."); + } + if (range.isTimeRangeDefined() && range.getEndTime() == null) { + errors.reject("limit", "Time based query with limit and desc order requires end time to be defined."); + } + } + } + + range.validate(maxPulseRange, maxTimeRange, query.getOrdering(), query.getLimit()); + query.setRange(range); + if (query.getAggregation() != null) { // check if only one binning element is defined final long durationPerBin = query.getAggregation().getDurationPerBin(); @@ -81,12 +115,15 @@ public class EventQueryValidator implements Validator, ApplicationContextAware { errors.reject("pulsesPerBin", "Time range queries only support time based binning."); } - // set default values (if not set) if (query.getAggregation().getAggregations() == null || query.getAggregation().getAggregations().isEmpty()) { query.getAggregation().setAggregations(new ArrayList<>(defaultResponseAggregations)); } + if (query.hasLimit()) { + errors.reject("limit", "Aggregation with limit does not make sense."); + } + // without adding this field, user need to explicitly ask for value field when querying // aggregations. // if (!query.getFields().contains(QueryField.value)) { diff --git a/src/test/java/ch/psi/daq/test/queryrest/controller/JsonQueryRestControllerTest.java b/src/test/java/ch/psi/daq/test/queryrest/controller/JsonQueryRestControllerTest.java index 268e4da..64ec4c5 100644 --- a/src/test/java/ch/psi/daq/test/queryrest/controller/JsonQueryRestControllerTest.java +++ b/src/test/java/ch/psi/daq/test/queryrest/controller/JsonQueryRestControllerTest.java @@ -47,7 +47,13 @@ import ch.psi.daq.domain.query.transform.image.color.TypedColorModel; import ch.psi.daq.domain.query.transform.image.encoding.Base64ImageEncoder; import ch.psi.daq.domain.query.transform.sampling.ValueSamplerValueTransformation; import ch.psi.daq.domain.request.range.RequestRangeDate; +import ch.psi.daq.domain.request.range.RequestRangeEndDate; +import ch.psi.daq.domain.request.range.RequestRangeEndPulseId; +import ch.psi.daq.domain.request.range.RequestRangeEndTime; import ch.psi.daq.domain.request.range.RequestRangePulseId; +import ch.psi.daq.domain.request.range.RequestRangeStartDate; +import ch.psi.daq.domain.request.range.RequestRangeStartPulseId; +import ch.psi.daq.domain.request.range.RequestRangeStartTime; import ch.psi.daq.domain.request.range.RequestRangeTime; import ch.psi.daq.domain.test.TestTimeUtils; import ch.psi.daq.queryrest.response.json.JSONHTTPResponse; @@ -609,6 +615,639 @@ public class JsonQueryRestControllerTest extends AbstractDaqRestTest implements .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[1].globalSeconds").value( TestTimeUtils.getTimeStr(1, 10000000))); } + + @Test + public void testOpenTimeRangeQueryStart_01() throws Exception { + DAQQuery request = new DAQQuery( + new RequestRangeStartTime( + TimeUtils.getTimeFromMillis(2000, 0)), + TEST_CHANNEL_NAMES); + request.setLimit(2); + + String content = mapper.writeValueAsString(request); + + this.mockMvc.perform(MockMvcRequestBuilders + .post(DomainConfig.PATH_QUERY) + .contentType(MediaType.APPLICATION_JSON) + .content(content)) + + .andDo(MockMvcResultHandlers.print()) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.jsonPath("$").isArray()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0]").exists()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].channel").isMap()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].channel.name").value(TEST_CHANNEL_01)) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data").isArray()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[0].pulseId").value(200)) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[0].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 0))) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[1].pulseId").value(201)) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[1].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 10000000))) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[2]").doesNotExist()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1]").exists()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].channel").isMap()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].channel.name").value(TEST_CHANNEL_02)) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data").isArray()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[0].pulseId").value(200)) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[0].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 0))) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[1].pulseId").value(201)) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[1].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 10000000))) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[2]").doesNotExist()); + } + + @Test + public void testOpenTimeRangeQueryStart_01_Exclusive() throws Exception { + DAQQuery request = new DAQQuery( + new RequestRangeStartTime( + TimeUtils.getTimeFromMillis(2000, 0), + false), + TEST_CHANNEL_NAMES); + request.setLimit(2); + + String content = mapper.writeValueAsString(request); + + this.mockMvc.perform(MockMvcRequestBuilders + .post(DomainConfig.PATH_QUERY) + .contentType(MediaType.APPLICATION_JSON) + .content(content)) + + .andDo(MockMvcResultHandlers.print()) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.jsonPath("$").isArray()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0]").exists()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].channel").isMap()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].channel.name").value(TEST_CHANNEL_01)) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data").isArray()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[0].pulseId").value(201)) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[0].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 10000000))) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[1].pulseId").value(202)) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[1].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 20000000))) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[2]").doesNotExist()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1]").exists()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].channel").isMap()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].channel.name").value(TEST_CHANNEL_02)) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data").isArray()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[0].pulseId").value(201)) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[0].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 10000000))) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[1].pulseId").value(202)) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[1].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 20000000))) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[2]").doesNotExist()); + } + + @Test + public void testOpenTimeRangeQueryStartDate_01_Exclusive() throws Exception { + DAQQuery request = new DAQQuery( + new RequestRangeStartDate( + TimeUtils.format(TimeUtils.getTimeFromMillis(2000, 0)), + false), + TEST_CHANNEL_NAMES); + request.setLimit(2); + + String content = mapper.writeValueAsString(request); + + this.mockMvc.perform(MockMvcRequestBuilders + .post(DomainConfig.PATH_QUERY) + .contentType(MediaType.APPLICATION_JSON) + .content(content)) + + .andDo(MockMvcResultHandlers.print()) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.jsonPath("$").isArray()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0]").exists()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].channel").isMap()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].channel.name").value(TEST_CHANNEL_01)) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data").isArray()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[0].pulseId").value(201)) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[0].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 10000000))) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[1].pulseId").value(202)) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[1].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 20000000))) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[2]").doesNotExist()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1]").exists()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].channel").isMap()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].channel.name").value(TEST_CHANNEL_02)) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data").isArray()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[0].pulseId").value(201)) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[0].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 10000000))) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[1].pulseId").value(202)) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[1].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 20000000))) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[2]").doesNotExist()); + } + + @Test + public void testOpenTimeRangeQueryStart_02() throws Exception { + DAQQuery request = new DAQQuery( + new RequestRangeStartTime( + TimeUtils.getTimeFromMillis(2000, 0)), + TEST_CHANNEL_NAMES); + + String content = mapper.writeValueAsString(request); + + try { + // limit needs to be defined + this.mockMvc.perform(MockMvcRequestBuilders + .post(DomainConfig.PATH_QUERY) + .contentType(MediaType.APPLICATION_JSON) + .content(content)) + + .andDo(MockMvcResultHandlers.print()) + .andExpect(MockMvcResultMatchers.status().isOk()); + assertTrue(false); + } catch (Exception e) { + assertTrue(true); + } + } + + @Test + public void testOpenTimeRangeQueryStart_03() throws Exception { + DAQQuery request = new DAQQuery( + new RequestRangeStartTime( + TimeUtils.getTimeFromMillis(2000, 0)), + TEST_CHANNEL_NAMES); + request.setLimit(2); + request.setOrdering(Ordering.desc); + + String content = mapper.writeValueAsString(request); + + try { + // asc needs to be defined + this.mockMvc.perform(MockMvcRequestBuilders + .post(DomainConfig.PATH_QUERY) + .contentType(MediaType.APPLICATION_JSON) + .content(content)) + + .andDo(MockMvcResultHandlers.print()) + .andExpect(MockMvcResultMatchers.status().isOk()); + assertTrue(false); + } catch (Exception e) { + assertTrue(true); + } + } + + @Test + public void testOpenTimeRangeQueryEnd_01() throws Exception { + DAQQuery request = new DAQQuery( + new RequestRangeEndTime( + TimeUtils.getTimeFromMillis(2010, 0)), + TEST_CHANNEL_NAMES); + request.setLimit(2); + request.setOrdering(Ordering.desc); + + String content = mapper.writeValueAsString(request); + + this.mockMvc.perform(MockMvcRequestBuilders + .post(DomainConfig.PATH_QUERY) + .contentType(MediaType.APPLICATION_JSON) + .content(content)) + + .andDo(MockMvcResultHandlers.print()) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.jsonPath("$").isArray()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0]").exists()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].channel").isMap()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].channel.name").value(TEST_CHANNEL_01)) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data").isArray()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[0].pulseId").value(201)) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[0].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 10000000))) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[1].pulseId").value(200)) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[1].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 0))) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[2]").doesNotExist()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1]").exists()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].channel").isMap()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].channel.name").value(TEST_CHANNEL_02)) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data").isArray()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[0].pulseId").value(201)) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[0].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 10000000))) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[1].pulseId").value(200)) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[1].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 0))) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[2]").doesNotExist()); + } + + @Test + public void testOpenTimeRangeQueryEnd_01_Exclusive() throws Exception { + DAQQuery request = new DAQQuery( + new RequestRangeEndTime( + TimeUtils.getTimeFromMillis(2020, 0), + false), + TEST_CHANNEL_NAMES); + request.setLimit(2); + request.setOrdering(Ordering.desc); + + String content = mapper.writeValueAsString(request); + + this.mockMvc.perform(MockMvcRequestBuilders + .post(DomainConfig.PATH_QUERY) + .contentType(MediaType.APPLICATION_JSON) + .content(content)) + + .andDo(MockMvcResultHandlers.print()) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.jsonPath("$").isArray()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0]").exists()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].channel").isMap()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].channel.name").value(TEST_CHANNEL_01)) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data").isArray()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[0].pulseId").value(201)) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[0].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 10000000))) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[1].pulseId").value(200)) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[1].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 0))) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[2]").doesNotExist()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1]").exists()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].channel").isMap()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].channel.name").value(TEST_CHANNEL_02)) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data").isArray()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[0].pulseId").value(201)) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[0].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 10000000))) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[1].pulseId").value(200)) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[1].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 0))) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[2]").doesNotExist()); + } + + @Test + public void testOpenTimeRangeQueryEndDate_01_Exclusive() throws Exception { + DAQQuery request = new DAQQuery( + new RequestRangeEndDate( + TimeUtils.format(TimeUtils.getTimeFromMillis(2020, 0)), + false), + TEST_CHANNEL_NAMES); + request.setLimit(2); + request.setOrdering(Ordering.desc); + + String content = mapper.writeValueAsString(request); + + this.mockMvc.perform(MockMvcRequestBuilders + .post(DomainConfig.PATH_QUERY) + .contentType(MediaType.APPLICATION_JSON) + .content(content)) + + .andDo(MockMvcResultHandlers.print()) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.jsonPath("$").isArray()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0]").exists()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].channel").isMap()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].channel.name").value(TEST_CHANNEL_01)) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data").isArray()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[0].pulseId").value(201)) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[0].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 10000000))) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[1].pulseId").value(200)) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[1].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 0))) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[2]").doesNotExist()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1]").exists()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].channel").isMap()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].channel.name").value(TEST_CHANNEL_02)) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data").isArray()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[0].pulseId").value(201)) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[0].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 10000000))) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[1].pulseId").value(200)) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[1].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 0))) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[2]").doesNotExist()); + } + + @Test + public void testOpenTimeRangeQueryEnd_02() throws Exception { + DAQQuery request = new DAQQuery( + new RequestRangeEndTime( + TimeUtils.getTimeFromMillis(2010, 0)), + TEST_CHANNEL_NAMES); + request.setOrdering(Ordering.desc); + + String content = mapper.writeValueAsString(request); + + try { + // limit needs to be defined + this.mockMvc.perform(MockMvcRequestBuilders + .post(DomainConfig.PATH_QUERY) + .contentType(MediaType.APPLICATION_JSON) + .content(content)) + + .andDo(MockMvcResultHandlers.print()) + .andExpect(MockMvcResultMatchers.status().isOk()); + assertTrue(false); + } catch (Exception e) { + assertTrue(true); + } + } + + @Test + public void testOpenTimeRangeQueryEnd_03() throws Exception { + DAQQuery request = new DAQQuery( + new RequestRangeEndTime( + TimeUtils.getTimeFromMillis(2010, 0)), + TEST_CHANNEL_NAMES); + request.setLimit(2); + request.setOrdering(Ordering.asc); + + String content = mapper.writeValueAsString(request); + + try { + // desc needs to be defined + this.mockMvc.perform(MockMvcRequestBuilders + .post(DomainConfig.PATH_QUERY) + .contentType(MediaType.APPLICATION_JSON) + .content(content)) + + .andDo(MockMvcResultHandlers.print()) + .andExpect(MockMvcResultMatchers.status().isOk()); + assertTrue(false); + } catch (Exception e) { + assertTrue(true); + } + } + + @Test + public void testOpenPulseRangeQueryStart_01() throws Exception { + DAQQuery request = new DAQQuery( + new RequestRangeStartPulseId( + 200), + TEST_CHANNEL_NAMES); + request.setLimit(2); + + String content = mapper.writeValueAsString(request); + + this.mockMvc.perform(MockMvcRequestBuilders + .post(DomainConfig.PATH_QUERY) + .contentType(MediaType.APPLICATION_JSON) + .content(content)) + + .andDo(MockMvcResultHandlers.print()) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.jsonPath("$").isArray()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0]").exists()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].channel").isMap()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].channel.name").value(TEST_CHANNEL_01)) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data").isArray()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[0].pulseId").value(200)) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[0].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 0))) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[1].pulseId").value(201)) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[1].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 10000000))) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[2]").doesNotExist()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1]").exists()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].channel").isMap()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].channel.name").value(TEST_CHANNEL_02)) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data").isArray()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[0].pulseId").value(200)) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[0].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 0))) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[1].pulseId").value(201)) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[1].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 10000000))) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[2]").doesNotExist()); + } + + @Test + public void testOpenPulseRangeQueryStart_01_Exclusive() throws Exception { + DAQQuery request = new DAQQuery( + new RequestRangeStartPulseId( + 200, + false), + TEST_CHANNEL_NAMES); + request.setLimit(2); + + String content = mapper.writeValueAsString(request); + + this.mockMvc.perform(MockMvcRequestBuilders + .post(DomainConfig.PATH_QUERY) + .contentType(MediaType.APPLICATION_JSON) + .content(content)) + + .andDo(MockMvcResultHandlers.print()) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.jsonPath("$").isArray()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0]").exists()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].channel").isMap()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].channel.name").value(TEST_CHANNEL_01)) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data").isArray()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[0].pulseId").value(201)) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[0].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 10000000))) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[1].pulseId").value(202)) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[1].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 20000000))) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[2]").doesNotExist()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1]").exists()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].channel").isMap()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].channel.name").value(TEST_CHANNEL_02)) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data").isArray()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[0].pulseId").value(201)) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[0].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 10000000))) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[1].pulseId").value(202)) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[1].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 20000000))) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[2]").doesNotExist()); + } + + @Test + public void testOpenPulseRangeQueryStart_02() throws Exception { + DAQQuery request = new DAQQuery( + new RequestRangeStartPulseId( + 200), + TEST_CHANNEL_NAMES); + + String content = mapper.writeValueAsString(request); + + try { + // limit needs to be defined + this.mockMvc.perform(MockMvcRequestBuilders + .post(DomainConfig.PATH_QUERY) + .contentType(MediaType.APPLICATION_JSON) + .content(content)) + + .andDo(MockMvcResultHandlers.print()) + .andExpect(MockMvcResultMatchers.status().isOk()); + assertTrue(false); + } catch (Exception e) { + assertTrue(true); + } + } + + @Test + public void testOpenPulseRangeQueryStart_03() throws Exception { + DAQQuery request = new DAQQuery( + new RequestRangeStartPulseId( + 200), + TEST_CHANNEL_NAMES); + request.setLimit(2); + request.setOrdering(Ordering.desc); + + String content = mapper.writeValueAsString(request); + + try { + // asc needs to be defined + this.mockMvc.perform(MockMvcRequestBuilders + .post(DomainConfig.PATH_QUERY) + .contentType(MediaType.APPLICATION_JSON) + .content(content)) + + .andDo(MockMvcResultHandlers.print()) + .andExpect(MockMvcResultMatchers.status().isOk()); + assertTrue(false); + } catch (Exception e) { + assertTrue(true); + } + } + + @Test + public void testOpenPulseRangeQueryEnd_01() throws Exception { + DAQQuery request = new DAQQuery( + new RequestRangeEndPulseId( + 201), + TEST_CHANNEL_NAMES); + request.setLimit(2); + request.setOrdering(Ordering.desc); + + String content = mapper.writeValueAsString(request); + + this.mockMvc.perform(MockMvcRequestBuilders + .post(DomainConfig.PATH_QUERY) + .contentType(MediaType.APPLICATION_JSON) + .content(content)) + + .andDo(MockMvcResultHandlers.print()) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.jsonPath("$").isArray()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0]").exists()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].channel").isMap()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].channel.name").value(TEST_CHANNEL_01)) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data").isArray()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[0].pulseId").value(201)) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[0].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 10000000))) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[1].pulseId").value(200)) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[1].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 0))) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[2]").doesNotExist()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1]").exists()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].channel").isMap()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].channel.name").value(TEST_CHANNEL_02)) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data").isArray()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[0].pulseId").value(201)) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[0].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 10000000))) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[1].pulseId").value(200)) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[1].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 0))) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[2]").doesNotExist()); + } + + @Test + public void testOpenPulseRangeQueryEnd_01_Exclusive() throws Exception { + DAQQuery request = new DAQQuery( + new RequestRangeEndPulseId( + 202, + false), + TEST_CHANNEL_NAMES); + request.setLimit(2); + request.setOrdering(Ordering.desc); + + String content = mapper.writeValueAsString(request); + + this.mockMvc.perform(MockMvcRequestBuilders + .post(DomainConfig.PATH_QUERY) + .contentType(MediaType.APPLICATION_JSON) + .content(content)) + + .andDo(MockMvcResultHandlers.print()) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.jsonPath("$").isArray()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0]").exists()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].channel").isMap()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].channel.name").value(TEST_CHANNEL_01)) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data").isArray()) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[0].pulseId").value(201)) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[0].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 10000000))) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[1].pulseId").value(200)) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[1].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 0))) + .andExpect(MockMvcResultMatchers.jsonPath("$[0].data[2]").doesNotExist()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1]").exists()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].channel").isMap()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].channel.name").value(TEST_CHANNEL_02)) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data").isArray()) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[0].pulseId").value(201)) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[0].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 10000000))) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[1].pulseId").value(200)) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[1].globalSeconds").value( + TestTimeUtils.getTimeStr(2, 0))) + .andExpect(MockMvcResultMatchers.jsonPath("$[1].data[2]").doesNotExist()); + } + + @Test + public void testOpenPulseRangeQueryEnd_02() throws Exception { + DAQQuery request = new DAQQuery( + new RequestRangeEndPulseId( + 201), + TEST_CHANNEL_NAMES); + request.setOrdering(Ordering.desc); + + String content = mapper.writeValueAsString(request); + + try { + // limit needs to be defined + this.mockMvc.perform(MockMvcRequestBuilders + .post(DomainConfig.PATH_QUERY) + .contentType(MediaType.APPLICATION_JSON) + .content(content)) + + .andDo(MockMvcResultHandlers.print()) + .andExpect(MockMvcResultMatchers.status().isOk()); + assertTrue(false); + } catch (Exception e) { + assertTrue(true); + } + } + + @Test + public void testOpenPulseRangeQueryEnd_03() throws Exception { + DAQQuery request = new DAQQuery( + new RequestRangeEndPulseId( + 201), + TEST_CHANNEL_NAMES); + request.setOrdering(Ordering.asc); + request.setLimit(2); + + String content = mapper.writeValueAsString(request); + + try { + // desc needs to be defined + this.mockMvc.perform(MockMvcRequestBuilders + .post(DomainConfig.PATH_QUERY) + .contentType(MediaType.APPLICATION_JSON) + .content(content)) + + .andDo(MockMvcResultHandlers.print()) + .andExpect(MockMvcResultMatchers.status().isOk()); + assertTrue(false); + } catch (Exception e) { + assertTrue(true); + } + } @Test public void testExtremaAggregation() throws Exception { diff --git a/src/test/java/ch/psi/daq/test/queryrest/response/ResponseQueryTest.java b/src/test/java/ch/psi/daq/test/queryrest/response/ResponseQueryTest.java index 68ac2d1..3771c59 100644 --- a/src/test/java/ch/psi/daq/test/queryrest/response/ResponseQueryTest.java +++ b/src/test/java/ch/psi/daq/test/queryrest/response/ResponseQueryTest.java @@ -112,6 +112,4 @@ public class ResponseQueryTest extends AbstractDaqRestTest { assertNull(deserial.getResponse()); } - - }