Documentation updates and bump to 0.5-SNAPSHOT

This commit is contained in:
James Agnew 2014-07-15 18:36:13 -04:00
parent 2adaa4a072
commit 41f160ed67
28 changed files with 586 additions and 295 deletions

View File

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>0.4</version> <version>0.5-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
@ -12,8 +12,8 @@
<artifactId>hapi-fhir-base</artifactId> <artifactId>hapi-fhir-base</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<url>http://hl7api.sourceforge.net/hapi-fhir/</url> <url>http://hl7api.sourceforge.net/hapi-fhir/</url>
<name>HAPI FHIR Library</name> <name>HAPI FHIR - Core Library</name>
<distributionManagement> <distributionManagement>
<site> <site>

View File

@ -6,7 +6,10 @@
<title>HAPI FHIR Changelog</title> <title>HAPI FHIR Changelog</title>
</properties> </properties>
<body> <body>
<release version="0.3" date="2014-May-12" description="This release corrects lots of bugs and introduces the fluent client mode"> <release version="0.5" date="TBD">
<action type="add">
Allow server methods to return wildcard genrric types (e.g. List&lt;? extends IResource&gt;)
</action>
</release> </release>
<release version="0.4" date="2014-Jul-13"> <release version="0.4" date="2014-Jul-13">
<action type="add"> <action type="add">
@ -80,6 +83,8 @@
<action type="add"> <action type="add">
Support added for deleted-entry by/name, by/email, and comment from Tombstones spec Support added for deleted-entry by/name, by/email, and comment from Tombstones spec
</action> </action>
</release>
<release version="0.3" date="2014-May-12" description="This release corrects lots of bugs and introduces the fluent client mode">
</release> </release>
</body> </body>
</document> </document>

View File

