diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseFhirDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseFhirDao.java index 17cc6848585..51d353bd0cb 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseFhirDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseFhirDao.java @@ -24,8 +24,10 @@ import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; import java.io.UnsupportedEncodingException; +import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; +import java.net.URL; import java.text.Normalizer; import java.util.ArrayList; import java.util.Collection; @@ -636,7 +638,12 @@ public abstract class BaseFhirDao implements IDao { SearchParameterMap paramMap = new SearchParameterMap(); List parameters; try { - parameters = URLEncodedUtils.parse(new URI(theMatchUrl), "UTF-8"); + String matchUrl = theMatchUrl; + if (matchUrl.indexOf('?') == -1) { + throw new InvalidRequestException("Failed to parse match URL[" + theMatchUrl + "] - Error was: URL does not contain any parameters ('?' not detected)"); + } + matchUrl = matchUrl.replace("|", "%7C"); + parameters = URLEncodedUtils.parse(new URI(matchUrl), "UTF-8"); } catch (URISyntaxException e) { throw new InvalidRequestException("Failed to parse match URL[" + theMatchUrl + "] - Error was: " + e.toString()); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java index 8b1686fff75..663986c3601 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java @@ -200,7 +200,10 @@ public class FhirSystemDaoDstu2 extends BaseFhirSystemDao { String url = extractTransactionUrlOrThrowException(nextEntry, verb); UrlParts parts = parseUrl(verb.getCode(), url); - if (parts.getResourceId() != null) { + if (res.getId().hasIdPart() && isBlank(parts.getResourceId())) { + parts.setResourceId(res.getId().getIdPart()); + } + if (isNotBlank(parts.getResourceId())) { res.setId(new IdDt(parts.getResourceType(), parts.getResourceId())); outcome = resourceDao.update(res, null, false); } else { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchParamExtractorDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchParamExtractorDstu2.java index d067887ee1a..4210057c369 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchParamExtractorDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchParamExtractorDstu2.java @@ -446,6 +446,22 @@ class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implements ISea } systems.add(null); codes.add(nextValue.getValueAsString()); + } else if (nextObject instanceof CodingDt) { + CodingDt nextValue = (CodingDt) nextObject; + if (nextValue.isEmpty()) { + continue; + } + String nextSystem = nextValue.getSystemElement().getValueAsString(); + String nextCode = nextValue.getCodeElement().getValue(); + if (isNotBlank(nextSystem) || isNotBlank(nextCode)) { + systems.add(nextSystem); + codes.add(nextCode); + } + + if (!nextValue.getDisplayElement().isEmpty()) { + systems.add(null); + codes.add(nextValue.getDisplayElement().getValue()); + } } else if (nextObject instanceof CodeableConceptDt) { CodeableConceptDt nextCC = (CodeableConceptDt) nextObject; if (!nextCC.getTextElement().isEmpty()) { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2Test.java index ac4923cc56b..68538dbca7c 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2Test.java @@ -217,8 +217,8 @@ public class FhirResourceDaoDstu2Test { } @Test - public void testCreateWithIfNoneExist() { - String methodName = "testCreateWithIfNoneExist"; + public void testCreateWithIfNoneExistBasic() { + String methodName = "testCreateWithIfNoneExistBasic"; MethodOutcome results; Patient p = new Patient(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java index c88aa5b6c83..1b4e2ddb69c 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java @@ -4,7 +4,15 @@ import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; @@ -13,6 +21,7 @@ import java.util.Set; import java.util.concurrent.TimeUnit; import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.builder.CompareToBuilder; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpGet; @@ -31,6 +40,8 @@ import org.junit.BeforeClass; import org.junit.Test; import org.springframework.context.support.ClassPathXmlApplicationContext; +import com.google.common.net.UrlEscapers; + import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.jpa.dao.IFhirResourceDao; @@ -89,6 +100,7 @@ public class ResourceProviderDstu2Test { private static DaoConfig ourDaoConfig; private static CloseableHttpClient ourHttpClient; private static String ourServerBase; + private static int ourPort; // private static JpaConformanceProvider ourConfProvider; @@ -147,6 +159,7 @@ public class ResourceProviderDstu2Test { String resource = ourFhirCtx.newXmlParser().encodeResourceToString(pt); HttpPost post = new HttpPost(ourServerBase + "/Patient"); + post.addHeader(Constants.HEADER_IF_NONE_EXIST, "Patient?name=" + methodName); post.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); CloseableHttpResponse response = ourHttpClient.execute(post); IdDt id; @@ -166,7 +179,7 @@ public class ResourceProviderDstu2Test { try { assertEquals(200, response.getStatusLine().getStatusCode()); String newIdString = response.getFirstHeader(Constants.HEADER_LOCATION_LC).getValue(); - assertEquals(id.getValue(), newIdString); + assertEquals(id.getValue(), newIdString); // version should match for conditional create } finally { response.close(); } @@ -181,7 +194,7 @@ public class ResourceProviderDstu2Test { pt.addName().addFamily(methodName); String resource = ourFhirCtx.newXmlParser().encodeResourceToString(pt); - HttpPost post = new HttpPost(ourServerBase + "/Patient"); + HttpPost post = new HttpPost(ourServerBase + "/Patient?name=" + methodName); post.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); CloseableHttpResponse response = ourHttpClient.execute(post); IdDt id; @@ -200,7 +213,7 @@ public class ResourceProviderDstu2Test { try { assertEquals(200, response.getStatusLine().getStatusCode()); IdDt newId = new IdDt(response.getFirstHeader(Constants.HEADER_LOCATION_LC).getValue()); - assertEquals(id.toVersionless(), newId.toVersionless()); + assertEquals(id.toVersionless(), newId.toVersionless()); // version shouldn't match for conditional update assertNotEquals(id, newId); } finally { response.close(); @@ -209,8 +222,8 @@ public class ResourceProviderDstu2Test { } @Test - public void testDeleteResourceConditional() throws IOException { - String methodName = "testDeleteResourceConditional"; + public void testDeleteResourceConditional1() throws IOException { + String methodName = "testDeleteResourceConditional1"; Patient pt = new Patient(); pt.addName().addFamily(methodName); @@ -248,6 +261,67 @@ public class ResourceProviderDstu2Test { } + /** + * Based on email from Rene Spronk + */ + @Test + public void testDeleteResourceConditional2() throws IOException, Exception { + String methodName = "testDeleteResourceConditional2"; + + Patient pt = new Patient(); + pt.addName().addFamily(methodName); + pt.addIdentifier().setSystem("http://ghh.org/patient").setValue("555-44-4444"); + String resource = ourFhirCtx.newXmlParser().encodeResourceToString(pt); + + HttpPost post = new HttpPost(ourServerBase + "/Patient"); + post.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); + CloseableHttpResponse response = ourHttpClient.execute(post); + IdDt id; + try { + assertEquals(201, response.getStatusLine().getStatusCode()); + String newIdString = response.getFirstHeader(Constants.HEADER_LOCATION_LC).getValue(); + assertThat(newIdString, startsWith(ourServerBase + "/Patient/")); + id = new IdDt(newIdString); + } finally { + response.close(); + } + + /* + * Try it with a raw socket call. The Apache client won't let us use the unescaped "|" in the URL + * but we want to make sure that works too.. + */ + Socket sock = new Socket(); + try { + sock.connect(new InetSocketAddress("localhost", ourPort)); + sock.getOutputStream().write(("DELETE " + "/fhir/context/Patient?identifier=" + ("http://ghh.org/patient|555-44-4444")).getBytes("UTF-8")); + sock.getOutputStream().write("\n\n".getBytes("UTF-8")); + sock.getOutputStream().flush(); + + InputStream inputStream = sock.getInputStream(); + + byte[] buf = new byte[10000]; + int count; + StringBuilder b = new StringBuilder(); + while ((count = inputStream.read(buf)) > 0) { + b.append(new String(buf, 0, count, Charset.forName("UTF-8"))); + } + String resp = b.toString(); + + ourLog.info("Resp: {}", resp); + } finally { + sock.close(); + } + HttpGet read = new HttpGet(ourServerBase + "/Patient/" + id.getIdPart()); + response = ourHttpClient.execute(read); + try { + ourLog.info(response.toString()); + assertEquals(Constants.STATUS_HTTP_410_GONE, response.getStatusLine().getStatusCode()); + } finally { + response.close(); + } + + } + /** * Test for issue #60 */ @@ -746,8 +820,7 @@ public class ResourceProviderDstu2Test { p1.addIdentifier().setValue("testSearchByIdentifierWithoutSystem01"); IdDt p1Id = ourClient.create().resource(p1).execute().getId(); - Bundle actual = ourClient.search().forResource(Patient.class).where(Patient.IDENTIFIER.exactly().systemAndCode(null, "testSearchByIdentifierWithoutSystem01")).encodedJson().prettyPrint() - .execute(); + Bundle actual = ourClient.search().forResource(Patient.class).where(Patient.IDENTIFIER.exactly().systemAndCode(null, "testSearchByIdentifierWithoutSystem01")).encodedJson().prettyPrint().execute(); assertEquals(1, actual.size()); assertEquals(p1Id.getIdPart(), actual.getEntries().get(0).getResource().getId().getIdPart()); @@ -845,8 +918,7 @@ public class ResourceProviderDstu2Test { assertThat(p1Id.getValue(), containsString("Patient/testUpdateWithClientSuppliedIdWhichDoesntExistRpDstu2/_history")); - Bundle actual = ourClient.search().forResource(Patient.class).where(Patient.IDENTIFIER.exactly().systemAndCode("urn:system", "testUpdateWithClientSuppliedIdWhichDoesntExistRpDstu2")) - .encodedJson().prettyPrint().execute(); + Bundle actual = ourClient.search().forResource(Patient.class).where(Patient.IDENTIFIER.exactly().systemAndCode("urn:system", "testUpdateWithClientSuppliedIdWhichDoesntExistRpDstu2")).encodedJson().prettyPrint().execute(); assertEquals(1, actual.size()); assertEquals(p1Id.getIdPart(), actual.getEntries().get(0).getResource().getId().getIdPart()); @@ -862,13 +934,13 @@ public class ResourceProviderDstu2Test { @SuppressWarnings("unchecked") @BeforeClass public static void beforeClass() throws Exception { - int port = RandomServerPortProvider.findFreePort(); + ourPort = RandomServerPortProvider.findFreePort(); RestfulServer restServer = new RestfulServer(); ourFhirCtx = FhirContext.forDstu2(); restServer.setFhirContext(ourFhirCtx); - ourServerBase = "http://localhost:" + port + "/fhir/context"; + ourServerBase = "http://localhost:" + ourPort + "/fhir/context"; ourAppCtx = new ClassPathXmlApplicationContext("hapi-fhir-server-resourceproviders-dstu2.xml", "fhir-jpabase-spring-test-config.xml"); @@ -886,7 +958,7 @@ public class ResourceProviderDstu2Test { restServer.setPagingProvider(new FifoMemoryPagingProvider(10)); - ourServer = new Server(port); + ourServer = new Server(ourPort); ServletContextHandler proxyHandler = new ServletContextHandler(); proxyHandler.setContextPath("/"); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/SystemProviderTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/SystemProviderTest.java index aa1a953a7d2..d8d657db000 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/SystemProviderTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/SystemProviderTest.java @@ -41,6 +41,14 @@ public class SystemProviderTest { ourLog.info(response); } + @Test + public void testTransactionFromBundle2() throws Exception { + + InputStream bundleRes = SystemProviderTest.class.getResourceAsStream("/transaction_link_patient_eve_temp.xml"); + String bundle = IOUtils.toString(bundleRes); + String response = ourClient.transaction().withBundle(bundle).prettyPrint().execute(); + ourLog.info(response); + } @AfterClass public static void afterClass() throws Exception { diff --git a/hapi-fhir-jpaserver-base/src/test/resources/transaction_link_patient_eve_temp.xml b/hapi-fhir-jpaserver-base/src/test/resources/transaction_link_patient_eve_temp.xml new file mode 100644 index 00000000000..4f6ccb24c92 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/resources/transaction_link_patient_eve_temp.xml @@ -0,0 +1,167 @@ + + + + + + + + + +
Authored on 15-Feb 2015 by GHH.
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + +
+
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+