DateRangeParam now correctly handles requests containing a single date

This commit is contained in:
jamesagnew 2014-11-08 12:17:42 -05:00
parent 450b270c00
commit fcb9a80bbc
6 changed files with 204 additions and 49 deletions

View File

@ -95,10 +95,10 @@ public class DateRangeParam implements IQueryParameterAnd<DateParam> {
* Constructor which takes two strings representing the lower and upper bounds of the range (inclusive on both ends)
*
* @param theLowerBound
* A qualified date param representing the lower date bound (optionally may include time), e.g.
* An unqualified date param representing the lower date bound (optionally may include time), e.g.
* "2011-02-22" or "2011-02-22T13:12:00"
* @param theUpperBound
* A qualified date param representing the upper date bound (optionally may include time), e.g.
* An unqualified date param representing the upper date bound (optionally may include time), e.g.
* "2011-02-22" or "2011-02-22T13:12:00"
*/
public DateRangeParam(String theLowerBound, String theUpperBound) {
@ -211,7 +211,9 @@ public class DateRangeParam implements IQueryParameterAnd<DateParam> {
@Override
public void setValuesAsQueryTokens(List<QualifiedParamList> theParameters) throws InvalidRequestException {
for (List<String> paramList : theParameters) {
boolean haveHadUnqualifiedParameter = false;
for (QualifiedParamList paramList : theParameters) {
if (paramList.size() == 0) {
continue;
}
@ -220,9 +222,18 @@ public class DateRangeParam implements IQueryParameterAnd<DateParam> {
}
String param = paramList.get(0);
DateParam parsed = new DateParam();
parsed.setValueAsQueryToken(null, param);
parsed.setValueAsQueryToken(paramList.getQualifier(), param);
addParam(parsed);
if (parsed.getComparator() == null) {
if (haveHadUnqualifiedParameter) {
throw new InvalidRequestException("Multiple date parameters with the same name and no qualifier (>, <, etc.) is not supported");
}
haveHadUnqualifiedParameter=true;
}
}
}
private void addParam(DateParam theParsed) throws InvalidRequestException {
@ -231,10 +242,9 @@ public class DateRangeParam implements IQueryParameterAnd<DateParam> {
throw new InvalidRequestException("Can not have multiple date range parameters for the same param without a qualifier");
}
myLowerBound = theParsed;
myUpperBound = theParsed;
// TODO: in this case, should set lower and upper to exact moments
// using specified precision
myLowerBound = new DateParam(QuantityCompararatorEnum.GREATERTHAN_OR_EQUALS, theParsed.getValueAsString());
myUpperBound = new DateParam(QuantityCompararatorEnum.LESSTHAN_OR_EQUALS, theParsed.getValueAsString());
} else {
switch (theParsed.getComparator()) {

View File

@ -8,7 +8,6 @@ import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.junit.BeforeClass;
import org.junit.Test;
import ca.uhn.fhir.rest.method.QualifiedParamList;
@ -16,13 +15,40 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public class DateRangeParamTest {
private static DateRangeParam create(String theLower, String theUpper) throws InvalidRequestException {
DateRangeParam p = new DateRangeParam();
List<QualifiedParamList> tokens = new ArrayList<QualifiedParamList>();
tokens.add(QualifiedParamList.singleton(null, theLower));
if (theUpper != null) {
tokens.add(QualifiedParamList.singleton(null, theUpper));
}
p.setValuesAsQueryTokens(tokens);
return p;
}
public static Date parse(String theString) throws ParseException {
return ourFmt.parse(theString);
}
public static Date parseM1(String theString) throws ParseException {
return new Date(ourFmt.parse(theString).getTime() - 1L);
}
private static SimpleDateFormat ourFmt;
static {
ourFmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSS");
}
private DateRangeParam create(String theString) {
return new DateRangeParam(new DateParam(theString));
}
@Test
public void testDay() throws Exception {
assertEquals(parse("2011-01-01 00:00:00.0000"), create(">=2011-01-01", "<2011-01-02").getLowerBoundAsInstant());
assertEquals(parseM1("2011-01-02 00:00:00.0000"), create(">=2011-01-01", "<2011-01-02").getUpperBoundAsInstant());
assertEquals(parse("2011-01-02 00:00:00.0000"), create(">2011-01-01", "<=2011-01-02").getLowerBoundAsInstant());
assertEquals(parseM1("2011-01-03 00:00:00.0000"), create(">2011-01-01", "<=2011-01-02").getUpperBoundAsInstant());
}
@ -31,7 +57,7 @@ public class DateRangeParamTest {
public void testFromQualifiedDateParam() throws Exception {
assertEquals(parse("2011-01-01 00:00:00.0000"), create("2011-01-01").getLowerBoundAsInstant());
assertEquals(parseM1("2011-01-02 00:00:00.0000"), create("2011-01-01").getUpperBoundAsInstant());
assertEquals(parse("2011-01-01 00:00:00.0000"), create(">=2011-01-01").getLowerBoundAsInstant());
assertEquals(null, create(">=2011-01-01").getUpperBoundAsInstant());
@ -43,16 +69,22 @@ public class DateRangeParamTest {
public void testMonth() throws Exception {
assertEquals(parse("2011-01-01 00:00:00.0000"), create(">=2011-01", "<2011-02").getLowerBoundAsInstant());
assertEquals(parseM1("2011-02-01 00:00:00.0000"), create(">=2011-01", "<2011-02").getUpperBoundAsInstant());
assertEquals(parse("2011-02-01 00:00:00.0000"), create(">2011-01", "<=2011-02").getLowerBoundAsInstant());
assertEquals(parseM1("2011-03-01 00:00:00.0000"), create(">2011-01", "<=2011-02").getUpperBoundAsInstant());
}
@Test
public void testOnlyOneParam() throws Exception {
assertEquals(parse("2011-01-01 00:00:00.0000"), create("2011-01-01").getLowerBoundAsInstant());
assertEquals(parseM1("2011-01-02 00:00:00.0000"), create("2011-01-01").getUpperBoundAsInstant());
}
@Test
public void testSecond() throws Exception {
assertEquals(parse("2011-01-01 00:00:00.0000"), create(">=2011-01-01T00:00:00", "<2011-01-01T01:00:00").getLowerBoundAsInstant());
assertEquals(parseM1("2011-01-01 02:00:00.0000"), create(">=2011-01-01T00:00:00", "<2011-01-01T02:00:00").getUpperBoundAsInstant());
assertEquals(parse("2011-01-01 00:00:01.0000"), create(">2011-01-01T00:00:00", "<=2011-01-01T02:00:00").getLowerBoundAsInstant());
assertEquals(parseM1("2011-01-01 02:00:01.0000"), create(">2011-01-01T00:00:00", "<=2011-01-01T02:00:00").getUpperBoundAsInstant());
}
@ -61,40 +93,9 @@ public class DateRangeParamTest {
public void testYear() throws Exception {
assertEquals(parse("2011-01-01 00:00:00.0000"), create(">=2011", "<2012").getLowerBoundAsInstant());
assertEquals(parseM1("2012-01-01 00:00:00.0000"), create(">=2011", "<2012").getUpperBoundAsInstant());
assertEquals(parse("2012-01-01 00:00:00.0000"), create(">2011", "<=2012").getLowerBoundAsInstant());
assertEquals(parseM1("2014-01-01 00:00:00.0000"), create(">2011", "<=2013").getUpperBoundAsInstant());
}
private DateRangeParam create(String theString) {
return new DateRangeParam(new QualifiedDateParam(theString));
}
private Date parse(String theString) throws ParseException {
return ourFmt.parse(theString);
}
private Date parseM1(String theString) throws ParseException {
return new Date(ourFmt.parse(theString).getTime() - 1L);
}
private Date parseP1(String theString) throws ParseException {
return new Date(ourFmt.parse(theString).getTime() + 1L);
}
@BeforeClass
public static void beforeClass() {
ourFmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSS");
}
private static DateRangeParam create(String theLower, String theUpper) throws InvalidRequestException {
DateRangeParam p = new DateRangeParam();
List<QualifiedParamList> tokens=new ArrayList<QualifiedParamList>();
tokens.add(QualifiedParamList.singleton(null,theLower));
tokens.add(QualifiedParamList.singleton(null,theUpper));
p.setValuesAsQueryTokens(tokens);
return p;
}
}

View File

@ -0,0 +1,131 @@
package ca.uhn.fhir.rest.server;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.DateRangeParamTest;
import ca.uhn.fhir.util.PortUtil;
/**
* Created by dsotnikov on 2/25/2014.
*/
public class DateRangeParamSearchTest {
private static CloseableHttpClient ourClient;
private static int ourPort;
private static Server ourServer;
@Test
public void testSearchForOneUnqualifiedDate() throws Exception {
String baseUrl = "http://localhost:" + ourPort + "/Patient?" + Patient.SP_BIRTHDATE + "=";
HttpGet httpGet = new HttpGet(baseUrl + "2012-01-01");
HttpResponse status = ourClient.execute(httpGet);
IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals("2012-01-01", ourLastDateRange.getLowerBound().getValueAsString());
assertEquals("2012-01-01", ourLastDateRange.getUpperBound().getValueAsString());
assertEquals(DateRangeParamTest.parse("2012-01-01 00:00:00.0000"), ourLastDateRange.getLowerBoundAsInstant());
assertEquals(DateRangeParamTest.parseM1("2012-01-02 00:00:00.0000"), ourLastDateRange.getUpperBoundAsInstant());
}
@Test
public void testSearchForMultipleUnqualifiedDate() throws Exception {
String baseUrl = "http://localhost:" + ourPort + "/Patient?" + Patient.SP_BIRTHDATE + "=";
HttpGet httpGet = new HttpGet(baseUrl + "2012-01-01&" + Patient.SP_BIRTHDATE + "=2012-02-03");
HttpResponse status = ourClient.execute(httpGet);
IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals(400, status.getStatusLine().getStatusCode());
}
@AfterClass
public static void afterClass() throws Exception {
ourServer.stop();
}
@Before
public void before() {
ourLastDateRange = null;
}
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();
ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer();
servlet.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
servlet.setResourceProviders(patientProvider);
ServletHolder servletHolder = new ServletHolder(servlet);
proxyHandler.addServletWithMapping(servletHolder, "/*");
ourServer.setHandler(proxyHandler);
ourServer.start();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourClient = builder.build();
}
private static DateRangeParam ourLastDateRange;
/**
* Created by dsotnikov on 2/25/2014.
*/
public static class DummyPatientResourceProvider implements IResourceProvider {
@Search()
public List<Patient> search(@RequiredParam(name=Patient.SP_BIRTHDATE) DateRangeParam theDateRange) {
ourLastDateRange = theDateRange;
ArrayList<Patient> retVal = new ArrayList<Patient>();
Patient patient = new Patient();
patient.setId("1");
patient.addIdentifier("system", "hello");
retVal.add(patient);
return retVal;
}
@Override
public Class<? extends IResource> getResourceType() {
return Patient.class;
}
}
}

View File

@ -36,16 +36,29 @@
returns a default Patient, but sometimes uses a custom subclass).
Thanks to Bill de Beaubien for the pull request!
</action>
<action>
<action type="add">
Add a new method <![CDATA[handleException]]> to the server interceptor
framework which allows interceptors to be notified of any exceptions and
runtime errors within server methods. Interceptors may optionally also
override the default error handling behaviour of the RestfulServer.
</action>
<action dev="wdebeau1">
<action dev="wdebeau1" type="add">
Add constants to BaseResource for the "_id" search parameter which all resources
should support.
</action>
<action type="fix">
DateRangeParam parameters on the server now return correct
<![CDATA[<code>getLowerBoundAsInstant()</code>]]>
and
<![CDATA[<code>getUpperBoundAsInstant()</code>]]>
values if a single unqualified value is passed in. For example, if
a query containing
<![CDATA[<code>&birthdate=2012-10-01</code>]]>
is received, previously these two methods would both return the same
value, but with this fix
<![CDATA[<code>getUpperBoundAsInstant()</code>]]>
now returns the instant at 23:59:59.9999.
</action>
</release>
<release version="0.7" date="2014-Oct-23">
<action type="add" issue="30">

View File

@ -120,7 +120,7 @@
</p>
<macro name="snippet">
<param name="file" value="src/test/resources/narrative/Practitioner.html" />
<param name="file" value="hapi-fhir-structures-dstu/src/test/resources/narrative/Practitioner.html" />
</macro>
<p>

View File

@ -86,7 +86,7 @@
and copy in the following contents:
</p>
<macro name="snippet">
<param name="file" value="../restful-server-example/src/main/webapp/WEB-INF/hapi-fhir-tester-config.xml" />
<param name="file" value="restful-server-example/src/main/webapp/WEB-INF/hapi-fhir-tester-config.xml" />
</macro>
<p>