@ -20,10 +20,12 @@ package ca.uhn.fhir.model.api;
* #L% * #L%
*/ */
import static org.apache.commons.lang3.StringUtils.*; import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
@ -36,10 +38,11 @@ import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.IntegerDt; import ca.uhn.fhir.model.primitive.IntegerDt;
import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.util.ElementUtil;
public class Bundle extends BaseBundle /* implements IElement */{ public class Bundle extends BaseBundle /* implements IElement */{
private volatile transient Map<IdDt, IResource> myIdToEntries;
//@formatter:off //@formatter:off
/* **************************************************** /* ****************************************************
* NB: add any new fields to the isEmpty() method!!! * NB: add any new fields to the isEmpty() method!!!
@ -75,6 +78,31 @@ public class Bundle extends BaseBundle /* implements IElement */{
return retVal; return retVal;
} }
/**
* Retrieves a resource from a bundle given its logical ID.
* <p>
* <b>Important usage notes</b>: This method ignores base URLs (so passing in an ID of <code>http://foo/Patient/123</code> will return a resource if it has the logical ID of
* <code>http://bar/Patient/123</code>. Also, this method is intended to be used for bundles which have already been populated. It will cache its results for fast performance, but that means that
* modifications to the bundle after this method is called may not be accurately reflected.
* </p>
*
* @param theId The resource ID
* @return Returns the resource with the given ID, or <code>null</code> if none is found
*/
public IResource getResourceById(IdDt theId) {
Map<IdDt, IResource> map = myIdToEntries;
if (map == null) {
map = new HashMap<IdDt, IResource>();
for (BundleEntry next : this.getEntries()) {
if (next.getId().isEmpty() == false) {
map.put(next.getId().toUnqualified(), next.getResource());
}
}
myIdToEntries = map;
}
return map.get(theId.toUnqualified());
}
public List<BundleEntry> getEntries() { public List<BundleEntry> getEntries() {
if (myEntries == null) { if (myEntries == null) {
myEntries = new ArrayList<BundleEntry>(); myEntries = new ArrayList<BundleEntry>();

View File

@ -466,15 +466,16 @@ public class XmlParser extends BaseParser implements IParser {
for (BaseRuntimeChildDefinition nextChild : children) { for (BaseRuntimeChildDefinition nextChild : children) {
if (nextChild instanceof RuntimeChildNarrativeDefinition && !theIncludedResource) { if (nextChild instanceof RuntimeChildNarrativeDefinition && !theIncludedResource) {
INarrativeGenerator gen = myContext.getNarrativeGenerator(); INarrativeGenerator gen = myContext.getNarrativeGenerator();
if (gen != null) { NarrativeDt narr = theResource.getText();
NarrativeDt narr = gen.generateNarrative(theResDef.getResourceProfile(), theResource); if (gen != null && narr.isEmpty()) {
if (narr != null) { narr = gen.generateNarrative(theResDef.getResourceProfile(), theResource);
RuntimeChildNarrativeDefinition child = (RuntimeChildNarrativeDefinition) nextChild; }
String childName = nextChild.getChildNameByDatatype(child.getDatatype()); if (narr != null) {
BaseRuntimeElementDefinition<?> type = child.getChildByName(childName); RuntimeChildNarrativeDefinition child = (RuntimeChildNarrativeDefinition) nextChild;
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, narr, childName, type, null, theIncludedResource); String childName = nextChild.getChildNameByDatatype(child.getDatatype());
continue; BaseRuntimeElementDefinition<?> type = child.getChildByName(childName);
} encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, narr, childName, type, null, theIncludedResource);
continue;
} }
} }

View File

@ -36,11 +36,22 @@ public class MethodOutcome {
myId = theId; myId = theId;
} }
public MethodOutcome(IdDt theId, OperationOutcome theOperationOutcome) {
myId = theId;
myOperationOutcome = theOperationOutcome;
}
/**
* @deprecated Use the constructor which accepts a single IdDt parameter, and include the logical ID and version ID in that IdDt instance
*/
public MethodOutcome(IdDt theId, IdDt theVersionId) { public MethodOutcome(IdDt theId, IdDt theVersionId) {
myId = theId; myId = theId;
myVersionId = theVersionId; myVersionId = theVersionId;
} }
/**
* @deprecated Use the constructor which accepts a single IdDt parameter, and include the logical ID and version ID in that IdDt instance
*/
public MethodOutcome(IdDt theId, IdDt theVersionId, OperationOutcome theOperationOutcome) { public MethodOutcome(IdDt theId, IdDt theVersionId, OperationOutcome theOperationOutcome) {
myId = theId; myId = theId;
myVersionId = theVersionId; myVersionId = theVersionId;

View File

@ -331,7 +331,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
@Override @Override
public T encodedXml() { public T encodedXml() {
myParamEncoding = EncodingEnum.XML; myParamEncoding = EncodingEnum.XML;
return null; return (T) this;
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")

View File

@ -145,6 +145,12 @@ public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBindin
return; return;
} }
if (response != null && response.getId() != null && response.getId().hasResourceType()) {
if (getContext().getResourceDefinition(response.getId().getResourceType()) == null) {
throw new InternalErrorException("Server method returned invalid resource ID: " + response.getId().getValue());
}
}
if (getResourceOperationType() == RestfulOperationTypeEnum.CREATE) { if (getResourceOperationType() == RestfulOperationTypeEnum.CREATE) {
if (response == null) { if (response == null) {
throw new InternalErrorException("Method " + getMethod().getName() + " in type " + getMethod().getDeclaringClass().getCanonicalName() + " returned null, which is not allowed for create operation"); throw new InternalErrorException("Method " + getMethod().getName() + " in type " + getMethod().getDeclaringClass().getCanonicalName() + " returned null, which is not allowed for create operation");

View File

@ -159,6 +159,7 @@ public class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithRe
return retVal; return retVal;
} }
/*
@Override @Override
public boolean incomingServerRequestMatchesMethod(Request theRequest) { public boolean incomingServerRequestMatchesMethod(Request theRequest) {
if (super.incomingServerRequestMatchesMethod(theRequest)) { if (super.incomingServerRequestMatchesMethod(theRequest)) {
@ -176,7 +177,8 @@ public class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithRe
return false; return false;
} }
} }
*/
@Override @Override
protected Set<RequestType> provideAllowableRequestTypes() { protected Set<RequestType> provideAllowableRequestTypes() {
return Collections.singleton(RequestType.PUT); return Collections.singleton(RequestType.PUT);

View File

@ -25,6 +25,7 @@ import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable; import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
public class ReflectionUtil { public class ReflectionUtil {
@ -73,6 +74,9 @@ public class ReflectionUtil {
} else if (firstArg instanceof TypeVariable<?>) { } else if (firstArg instanceof TypeVariable<?>) {
Type decl = ((TypeVariable) firstArg).getBounds()[0]; Type decl = ((TypeVariable) firstArg).getBounds()[0];
return (Class<?>) decl; return (Class<?>) decl;
} else if (firstArg instanceof WildcardType) {
Type decl = ((WildcardType) firstArg).getUpperBounds()[0];
return (Class<?>) decl;
} else { } else {
type = (Class<?>) firstArg; type = (Class<?>) firstArg;
} }

View File

@ -1,7 +1,10 @@
package ca.uhn.fhir.rest.server; package ca.uhn.fhir.rest.server;
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.*; import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import java.io.StringReader; import java.io.StringReader;
import java.util.ArrayList; import java.util.ArrayList;
@ -17,11 +20,9 @@ import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse; import org.apache.http.HttpResponse;
import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.ContentType; import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity; import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.CloseableHttpClient;
@ -43,8 +44,6 @@ import ca.uhn.fhir.model.api.BundleEntry;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.PathSpecification; import ca.uhn.fhir.model.api.PathSpecification;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.Tag;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.dstu.composite.CodingDt; import ca.uhn.fhir.model.dstu.composite.CodingDt;
import ca.uhn.fhir.model.dstu.composite.HumanNameDt; import ca.uhn.fhir.model.dstu.composite.HumanNameDt;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt; import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
@ -973,184 +972,6 @@ public class ResfulServerMethodTest {
} }
@Test
public void testUpdate() throws Exception {
Patient patient = new Patient();
patient.addIdentifier().setValue("002");
HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient/001");
httpPost.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
OperationOutcome oo = new FhirContext().newXmlParser().parseResource(OperationOutcome.class, responseContent);
assertEquals("OODETAILS", oo.getIssueFirstRep().getDetails().getValue());
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue());
}
@Test
public void testUpdateNoResponse() throws Exception {
DiagnosticReport dr = new DiagnosticReport();
dr.addCodedDiagnosis().addCoding().setCode("AAA");
HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/DiagnosticReport/001");
httpPost.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(dr), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
assertEquals(204, status.getStatusLine().getStatusCode());
assertEquals("http://localhost:" + ourPort + "/DiagnosticReport/001/_history/002", status.getFirstHeader("location").getValue());
}
@Test
public void testUpdateWithTagMultiple() throws Exception {
DiagnosticReport dr = new DiagnosticReport();
dr.addCodedDiagnosis().addCoding().setCode("AAA");
HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/DiagnosticReport/001");
httpPost.addHeader("Category", "Dog, Cat");
httpPost.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(dr), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
ourClient.execute(httpPost);
assertEquals(2, ourReportProvider.getLastTags().size());
assertEquals(new Tag("Dog"), ourReportProvider.getLastTags().get(0));
assertEquals(new Tag("Cat"), ourReportProvider.getLastTags().get(1));
httpPost = new HttpPut("http://localhost:" + ourPort + "/DiagnosticReport/001");
httpPost.addHeader("Category", "Dog; label=\"aa\", Cat; label=\"bb\"");
httpPost.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(dr), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
ourClient.execute(httpPost);
assertEquals(2, ourReportProvider.getLastTags().size());
assertEquals(new Tag((String) null, "Dog", "aa"), ourReportProvider.getLastTags().get(0));
assertEquals(new Tag((String) null, "Cat", "bb"), ourReportProvider.getLastTags().get(1));
}
@Test
public void testUpdateWithTagSimple() throws Exception {
DiagnosticReport dr = new DiagnosticReport();
dr.addCodedDiagnosis().addCoding().setCode("AAA");
HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/DiagnosticReport/001");
httpPost.addHeader("Category", "Dog");
httpPost.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(dr), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
ourClient.execute(httpPost);
assertEquals(1, ourReportProvider.getLastTags().size());
assertEquals(new Tag("Dog"), ourReportProvider.getLastTags().get(0));
}
@Test
public void testUpdateWithTagWithScheme() throws Exception {
DiagnosticReport dr = new DiagnosticReport();
dr.addCodedDiagnosis().addCoding().setCode("AAA");
HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/DiagnosticReport/001");
httpPost.addHeader("Category", "Dog; scheme=\"http://foo\"");
httpPost.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(dr), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
ourClient.execute(httpPost);
assertEquals(1, ourReportProvider.getLastTags().size());
assertEquals(new Tag("http://foo", "Dog", null), ourReportProvider.getLastTags().get(0));
httpPost = new HttpPut("http://localhost:" + ourPort + "/DiagnosticReport/001");
httpPost.addHeader("Category", "Dog; scheme=\"http://foo\";");
httpPost.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(dr), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
ourClient.execute(httpPost);
assertEquals(1, ourReportProvider.getLastTags().size());
assertEquals(new Tag("http://foo", "Dog", null), ourReportProvider.getLastTags().get(0));
}
@Test
public void testUpdateWithTagWithSchemeAndLabel() throws Exception {
DiagnosticReport dr = new DiagnosticReport();
dr.addCodedDiagnosis().addCoding().setCode("AAA");
HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/DiagnosticReport/001");
httpPost.addHeader("Category", "Dog; scheme=\"http://foo\"; label=\"aaaa\"");
httpPost.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(dr), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
ourClient.execute(httpPost);
assertEquals(1, ourReportProvider.getLastTags().size());
assertEquals(new Tag("http://foo", "Dog", "aaaa"), ourReportProvider.getLastTags().get(0));
httpPost = new HttpPut("http://localhost:" + ourPort + "/DiagnosticReport/001");
httpPost.addHeader("Category", "Dog; scheme=\"http://foo\"; label=\"aaaa\"; ");
httpPost.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(dr), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
ourClient.execute(httpPost);
assertEquals(1, ourReportProvider.getLastTags().size());
assertEquals(new Tag("http://foo", "Dog", "aaaa"), ourReportProvider.getLastTags().get(0));
}
@Test
public void testUpdateWithVersion() throws Exception {
DiagnosticReport patient = new DiagnosticReport();
patient.getIdentifier().setValue("001");
HttpPut httpPut = new HttpPut("http://localhost:" + ourPort + "/DiagnosticReport/001");
httpPut.addHeader("Content-Location", "/DiagnosticReport/001/_history/004");
httpPut.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpResponse status = ourClient.execute(httpPut);
// String responseContent =
// IOUtils.toString(status.getEntity().getContent());
// ourLog.info("Response was:\n{}", responseContent);
assertEquals(204, status.getStatusLine().getStatusCode());
assertNull(status.getEntity());
assertEquals("http://localhost:" + ourPort + "/DiagnosticReport/001/_history/004", status.getFirstHeader("Location").getValue());
}
@Test()
public void testUpdateWithVersionBadContentLocationHeader() throws Exception {
DiagnosticReport patient = new DiagnosticReport();
patient.getIdentifier().setValue("001");
HttpPut httpPut = new HttpPut("http://localhost:" + ourPort + "/DiagnosticReport/001");
httpPut.addHeader("Content-Location", "/Patient/001/_history/002");
httpPut.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
CloseableHttpResponse results = ourClient.execute(httpPut);
assertEquals(400, results.getStatusLine().getStatusCode());
String responseContent = IOUtils.toString(results.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
}
public void testUpdateWrongResourceType() throws Exception {
// TODO: this method sends in the wrong resource type vs. the URL so it
// should
// give a useful error message (and then make this unit test actually
// run)
Patient patient = new Patient();
patient.addIdentifier().setValue("002");
HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/DiagnosticReport/001");
httpPost.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
ourClient.execute(httpPost);
fail();
}
@Test @Test
public void testValidate() throws Exception { public void testValidate() throws Exception {
@ -1317,7 +1138,7 @@ public class ResfulServerMethodTest {
public static class DummyDiagnosticReportResourceProvider implements IResourceProvider { public static class DummyDiagnosticReportResourceProvider implements IResourceProvider {
private TagList myLastTags;
/** /**
* @param theValue * @param theValue
@ -1339,30 +1160,14 @@ public class ResfulServerMethodTest {
// do nothing // do nothing
} }
public TagList getLastTags() {
return myLastTags;
}
@Override @Override
public Class<? extends IResource> getResourceType() { public Class<? extends IResource> getResourceType() {
return DiagnosticReport.class; return DiagnosticReport.class;
} }
@Update()
public MethodOutcome updateDiagnosticReportWithNoResponse(@IdParam IdDt theId, @VersionIdParam IdDt theVersionId, @ResourceParam DiagnosticReport theDr) {
IdDt id = theId;
IdDt version = theVersionId;
myLastTags = (TagList) theDr.getResourceMetadata().get(ResourceMetadataKeyEnum.TAG_LIST);
return new MethodOutcome(id, version);
}
@Update()
public MethodOutcome updateDiagnosticReportWithVersionAndNoResponse(@IdParam IdDt theId, @ResourceParam DiagnosticReport theDr) {
IdDt id = theId;
IdDt version = new IdDt("002");
myLastTags = (TagList) theDr.getResourceMetadata().get(ResourceMetadataKeyEnum.TAG_LIST);
return new MethodOutcome(id, version);
}
} }
@ -1607,25 +1412,9 @@ public class ResfulServerMethodTest {
return Patient.class; return Patient.class;
} }
@Update()
public MethodOutcome updateDiagnosticReportWithVersion(@IdParam IdDt theId, @VersionIdParam IdDt theVersionId, @ResourceParam DiagnosticOrder thePatient) {
/*
* TODO: THIS METHOD IS NOT USED. It's the wrong type (DiagnosticOrder), so it should cause an exception on startup. Also we should detect if there are multiple resource params on an
* update/create/etc method
*/
IdDt id = theId;
IdDt version = theVersionId;
return new MethodOutcome(id, version);
}
@Update()
public MethodOutcome updatePatient(@IdParam IdDt theId, @ResourceParam Patient thePatient) {
IdDt id = theId;
IdDt version = new IdDt(thePatient.getIdentifierFirstRep().getValue().getValue());
OperationOutcome oo = new OperationOutcome();
oo.addIssue().setDetails("OODETAILS");
return new MethodOutcome(id, version, oo);
}
@Validate() @Validate()
public MethodOutcome validatePatient(@ResourceParam Patient thePatient) { public MethodOutcome validatePatient(@ResourceParam Patient thePatient) {

View File

@ -26,6 +26,7 @@ import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.composite.HumanNameDt; import ca.uhn.fhir.model.dstu.composite.HumanNameDt;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt; import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu.resource.Patient; import ca.uhn.fhir.model.dstu.resource.Patient;
@ -196,7 +197,7 @@ public class ServerFeaturesTest {
assertThat(responseContent, StringContains.containsString("\",\n")); assertThat(responseContent, StringContains.containsString("\",\n"));
} }
@Test @Test
public void testInternalErrorIfNoId() throws Exception { public void testInternalErrorIfNoId() throws Exception {
@ -211,6 +212,21 @@ public class ServerFeaturesTest {
} }
@Test
public void testSearchWithWildcardRetVal() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/?_query=searchWithWildcardRetVal");
httpGet.addHeader("Accept", Constants.CT_FHIR_XML + "; pretty=true");
CloseableHttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
assertThat(responseContent, StringContains.containsString("searchWithWildcardRetVal"));
}
@AfterClass @AfterClass
public static void afterClass() throws Exception { public static void afterClass() throws Exception {
ourServer.stop(); ourServer.stop();
@ -284,6 +300,15 @@ public class ServerFeaturesTest {
return retVal; return retVal;
} }
@Search(queryName="searchWithWildcardRetVal")
public List<? extends IResource> searchWithWildcardRetVal() {
Patient p = new Patient();
p.setId("1234");
p.addName().addFamily("searchWithWildcardRetVal");
return Collections.singletonList(p);
}
/** /**
* Retrieve the resource by its identifier * Retrieve the resource by its identifier
* *

View File

@ -0,0 +1,349 @@
package ca.uhn.fhir.rest.server;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
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.BeforeClass;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.Tag;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.dstu.resource.DiagnosticOrder;
import ca.uhn.fhir.model.dstu.resource.DiagnosticReport;
import ca.uhn.fhir.model.dstu.resource.Observation;
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Update;
import ca.uhn.fhir.rest.annotation.VersionIdParam;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.testutil.RandomServerPortProvider;
/**
* Created by dsotnikov on 2/25/2014.
*/
public class UpdateTest {
private static CloseableHttpClient ourClient;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(UpdateTest.class);
private static int ourPort;
private static DiagnosticReportProvider ourReportProvider;
private static Server ourServer;
@Test
public void testUpdate() throws Exception {
Patient patient = new Patient();
patient.addIdentifier().setValue("002");
HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient/001");
httpPost.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
OperationOutcome oo = new FhirContext().newXmlParser().parseResource(OperationOutcome.class, responseContent);
assertEquals("OODETAILS", oo.getIssueFirstRep().getDetails().getValue());
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue());
}
@Test
public void testUpdateMethodReturnsInvalidId() throws Exception {
Patient patient = new Patient();
patient.addIdentifier().setValue("002");
HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/DiagnosticReport/AAAAAA");
httpPost.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(500, status.getStatusLine().getStatusCode());
}
@Test
public void testUpdateNoResponse() throws Exception {
DiagnosticReport dr = new DiagnosticReport();
dr.addCodedDiagnosis().addCoding().setCode("AAA");
HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/DiagnosticReport/001");
httpPost.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(dr), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
assertEquals(204, status.getStatusLine().getStatusCode());
assertEquals("http://localhost:" + ourPort + "/DiagnosticReport/001/_history/002", status.getFirstHeader("location").getValue());
}
@Test
public void testUpdateWithTagMultiple() throws Exception {
DiagnosticReport dr = new DiagnosticReport();
dr.addCodedDiagnosis().addCoding().setCode("AAA");
HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/DiagnosticReport/001");
httpPost.addHeader("Category", "Dog, Cat");
httpPost.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(dr), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
ourClient.execute(httpPost);
assertEquals(2, ourReportProvider.getLastTags().size());
assertEquals(new Tag("Dog"), ourReportProvider.getLastTags().get(0));
assertEquals(new Tag("Cat"), ourReportProvider.getLastTags().get(1));
httpPost = new HttpPut("http://localhost:" + ourPort + "/DiagnosticReport/001");
httpPost.addHeader("Category", "Dog; label=\"aa\", Cat; label=\"bb\"");
httpPost.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(dr), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
ourClient.execute(httpPost);
assertEquals(2, ourReportProvider.getLastTags().size());
assertEquals(new Tag((String) null, "Dog", "aa"), ourReportProvider.getLastTags().get(0));
assertEquals(new Tag((String) null, "Cat", "bb"), ourReportProvider.getLastTags().get(1));
}
@Test
public void testUpdateWithTagSimple() throws Exception {
DiagnosticReport dr = new DiagnosticReport();
dr.addCodedDiagnosis().addCoding().setCode("AAA");
HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/DiagnosticReport/001");
httpPost.addHeader("Category", "Dog");
httpPost.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(dr), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
ourClient.execute(httpPost);
assertEquals(1, ourReportProvider.getLastTags().size());
assertEquals(new Tag("Dog"), ourReportProvider.getLastTags().get(0));
}
@Test
public void testUpdateWithTagWithScheme() throws Exception {
DiagnosticReport dr = new DiagnosticReport();
dr.addCodedDiagnosis().addCoding().setCode("AAA");
HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/DiagnosticReport/001");
httpPost.addHeader("Category", "Dog; scheme=\"http://foo\"");
httpPost.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(dr), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
ourClient.execute(httpPost);
assertEquals(1, ourReportProvider.getLastTags().size());
assertEquals(new Tag("http://foo", "Dog", null), ourReportProvider.getLastTags().get(0));
httpPost = new HttpPut("http://localhost:" + ourPort + "/DiagnosticReport/001");
httpPost.addHeader("Category", "Dog; scheme=\"http://foo\";");
httpPost.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(dr), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
ourClient.execute(httpPost);
assertEquals(1, ourReportProvider.getLastTags().size());
assertEquals(new Tag("http://foo", "Dog", null), ourReportProvider.getLastTags().get(0));
}
@Test
public void testUpdateWithTagWithSchemeAndLabel() throws Exception {
DiagnosticReport dr = new DiagnosticReport();
dr.addCodedDiagnosis().addCoding().setCode("AAA");
HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/DiagnosticReport/001");
httpPost.addHeader("Category", "Dog; scheme=\"http://foo\"; label=\"aaaa\"");
httpPost.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(dr), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
ourClient.execute(httpPost);
assertEquals(1, ourReportProvider.getLastTags().size());
assertEquals(new Tag("http://foo", "Dog", "aaaa"), ourReportProvider.getLastTags().get(0));
httpPost = new HttpPut("http://localhost:" + ourPort + "/DiagnosticReport/001");
httpPost.addHeader("Category", "Dog; scheme=\"http://foo\"; label=\"aaaa\"; ");
httpPost.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(dr), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
ourClient.execute(httpPost);
assertEquals(1, ourReportProvider.getLastTags().size());
assertEquals(new Tag("http://foo", "Dog", "aaaa"), ourReportProvider.getLastTags().get(0));
}
@Test
public void testUpdateWithVersion() throws Exception {
DiagnosticReport patient = new DiagnosticReport();
patient.getIdentifier().setValue("001");
HttpPut httpPut = new HttpPut("http://localhost:" + ourPort + "/DiagnosticReport/001");
httpPut.addHeader("Content-Location", "/DiagnosticReport/001/_history/004");
httpPut.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpResponse status = ourClient.execute(httpPut);
// String responseContent =
// IOUtils.toString(status.getEntity().getContent());
// ourLog.info("Response was:\n{}", responseContent);
assertEquals(204, status.getStatusLine().getStatusCode());
assertNull(status.getEntity());
assertEquals("http://localhost:" + ourPort + "/DiagnosticReport/001/_history/002", status.getFirstHeader("Location").getValue());
}
@Test()
public void testUpdateWithVersionBadContentLocationHeader() throws Exception {
DiagnosticReport patient = new DiagnosticReport();
patient.getIdentifier().setValue("001");
HttpPut httpPut = new HttpPut("http://localhost:" + ourPort + "/DiagnosticReport/001");
httpPut.addHeader("Content-Location", "/Patient/001/_history/002");
httpPut.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
CloseableHttpResponse results = ourClient.execute(httpPut);
assertEquals(400, results.getStatusLine().getStatusCode());
String responseContent = IOUtils.toString(results.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
}
public void testUpdateWrongResourceType() throws Exception {
// TODO: this method sends in the wrong resource type vs. the URL so it
// should
// give a useful error message (and then make this unit test actually
// run)
Patient patient = new Patient();
patient.addIdentifier().setValue("002");
HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/DiagnosticReport/001");
httpPost.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
ourClient.execute(httpPost);
fail();
}
@AfterClass
public static void afterClass() throws Exception {
ourServer.stop();
}
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = RandomServerPortProvider.findFreePort();
ourServer = new Server(ourPort);
PatientProvider patientProvider = new PatientProvider();
ourReportProvider = new DiagnosticReportProvider();
ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer();
servlet.setResourceProviders(patientProvider, ourReportProvider, new ObservationProvider());
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();
}
public static class DiagnosticReportProvider implements IResourceProvider {
private TagList myLastTags;
private IdDt myLastVersion;
public TagList getLastTags() {
return myLastTags;
}
@Override
public Class<? extends IResource> getResourceType() {
return DiagnosticReport.class;
}
@Update()
public MethodOutcome updateDiagnosticReportWithVersionAndNoResponse(@IdParam IdDt theId, @ResourceParam DiagnosticReport theDr) {
IdDt id = theId;
myLastVersion = id;
if (theId.getValue().contains("AAAAAA")) {
IdDt id2 = new IdDt(id.getIdPart(), "002");
return new MethodOutcome(id2); // this is invalid
}
myLastTags = (TagList) theDr.getResourceMetadata().get(ResourceMetadataKeyEnum.TAG_LIST);
return new MethodOutcome(new IdDt("DiagnosticReport", id.getIdPart(), "002"));
}
}
public static class ObservationProvider implements IResourceProvider{
@Override
public Class<? extends IResource> getResourceType() {
return Observation.class;
}
@Update()
public MethodOutcome updateDiagnosticReportWithVersion(@IdParam IdDt theId, @VersionIdParam IdDt theVersionId, @ResourceParam DiagnosticOrder thePatient) {
/*
* TODO: THIS METHOD IS NOT USED. It's the wrong type (DiagnosticOrder), so it should cause an exception on startup. Also we should detect if there are multiple resource params on an
* update/create/etc method
*/
IdDt id = theId;
IdDt version = theVersionId;
return new MethodOutcome(id, version);
}
}
public static class PatientProvider implements IResourceProvider {
@Override
public Class<? extends IResource> getResourceType() {
return Patient.class;
}
@Update()
public MethodOutcome updatePatient(@IdParam IdDt theId, @ResourceParam Patient thePatient) {
IdDt id = theId;
IdDt version = new IdDt(thePatient.getIdentifierFirstRep().getValue().getValue());
OperationOutcome oo = new OperationOutcome();
oo.addIssue().setDetails("OODETAILS");
return new MethodOutcome(id, version, oo);
}
}
}

View File

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>0.4</version> <version>0.5-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
@ -17,7 +17,7 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId> <artifactId>hapi-fhir-base</artifactId>
<version>0.4</version> <version>0.5-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -724,9 +724,12 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
@Override @Override
public T read(IdDt theId) { public T read(IdDt theId) {
validateResourceTypeAndThrowIllegalArgumentException(theId);
StopWatch w = new StopWatch(); StopWatch w = new StopWatch();
BaseHasResource entity = readEntity(theId); BaseHasResource entity = readEntity(theId);
validateResourceType(entity);
T retVal = toResource(myResourceType, entity); T retVal = toResource(myResourceType, entity);
InstantDt deleted = ResourceMetadataKeyEnum.DELETED_AT.get(retVal); InstantDt deleted = ResourceMetadataKeyEnum.DELETED_AT.get(retVal);
@ -738,8 +741,22 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
return retVal; return retVal;
} }
private void validateResourceType(BaseHasResource entity) {
if (!myResourceName.equals(entity.getResourceType())) {
throw new ResourceNotFoundException("Resource with ID " + entity.getIdDt().getIdPart() + " exists but it is not of type " + myResourceName + ", found resource of type " + entity.getResourceType());
}
}
private void validateResourceTypeAndThrowIllegalArgumentException(IdDt theId) {
if (theId.hasResourceType() && !theId.getResourceType().equals(myResourceName)) {
throw new IllegalArgumentException("Incorrect resource type (" + theId.getResourceType()+ ") for this DAO, wanted: " + myResourceName);
}
}
@Override @Override
public BaseHasResource readEntity(IdDt theId) { public BaseHasResource readEntity(IdDt theId) {
validateResourceTypeAndThrowIllegalArgumentException(theId);
Long pid = translateForcedIdToPid(theId); Long pid = translateForcedIdToPid(theId);
BaseHasResource entity = myEntityManager.find(ResourceTable.class, pid); BaseHasResource entity = myEntityManager.find(ResourceTable.class, pid);
if (theId.hasVersionIdPart()) { if (theId.hasVersionIdPart()) {
@ -761,6 +778,8 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
} }
} }
validateResourceType(entity);
return entity; return entity;
} }

View File

@ -1,7 +1,14 @@
package ca.uhn.fhir.jpa.dao; package ca.uhn.fhir.jpa.dao;
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.contains;
import static org.junit.Assert.*; import static org.hamcrest.Matchers.greaterThan;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
@ -40,9 +47,6 @@ import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException;
import ca.uhn.fhir.rest.gclient.IQuery;
import ca.uhn.fhir.rest.gclient.TokenParam;
import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.IdentifierListParam; import ca.uhn.fhir.rest.param.IdentifierListParam;
import ca.uhn.fhir.rest.param.QualifiedDateParam; import ca.uhn.fhir.rest.param.QualifiedDateParam;
@ -51,6 +55,7 @@ import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.IBundleProvider; import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
public class FhirResourceDaoTest { public class FhirResourceDaoTest {
@ -62,7 +67,6 @@ public class FhirResourceDaoTest {
private static IFhirResourceDao<DiagnosticReport> ourDiagnosticReportDao; private static IFhirResourceDao<DiagnosticReport> ourDiagnosticReportDao;
private static IFhirResourceDao<Organization> ourOrganizationDao; private static IFhirResourceDao<Organization> ourOrganizationDao;
private static IFhirResourceDao<Location> ourLocationDao; private static IFhirResourceDao<Location> ourLocationDao;
private static Date ourTestStarted;
private static IFhirResourceDao<Encounter> ourEncounterDao; private static IFhirResourceDao<Encounter> ourEncounterDao;
private static FhirContext ourFhirCtx; private static FhirContext ourFhirCtx;
@ -252,6 +256,23 @@ public class FhirResourceDaoTest {
assertTrue(updated.before(now)); assertTrue(updated.before(now));
} }
/*
* This ID points to a patient, so we should not be able to return
* othe types with it
*/
try {
ourEncounterDao.read(outcome.getId());
fail();
} catch (IllegalArgumentException e){
//expected
}
try {
ourEncounterDao.read(new IdDt(outcome.getId().getIdPart()));
fail();
} catch (ResourceNotFoundException e){
//expected
}
// Now search by _id // Now search by _id
{ {
SearchParameterMap paramMap = new SearchParameterMap(); SearchParameterMap paramMap = new SearchParameterMap();
@ -1068,7 +1089,6 @@ public class FhirResourceDaoTest {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@BeforeClass @BeforeClass
public static void beforeClass() { public static void beforeClass() {
ourTestStarted = new Date();
ourCtx = new ClassPathXmlApplicationContext("fhir-jpabase-spring-test-config.xml"); ourCtx = new ClassPathXmlApplicationContext("fhir-jpabase-spring-test-config.xml");
ourPatientDao = ourCtx.getBean("myPatientDao", IFhirResourceDao.class); ourPatientDao = ourCtx.getBean("myPatientDao", IFhirResourceDao.class);
ourObservationDao = ourCtx.getBean("myObservationDao", IFhirResourceDao.class); ourObservationDao = ourCtx.getBean("myObservationDao", IFhirResourceDao.class);

View File

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>0.4</version> <version>0.5-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
@ -17,7 +17,7 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jpaserver-base</artifactId> <artifactId>hapi-fhir-jpaserver-base</artifactId>
<version>0.4</version> <version>0.5-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.thymeleaf</groupId> <groupId>org.thymeleaf</groupId>
@ -127,7 +127,7 @@
<plugin> <plugin>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-tinder-plugin</artifactId> <artifactId>hapi-tinder-plugin</artifactId>
<version>0.4</version> <version>0.5-SNAPSHOT</version>
<executions> <executions>
<execution> <execution>
<id>buildclient</id> <id>buildclient</id>

View File

@ -32,6 +32,7 @@ import ca.uhn.fhir.model.dstu.resource.Questionnaire;
import ca.uhn.fhir.model.dstu.valueset.ResourceTypeEnum; import ca.uhn.fhir.model.dstu.valueset.ResourceTypeEnum;
import ca.uhn.fhir.model.primitive.DecimalDt; import ca.uhn.fhir.model.primitive.DecimalDt;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
import ca.uhn.fhir.rest.client.IGenericClient; import ca.uhn.fhir.rest.client.IGenericClient;
import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
@ -194,6 +195,30 @@ private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger
} }
@Test
public void testSaveAndRetrieveExistingNarrative() {
Patient p1 = new Patient();
p1.getText().getDiv().setValueAsString("<div>HELLO WORLD</div>");
p1.addIdentifier().setSystem("urn:system").setValue("testSearchByResourceChain01");
IdDt newId = ourClient.create(p1).getId();
Patient actual = ourClient.read(Patient.class, newId);
assertEquals("<div xmlns=\"http://www.w3.org/1999/xhtml\">HELLO WORLD</div>", actual.getText().getDiv().getValueAsString());
}
@Test
public void testSaveAndRetrieveWithoutNarrative() {
Patient p1 = new Patient();
p1.addIdentifier().setSystem("urn:system").setValue("testSearchByResourceChain01");
IdDt newId = ourClient.create(p1).getId();
Patient actual = ourClient.read(Patient.class, newId);
assertThat(actual.getText().getDiv().getValueAsString(), containsString("<td>Identifier</td><td>testSearchByResourceChain01</td>"));
}
@AfterClass @AfterClass
public static void afterClass() throws Exception { public static void afterClass() throws Exception {
ourServer.stop(); ourServer.stop();
@ -223,6 +248,7 @@ private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger
RestfulServer restServer = new RestfulServer(); RestfulServer restServer = new RestfulServer();
restServer.setResourceProviders(patientRp, questionnaireRp, observationRp, organizationRp); restServer.setResourceProviders(patientRp, questionnaireRp, observationRp, organizationRp);
restServer.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
IFhirSystemDao systemDao = (IFhirSystemDao) ourAppCtx.getBean("mySystemDao", IFhirSystemDao.class); IFhirSystemDao systemDao = (IFhirSystemDao) ourAppCtx.getBean("mySystemDao", IFhirSystemDao.class);

View File

@ -6,13 +6,13 @@
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/resources"/> <wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/resources"/>
<wb-resource deploy-path="/" source-path="/target/m2e-wtp/web-resources"/> <wb-resource deploy-path="/" source-path="/target/m2e-wtp/web-resources"/>
<wb-resource deploy-path="/" source-path="/src/main/webapp" tag="defaultRootSource"/> <wb-resource deploy-path="/" source-path="/src/main/webapp" tag="defaultRootSource"/>
<dependent-module archiveName="hapi-fhir-jpaserver-base-0.4.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/hapi-fhir-jpaserver-base/hapi-fhir-jpaserver-base"> <dependent-module archiveName="hapi-fhir-jpaserver-base-0.5-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/hapi-fhir-jpaserver-base/hapi-fhir-jpaserver-base">
<dependency-type>uses</dependency-type> <dependency-type>uses</dependency-type>
</dependent-module> </dependent-module>
<dependent-module archiveName="hapi-fhir-base-0.4.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/hapi-fhir-base/hapi-fhir-base"> <dependent-module archiveName="hapi-fhir-base-0.5-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/hapi-fhir-base/hapi-fhir-base">
<dependency-type>uses</dependency-type> <dependency-type>uses</dependency-type>
</dependent-module> </dependent-module>
<dependent-module deploy-path="/" handle="module:/overlay/prj/hapi-fhir-tester-overlay?includes=**/**&amp;excludes=META-INF/MANIFEST.MF"> <dependent-module deploy-path="/" handle="module:/overlay/prj/hapi-fhir-testpage-overlay?includes=**/**&amp;excludes=META-INF/MANIFEST.MF">
<dependency-type>consumes</dependency-type> <dependency-type>consumes</dependency-type>
</dependent-module> </dependent-module>
<dependent-module deploy-path="/" handle="module:/overlay/slf/?includes=**/**&amp;excludes=META-INF/MANIFEST.MF"> <dependent-module deploy-path="/" handle="module:/overlay/slf/?includes=**/**&amp;excludes=META-INF/MANIFEST.MF">

View File

@ -4,24 +4,24 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>0.4</version> <version>0.5-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<artifactId>hapi-fhir-jpaserver-uhnfhirtest</artifactId> <artifactId>hapi-fhir-jpaserver-uhnfhirtest</artifactId>
<name>HAPI FHIR JPA Server - Test Project</name> <name>HAPI FHIR - fhirtest.uhn.ca Deployable WAR</name>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jpaserver-base</artifactId> <artifactId>hapi-fhir-jpaserver-base</artifactId>
<version>0.4</version> <version>0.5-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-testpage-overlay</artifactId> <artifactId>hapi-fhir-testpage-overlay</artifactId>
<version>0.4</version> <version>0.5-SNAPSHOT</version>
<type>war</type> <type>war</type>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
@ -29,7 +29,7 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jpaserver-test</artifactId> <artifactId>hapi-fhir-jpaserver-test</artifactId>
<version>0.4</version> <version>0.5-SNAPSHOT</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
@ -179,7 +179,7 @@
<plugin> <plugin>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-tinder-plugin</artifactId> <artifactId>hapi-tinder-plugin</artifactId>
<version>0.4</version> <version>0.5-SNAPSHOT</version>
<executions> <executions>
<execution> <execution>
<id>buildclient</id> <id>buildclient</id>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>0.4</version> <version>0.5-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
@ -18,7 +18,7 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId> <artifactId>hapi-fhir-base</artifactId>
<version>0.4</version> <version>0.5-SNAPSHOT</version>
</dependency> </dependency>
</dependencies> </dependencies>
@ -27,7 +27,7 @@
<plugin> <plugin>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-tinder-plugin</artifactId> <artifactId>hapi-tinder-plugin</artifactId>
<version>0.4</version> <version>0.5-SNAPSHOT</version>
<executions> <executions>
<execution> <execution>
<goals> <goals>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>0.4</version> <version>0.5-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
@ -18,7 +18,7 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId> <artifactId>hapi-fhir-base</artifactId>
<version>0.4</version> <version>0.5-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.thymeleaf</groupId> <groupId>org.thymeleaf</groupId>
@ -41,7 +41,7 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jpaserver-test</artifactId> <artifactId>hapi-fhir-jpaserver-test</artifactId>
<version>0.4</version> <version>0.5-SNAPSHOT</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>

View File

@ -1,6 +1,9 @@
package ca.uhn.fhir.to; package ca.uhn.fhir.to;
import static org.apache.commons.lang3.StringUtils.*; import static org.apache.commons.lang3.StringUtils.defaultIfEmpty;
import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.StringWriter; import java.io.StringWriter;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
@ -14,7 +17,6 @@ import java.util.Map;
import java.util.TreeSet; import java.util.TreeSet;
import javax.json.Json; import javax.json.Json;
import javax.json.JsonWriter;
import javax.json.stream.JsonGenerator; import javax.json.stream.JsonGenerator;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -34,9 +36,6 @@ import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.thymeleaf.TemplateEngine; import org.thymeleaf.TemplateEngine;
import com.google.common.escape.Escaper;
import com.google.common.escape.Escapers;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.Bundle; import ca.uhn.fhir.model.api.Bundle;

View File

@ -26,17 +26,7 @@ function generateHapiSearch(json, container) {
var indented = $('<div />', {'class': 'clientCodeIndent'}); var indented = $('<div />', {'class': 'clientCodeIndent'});
container.append(indented); container.append(indented);
if (json.pretty) {
indented.append($('<span />', {'class': 'clientCodeMain'}).text('.setPrettyPrint(' + json.pretty + ')'));
indented.append($('<br/>'));
}
if (json.format) {
indented.append($('<span />', {'class': 'clientCodeMain'}).text('.setEncoding(EncodingEnum.' + json.format.toUpperCase() + ')'));
indented.append($('<br/>'));
}
for (var i = 0; i < json.params.length; i++) { for (var i = 0; i < json.params.length; i++) {
var nextParam = json.params[i]; var nextParam = json.params[i];
var paramLine = null; var paramLine = null;
@ -53,15 +43,22 @@ function generateHapiSearch(json, container) {
} }
} else if (nextParam.type == 'number') { } else if (nextParam.type == 'number') {
paramLine = '.where(new NumberParam("' + nextParam.name + '").exactly().value("' + nextParam.value + '"))'; paramLine = '.where(new NumberParam("' + nextParam.name + '").exactly().value("' + nextParam.value + '"))';
} else if (nextParam.type == 'reference') {
if (nextParam.qualifier == '') {
if (nextParam.name.indexOf('.') == -1) {
paramLine = '.where(new ReferenceParam("' + nextParam.name + '").hasId("' + nextParam.value + '"))';
}
}
} else if (nextParam.type == 'date') { } else if (nextParam.type == 'date') {
var dateQual = nextParam.value.indexOf('T') == -1 ? 'day' : 'second';
if (nextParam.value.substring(0,2) == '>=') { if (nextParam.value.substring(0,2) == '>=') {
paramLine = '.where(new DateParam("' + nextParam.name + '").afterOrEquals().value("' + nextParam.value.substring(2) + '"))'; paramLine = '.where(new DateParam("' + nextParam.name + '").afterOrEquals().' + dateQual + '("' + nextParam.value.substring(2) + '"))';
} else if (nextParam.value.substring(0,1) == '>') { } else if (nextParam.value.substring(0,1) == '>') {
paramLine = '.where(new DateParam("' + nextParam.name + '").after().value("' + nextParam.value.substring(1) + '"))'; paramLine = '.where(new DateParam("' + nextParam.name + '").after().' + dateQual + '("' + nextParam.value.substring(1) + '"))';
} else if (nextParam.value.substring(0,2) == '<=') { } else if (nextParam.value.substring(0,2) == '<=') {
paramLine = '.where(new DateParam("' + nextParam.name + '").beforeOrEquals().value("' + nextParam.value.substring(2) + '"))'; paramLine = '.where(new DateParam("' + nextParam.name + '").beforeOrEquals().' + dateQual + '("' + nextParam.value.substring(2) + '"))';
} else if (nextParam.value.substring(0,1) == '<') { } else if (nextParam.value.substring(0,1) == '<') {
paramLine = '.where(new DateParam("' + nextParam.name + '").before().value("' + nextParam.value.substring(1) + '"))'; paramLine = '.where(new DateParam("' + nextParam.name + '").before().' + dateQual + '("' + nextParam.value.substring(1) + '"))';
} }
} }
if (paramLine != null) { if (paramLine != null) {
@ -75,6 +72,16 @@ function generateHapiSearch(json, container) {
indented.append($('<br/>')); indented.append($('<br/>'));
} }
if (json.pretty && json.pretty == 'true') {
indented.append($('<span />', {'class': 'clientCodeMain'}).text('.prettyPrint()'));
indented.append($('<br/>'));
}
if (json.format) {
indented.append($('<span />', {'class': 'clientCodeMain'}).text('.encoded' + json.format.substring(0,1).toUpperCase() + json.format.substring(1).toLowerCase() + '()'));
indented.append($('<br/>'));
}
if (json.limit) { if (json.limit) {
indented.append($('<span />', {'class': 'clientCodeMain'}).text('.limitTo(' + json.limit + ')')); indented.append($('<span />', {'class': 'clientCodeMain'}).text('.limitTo(' + json.limit + ')'));
indented.append($('<br/>')); indented.append($('<br/>'));

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>0.4</version> <version>0.5-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
@ -20,7 +20,7 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId> <artifactId>hapi-fhir-base</artifactId>
<version>0.4</version> <version>0.5-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>0.4</version> <version>0.5-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
@ -17,7 +17,7 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId> <artifactId>hapi-fhir-base</artifactId>
<version>0.4</version> <version>0.5-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
@ -40,7 +40,7 @@
<plugin> <plugin>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-tinder-plugin</artifactId> <artifactId>hapi-tinder-plugin</artifactId>
<version>0.4</version> <version>0.5-SNAPSHOT</version>
<executions> <executions>
<execution> <execution>
<id>custom-structs</id> <id>custom-structs</id>

View File

@ -11,7 +11,7 @@
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<packaging>pom</packaging> <packaging>pom</packaging>
<version>0.4</version> <version>0.5-SNAPSHOT</version>
<name>HAPI-FHIR</name> <name>HAPI-FHIR</name>
<url>http://hl7api.sourceforge.net/hapi-fhir/</url> <url>http://hl7api.sourceforge.net/hapi-fhir/</url>

View File

@ -14,7 +14,7 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId> <artifactId>hapi-fhir-base</artifactId>
<version>0.4</version> <version>0.5-SNAPSHOT</version>
</dependency> </dependency>
<!-- HAPI-FHIR uses Logback for logging support. The logback library is included automatically by Maven as a part of the hapi-fhir-base dependency, but you also need to include a <!-- HAPI-FHIR uses Logback for logging support. The logback library is included automatically by Maven as a part of the hapi-fhir-base dependency, but you also need to include a

View File

@ -1 +1 @@
mvn versions:set -DnewVersion=0.4 mvn versions:set -DnewVersion=0.5-SNAPSHOT