Some unit test fixes, still some failures

This commit is contained in:
jamesagnew 2014-03-21 08:12:44 -04:00
parent 2c425ca144
commit 79bdd94f72
14 changed files with 309 additions and 104 deletions

View File

@ -27,6 +27,9 @@ public class FhirContext {
this(toCollection(theResourceType));
}
public FhirContext() {
}
public FhirContext(Class<?>... theResourceTypes) {
this(toCollection(theResourceTypes));
}

View File

@ -1,14 +1,45 @@
package ca.uhn.fhir.model.api;
import java.util.Calendar;
import java.util.Date;
import org.apache.commons.lang3.time.DateUtils;
public enum TemporalPrecisionEnum {
YEAR(Calendar.YEAR),
MONTH(Calendar.MONTH),
DAY(Calendar.DATE),
SECOND(Calendar.SECOND),
MILLI(Calendar.MILLISECOND),
YEAR(Calendar.YEAR) {
@Override
public Date add(Date theInput, int theAmount) {
return DateUtils.addYears(theInput, theAmount);
}
},
MONTH(Calendar.MONTH) {
@Override
public Date add(Date theInput, int theAmount) {
return DateUtils.addMonths(theInput, theAmount);
}
},
DAY(Calendar.DATE) {
@Override
public Date add(Date theInput, int theAmount) {
return DateUtils.addDays(theInput, theAmount);
}
},
SECOND(Calendar.SECOND) {
@Override
public Date add(Date theInput, int theAmount) {
return DateUtils.addSeconds(theInput, theAmount);
}
},
MILLI(Calendar.MILLISECOND) {
@Override
public Date add(Date theInput, int theAmount) {
return DateUtils.addMilliseconds(theInput, theAmount);
}
},
;
private int myCalendarConstant;
@ -17,7 +48,10 @@ public enum TemporalPrecisionEnum {
myCalendarConstant = theCalendarConstant;
}
public abstract Date add(Date theInput, int theAmount);
public int getCalendarConstant() {
return myCalendarConstant;
}
}

View File

@ -30,6 +30,7 @@ public abstract class BaseParser implements IParser {
public IResource parseResource(Reader theReader) throws ConfigurationException, DataFormatException {
return parseResource(null, theReader);
}
public void containResourcesForEncoding(IResource theResource) {
List<ResourceReferenceDt> allElements = theResource.getAllPopulatedChildElementsOfType(ResourceReferenceDt.class);

View File

@ -28,7 +28,7 @@ public interface IParser {
<T extends IResource> T parseResource(Class<T> theResourceType, String theMessageString);
IResource parseResource(Class<? extends IResource> theResourceType, Reader theReader);
<T extends IResource> T parseResource(Class<T> theResourceType, Reader theReader);
IParser setPrettyPrint(boolean thePrettyPrint);

View File

@ -4,6 +4,7 @@ import static org.apache.commons.lang3.StringUtils.*;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
@ -65,21 +66,6 @@ public class JsonParser extends BaseParser implements IParser {
return stringWriter.toString();
}
private void writeTagWithTextNode(JsonGenerator theEventWriter, String theElementName, StringDt theStringDt) {
if (StringUtils.isNotBlank(theStringDt.getValue())) {
theEventWriter.write(theElementName, theStringDt.getValue());
} else {
theEventWriter.writeNull(theElementName);
}
}
private void writeOptionalTagWithTextNode(JsonGenerator theEventWriter, String theElementName, IPrimitiveDatatype<?> theInstantDt) {
String str = theInstantDt.getValueAsString();
if (StringUtils.isNotBlank(str)) {
theEventWriter.write(theElementName, theInstantDt.getValueAsString());
}
}
@Override
public void encodeBundleToWriter(Bundle theBundle, Writer theWriter) throws IOException {
JsonGenerator eventWriter = createJsonGenerator(theWriter);
@ -132,26 +118,6 @@ public class JsonParser extends BaseParser implements IParser {
eventWriter.close();
}
private void writeAuthor(BaseBundle theBundle, JsonGenerator eventWriter) {
if (StringUtils.isNotBlank(theBundle.getAuthorName().getValue())) {
eventWriter.writeStartArray("author");
eventWriter.writeStartObject();
writeTagWithTextNode(eventWriter, "name", theBundle.getAuthorName());
writeOptionalTagWithTextNode(eventWriter, "uri", theBundle.getAuthorUri());
eventWriter.writeEnd();
eventWriter.writeEnd();
}
}
private void writeAtomLink(JsonGenerator theEventWriter, String theRel, StringDt theLink) {
if (isNotBlank(theLink.getValue())) {
theEventWriter.writeStartObject();
theEventWriter.write("rel", theRel);
theEventWriter.write("href", theLink.getValue());
theEventWriter.writeEnd();
}
}
@Override
public String encodeResourceToString(IResource theResource) throws DataFormatException, IOException {
Writer stringWriter = new StringWriter();
@ -172,16 +138,6 @@ public class JsonParser extends BaseParser implements IParser {
// }
}
private JsonGenerator createJsonGenerator(Writer theWriter) {
Map<String, Object> properties = new HashMap<String, Object>(1);
if (myPrettyPrint) {
properties.put(JsonGenerator.PRETTY_PRINTING, myPrettyPrint);
}
JsonGeneratorFactory jgf = Json.createGeneratorFactory(properties);
JsonGenerator eventWriter = jgf.createGenerator(theWriter);
return eventWriter;
}
@Override
public Bundle parseBundle(Reader theReader) {
// TODO Auto-generated method stub
@ -195,17 +151,22 @@ public class JsonParser extends BaseParser implements IParser {
}
@Override
public IResource parseResource(Class<? extends IResource> theResourceType, Reader theReader) {
public <T extends IResource> T parseResource(Class<T> theResourceType, String theMessageString) {
return parseResource(theResourceType, new StringReader(theMessageString));
}
@Override
public <T extends IResource> T parseResource(Class<T> theResourceType, Reader theReader) {
JsonReader reader = Json.createReader(theReader);
JsonObject object = reader.readObject();
JsonValue resourceTypeObj = object.remove("resourceType");
JsonValue resourceTypeObj = object.get("resourceType");
assertObjectOfType(resourceTypeObj, JsonValue.ValueType.STRING, "resourceType");
String resourceType = ((JsonString)resourceTypeObj).getString();
String resourceType = ((JsonString) resourceTypeObj).getString();
if (theResourceType != null) {
RuntimeResourceDefinition def = myContext.getResourceDefinition(theResourceType);
}else {
} else {
RuntimeResourceDefinition def = myContext.getResourceDefinition(resourceType);
}
@ -239,25 +200,11 @@ public class JsonParser extends BaseParser implements IParser {
}
}
return null;
}
private void assertObjectOfType(JsonValue theResourceTypeObj, ValueType theValueType, String thePosition) {
if (theResourceTypeObj.getValueType() != theValueType) {
throw new DataFormatException("Invalid content of element " + thePosition + ", expected " + theValueType);
}
}
@Override
public <T extends IResource> T parseResource(Class<T> theResourceType, String theMessageString) {
// TODO Auto-generated method stub
return null;
}
@Override
public IParser setPrettyPrint(boolean thePrettyPrint) {
myPrettyPrint = thePrettyPrint;
@ -279,6 +226,22 @@ public class JsonParser extends BaseParser implements IParser {
}
}
private void assertObjectOfType(JsonValue theResourceTypeObj, ValueType theValueType, String thePosition) {
if (theResourceTypeObj.getValueType() != theValueType) {
throw new DataFormatException("Invalid content of element " + thePosition + ", expected " + theValueType);
}
}
private JsonGenerator createJsonGenerator(Writer theWriter) {
Map<String, Object> properties = new HashMap<String, Object>(1);
if (myPrettyPrint) {
properties.put(JsonGenerator.PRETTY_PRINTING, myPrettyPrint);
}
JsonGeneratorFactory jgf = Json.createGeneratorFactory(properties);
JsonGenerator eventWriter = jgf.createGenerator(theWriter);
return eventWriter;
}
private void encodeChildElementToStreamWriter(JsonGenerator theWriter, IElement theValue, BaseRuntimeElementDefinition<?> theChildDef, String theChildName) throws IOException {
switch (theChildDef.getChildType()) {
@ -318,7 +281,7 @@ public class JsonParser extends BaseParser implements IParser {
if (theChildName != null) {
theWriter.writeStartObject(theChildName);
} else {
theWriter.flush();//TODO: remove
theWriter.flush();// TODO: remove
theWriter.writeStartObject();
}
encodeCompositeElementToStreamWriter(theValue, theWriter, childCompositeDef);
@ -424,7 +387,7 @@ public class JsonParser extends BaseParser implements IParser {
}
currentChildName = childName;
} else {
theEventWriter.flush();//TODO: remove
theEventWriter.flush();// TODO: remove
encodeChildElementToStreamWriter(theEventWriter, nextValue, childDef, null);
}
@ -462,7 +425,7 @@ public class JsonParser extends BaseParser implements IParser {
}
if (!haveContent) {
// theEventWriter.writeEnd();
// theEventWriter.writeEnd();
theEventWriter.flush(); // TODO: remove
theEventWriter.writeNull();
}
@ -520,7 +483,7 @@ public class JsonParser extends BaseParser implements IParser {
}
theEventWriter.write("resourceType", resDef.getName());
if (theResource.getId() !=null && isNotBlank(theResource.getId().getValue())) {
if (theResource.getId() != null && isNotBlank(theResource.getId().getValue())) {
theEventWriter.write("id", theResource.getId().getValue());
}
@ -559,6 +522,41 @@ public class JsonParser extends BaseParser implements IParser {
theWriter.writeEnd();
}
private void writeAtomLink(JsonGenerator theEventWriter, String theRel, StringDt theLink) {
if (isNotBlank(theLink.getValue())) {
theEventWriter.writeStartObject();
theEventWriter.write("rel", theRel);
theEventWriter.write("href", theLink.getValue());
theEventWriter.writeEnd();
}
}
private void writeAuthor(BaseBundle theBundle, JsonGenerator eventWriter) {
if (StringUtils.isNotBlank(theBundle.getAuthorName().getValue())) {
eventWriter.writeStartArray("author");
eventWriter.writeStartObject();
writeTagWithTextNode(eventWriter, "name", theBundle.getAuthorName());
writeOptionalTagWithTextNode(eventWriter, "uri", theBundle.getAuthorUri());
eventWriter.writeEnd();
eventWriter.writeEnd();
}
}
private void writeOptionalTagWithTextNode(JsonGenerator theEventWriter, String theElementName, IPrimitiveDatatype<?> theInstantDt) {
String str = theInstantDt.getValueAsString();
if (StringUtils.isNotBlank(str)) {
theEventWriter.write(theElementName, theInstantDt.getValueAsString());
}
}
private void writeTagWithTextNode(JsonGenerator theEventWriter, String theElementName, StringDt theStringDt) {
if (StringUtils.isNotBlank(theStringDt.getValue())) {
theEventWriter.write(theElementName, theStringDt.getValue());
} else {
theEventWriter.writeNull(theElementName);
}
}
private class HeldExtension {
private UndeclaredExtension myUndeclaredExtension;

View File

@ -115,8 +115,8 @@ class ParserState<T extends IElement> {
* @param theResourceType
* May be null
*/
public static ParserState<IResource> getPreResourceInstance(Class<? extends IResource> theResourceType, FhirContext theContext) throws DataFormatException {
ParserState<IResource> retVal = new ParserState<IResource>(theContext);
public static <T extends IResource> ParserState<T> getPreResourceInstance(Class<T> theResourceType, FhirContext theContext) throws DataFormatException {
ParserState<T> retVal = new ParserState<T>(theContext);
retVal.push(retVal.new PreResourceState(theResourceType));
return retVal;
}

View File

@ -138,6 +138,10 @@ public class XmlParser extends BaseParser implements IParser {
@Override
public String encodeResourceToString(IResource theResource) throws DataFormatException {
if (theResource==null) {
throw new NullPointerException("Resource can not be null");
}
Writer stringWriter = new StringWriter();
encodeResourceToWriter(theResource, stringWriter);
return stringWriter.toString();
@ -183,7 +187,7 @@ public class XmlParser extends BaseParser implements IParser {
}
@Override
public IResource parseResource(Class<? extends IResource> theResourceType, Reader theReader) {
public <T extends IResource> T parseResource(Class<T> theResourceType, Reader theReader) {
XMLEventReader streamReader;
try {
streamReader = myXmlInputFactory.createXMLEventReader(theReader);
@ -545,8 +549,8 @@ public class XmlParser extends BaseParser implements IParser {
return doXmlLoop(theStreamReader, parserState);
}
private IResource parseResource(Class<? extends IResource> theResourceType, XMLEventReader theStreamReader) {
ParserState<IResource> parserState = ParserState.getPreResourceInstance(theResourceType, myContext);
private <T extends IResource> T parseResource(Class<T> theResourceType, XMLEventReader theStreamReader) {
ParserState<T> parserState = ParserState.getPreResourceInstance(theResourceType, myContext);
return doXmlLoop(theStreamReader, parserState);
}

View File

@ -6,6 +6,7 @@ import java.util.Date;
import java.util.List;
import ca.uhn.fhir.model.api.IQueryParameterAnd;
import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public class DateRangeParam implements IQueryParameterAnd {
@ -91,13 +92,39 @@ public class DateRangeParam implements IQueryParameterAnd {
}
public Date getLowerBoundAsInstant() {
// TODO: account for precision
return myLowerBound.getValue();
Date retVal = myLowerBound.getValue();
if (myLowerBound.getComparator() != null) {
switch (myLowerBound.getComparator()) {
case GREATERTHAN:
retVal = myLowerBound.getPrecision().add(retVal, 1);
break;
case GREATERTHAN_OR_EQUALS:
break;
case LESSTHAN:
case LESSTHAN_OR_EQUALS:
throw new IllegalStateException("Unvalid lower bound comparator: " + myLowerBound.getComparator());
}
}
return retVal;
}
public Date getUpperBoundAsInstant() {
// TODO: account for precision
return myUpperBound.getValue();
Date retVal = myUpperBound.getValue();
if (myUpperBound.getComparator() != null) {
switch (myUpperBound.getComparator()) {
case LESSTHAN:
retVal = new Date(retVal.getTime() - 1L);
break;
case LESSTHAN_OR_EQUALS:
retVal = myUpperBound.getPrecision().add(retVal, 1);
retVal = new Date(retVal.getTime() - 1L);
break;
case GREATERTHAN_OR_EQUALS:
case GREATERTHAN:
throw new IllegalStateException("Unvalid upper bound comparator: " + myUpperBound.getComparator());
}
}
return retVal;
}
}

View File

@ -17,12 +17,7 @@ public class FhirContextIntro {
public static void creatingContext() {
// START SNIPPET: creatingContext
/*
* Create a FhirContex instance which is able to parse and encode
* the Patient and Observation resources, and any resources referenced
* from these.
*/
FhirContext ctx = new FhirContext(Patient.class, Observation.class);
FhirContext ctx = new FhirContext();
// END SNIPPET: creatingContext
}
@ -103,7 +98,7 @@ StringDt familyName = patient.getName().get(0).getFamily().get(0);
CodeDt gender = patient.getGender().getCoding().get(0).getCode();
/*
* The
* The various datatype classes have accessors called getValue()
*/
System.out.println(patientId.getValue()); // PRP1660
System.out.println(familyName.getValue()); // Cardinal

View File

@ -24,6 +24,7 @@ import ca.uhn.fhir.rest.annotation.Required;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.client.ITestClient;
import ca.uhn.fhir.rest.param.CodingListParam;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.QualifiedDateParam;
import ca.uhn.fhir.rest.server.IResourceProvider;
@ -161,6 +162,22 @@ public List<DiagnosticReport> getDiagnosticReport( @Required(name=DiagnosticRepo
}
//END SNIPPET: pathSpec
//START SNIPPET: dateRange
@Search()
public List<Observation> getObservationsByDateRange(@Required(name="subject.identifier") IdentifierDt theSubjectId,
@Required(name=Observation.SP_DATE) DateRangeParam theRange) {
List<Observation> retVal = new ArrayList<Observation>();
// The following two will be set as the start and end
// of the range specified by the query parameter
Date from = theRange.getLowerBoundAsInstant();
Date to = theRange.getUpperBoundAsInstant();
// ... populate ...
return retVal;
}
//END SNIPPET: dateRange
private DiagnosticReport loadSomeDiagnosticReportFromDatabase(IdentifierDt theIdentifier) {
return null;
}

View File

@ -159,15 +159,15 @@
may be optional. To add a second required parameter, annotate the
parameter with
<a href="./apidocs/ca/uhn/fhir/rest/server/parameters/Required.html">@Required</a>.
To add an optional parameter (which will be pased in as <code>null</code> if no value
To add an optional parameter (which will be passed in as <code>null</code> if no value
is supplied), annotate the method with
<a href="./apidocs/ca/uhn/fhir/rest/server/parameters/Required.html">@Optional</a>.
</p>
<p>
You may annotate a method with any combination of as many @Required and as many @Optional
parameters as you want. It is valid to have no @Required parameters, or
no @Optional parameters as well.
parameters as you want. It is valid to have only @Required parameters, or
only @Optional parameters, or any combination of the two.
</p>
<macro name="snippet">
@ -230,7 +230,7 @@
</subsection>
<subsection name="Date Parameters">
<subsection name="Date Parameters - Simple">
<p>
The FHIR specification provides a sytax for specifying
@ -270,6 +270,46 @@
</subsection>
<subsection name="Date Parameters - Ranges">
<p>
A common scenario in searches is to allow searching for resources
with values (i.e timestamps) within a range of dates.
</p>
<p>
FHIR allows for multiple parameters with the same key, and interprets
these as being an "AND" set. So, for example, a range of<br/>
<code>DATE &gt;= 2011-01-01</code> and <code>DATE &lt; 2011-02-01</code><br/>
can be interpreted as any date within January 2011.
</p>
<p>
The following snippet shows how to accept such a range, and combines it
with a specific identifier, which is a common scenario. (i.e. Give me a list
of observations for a specific patient within a given date range)
</p>
<macro name="snippet">
<param name="id" value="dateRange" />
<param name="file" value="src/site/example/java/example/RestfulPatientResourceProviderMore.java" />
</macro>
<p>
Example URL to invoke this method:<br/>
<code>http://fhir.example.com/Observation?subject.identifier=7000135&amp;date=&gt;=2011-01-01&amp;date=&lt;2011-02-01</code>
</p>
<p>
Invoking a client of thie type involves the following syntax:
</p>
<macro name="snippet">
<param name="id" value="dateClient" />
<param name="file" value="src/site/example/java/example/RestfulPatientResourceProviderMore.java" />
</macro>
</subsection>
<subsection name="Resource Includes (_include)">
<p>

View File

@ -80,11 +80,13 @@ public class JsonParserTest {
}
@Test
public void testSimpleParse() throws DataFormatException, IOException {
String msg = IOUtils.toString(XmlParser.class.getResourceAsStream("/example-patient-general.json"));
FhirContext ctx = new FhirContext(Patient.class);
IParser p = ctx.newJsonParser();
ourLog.info("Reading in message: {}", msg);
Patient res = p.parseResource(Patient.class, msg);
String encoded = ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(res);

View File

@ -82,7 +82,7 @@ public class XmlParserTest {
String str = p.encodeResourceToString(rpt);
ourLog.info(str);
assertThat(str, StringContains.containsString("<div>AAA</div>"));
assertThat(str, StringContains.containsString("<div xmlns=\"\">AAA</div>"));
assertThat(str, StringContains.containsString("reference value=\"#"));
int idx = str.indexOf("reference value=\"#") + "reference value=\"#".length();

View File

@ -0,0 +1,84 @@
package ca.uhn.fhir.rest.param;
import static org.junit.Assert.*;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import org.junit.BeforeClass;
import org.junit.Test;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public class DateRangeParamTest {
private static SimpleDateFormat ourFmt;
@BeforeClass
public static void beforeClass() {
ourFmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSS");
}
@Test
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());
}
@Test
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 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());
}
@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());
}
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);
}
private static DateRangeParam create(String theLower, String theUpper) throws InvalidRequestException {
DateRangeParam p = new DateRangeParam();
List<List<String>> tokens=new ArrayList<List<String>>();
tokens.add(Collections.singletonList(theLower));
tokens.add(Collections.singletonList(theUpper));
p.setValuesAsQueryTokens(tokens);
return p;
}
}