diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ConditionalParamBinder.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ConditionalParamBinder.java index 1d96e2c88e6..1a418009522 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ConditionalParamBinder.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ConditionalParamBinder.java @@ -83,16 +83,6 @@ class ConditionalParamBinder implements IParameter { if (theRequest.getId() != null && theRequest.getId().hasIdPart()) { return null; } - boolean haveParam = false; - for (String next : theRequest.getParameters().keySet()) { - if (!next.startsWith("_")) { - haveParam=true; - break; - } - } - if (!haveParam) { - return null; - } int questionMarkIndex = theRequest.getCompleteUrl().indexOf('?'); return theRequest.getResourceName() + theRequest.getCompleteUrl().substring(questionMarkIndex); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java index e2c328e761d..5253609d9c7 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java @@ -528,6 +528,40 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { } + @Test + public void testTransactionCreateInlineMatchUrlWithOneMatchLastUpdated() { + Bundle request = new Bundle(); + Observation o = new Observation(); + o.getCode().setText("Some Observation"); + request.addEntry().setResource(o).getRequest().setMethod(HTTPVerb.POST).setIfNoneExist("Observation?_lastUpdated=gt2011-01-01"); + Bundle resp = mySystemDao.transaction(mySrd, request); + assertEquals(1, resp.getEntry().size()); + + BundleEntryComponent respEntry = resp.getEntry().get(0); + assertEquals(Constants.STATUS_HTTP_201_CREATED + " Created", respEntry.getResponse().getStatus()); + assertThat(respEntry.getResponse().getLocation(), containsString("Observation/")); + assertThat(respEntry.getResponse().getLocation(), endsWith("/_history/1")); + assertEquals("1", respEntry.getResponse().getEtag()); + + /* + * Second time should not update + */ + + request = new Bundle(); + o = new Observation(); + o.getCode().setText("Some Observation"); + request.addEntry().setResource(o).getRequest().setMethod(HTTPVerb.POST).setIfNoneExist("Observation?_lastUpdated=gt2011-01-01"); + resp = mySystemDao.transaction(mySrd, request); + assertEquals(1, resp.getEntry().size()); + + respEntry = resp.getEntry().get(0); + assertEquals(Constants.STATUS_HTTP_200_OK + " OK", respEntry.getResponse().getStatus()); + assertThat(respEntry.getResponse().getLocation(), containsString("Observation/")); + assertThat(respEntry.getResponse().getLocation(), endsWith("/_history/1")); + assertEquals("1", respEntry.getResponse().getEtag()); + + } + @Test public void testTransactionCreateInlineMatchUrlWithNoMatches() { String methodName = "testTransactionCreateInlineMatchUrlWithNoMatches"; diff --git a/hapi-fhir-jpaserver-base/src/test/resources/transaction-bundle2.xml b/hapi-fhir-jpaserver-base/src/test/resources/transaction-bundle2.xml new file mode 100644 index 00000000000..1837ad4fbde --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/resources/transaction-bundle2.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + +
+ + +
+
+ + + + + + + +
+
diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/CreateDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/CreateDstu3Test.java index d4e09dc8a97..af26f5a3aab 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/CreateDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/CreateDstu3Test.java @@ -31,6 +31,7 @@ import org.junit.BeforeClass; import org.junit.Test; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; import ca.uhn.fhir.rest.annotation.Create; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Read; @@ -49,33 +50,6 @@ public class CreateDstu3Test { private static int ourPort; private static Server ourServer; - @Test - public void testRead() throws Exception { - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2?_format=xml&_pretty=true"); - HttpResponse status = ourClient.execute(httpGet); - - String responseContent = IOUtils.toString(status.getEntity().getContent()); - IOUtils.closeQuietly(status.getEntity().getContent()); - - ourLog.info("Response was:\n{}", responseContent); - - assertEquals(200, status.getStatusLine().getStatusCode()); - - //@formatter:off - assertThat(responseContent, stringContainsInOrder( - "", - "", - "", - "", - "", - "", - "", - "", - "")); - //@formatter:on - } - /** * #342 */ @@ -93,7 +67,34 @@ public class CreateDstu3Test { assertEquals(400, status.getStatusLine().getStatusCode()); String expected = ""; assertEquals(expected, responseContent); - + + } + + @Test + public void testRead() throws Exception { + + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2?_format=xml&_pretty=true"); + HttpResponse status = ourClient.execute(httpGet); + + String responseContent = IOUtils.toString(status.getEntity().getContent()); + IOUtils.closeQuietly(status.getEntity().getContent()); + + ourLog.info("Response was:\n{}", responseContent); + + assertEquals(200, status.getStatusLine().getStatusCode()); + + //@formatter:off + assertThat(responseContent, stringContainsInOrder( + "", + "", + "", + "", + "", + "", + "", + "", + "")); + //@formatter:on } @Test @@ -108,7 +109,7 @@ public class CreateDstu3Test { ourLog.info("Response was:\n{}", responseContent); assertEquals(200, status.getStatusLine().getStatusCode()); - + //@formatter:off assertThat(responseContent, stringContainsInOrder( "", @@ -121,7 +122,7 @@ public class CreateDstu3Test { "", "")); //@formatter:on - + assertThat(responseContent, not(containsString("http://hl7.org/fhir/"))); } @@ -156,6 +157,11 @@ public class CreateDstu3Test { public static class PatientProvider implements IResourceProvider { + @Create() + public MethodOutcome create(@ResourceParam Patient theIdParam) { + return new MethodOutcome(new IdType("Patient", "1"), true); + } + @Override public Class getResourceType() { return Patient.class; @@ -169,11 +175,6 @@ public class CreateDstu3Test { return p0; } - @Create() - public MethodOutcome read(@ResourceParam Patient theIdParam) { - return new MethodOutcome(new IdType("Patient", "1"), true); - } - @Search public List search() { ArrayList retVal = new ArrayList(); diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/UpdateDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/UpdateDstu3Test.java index 4c0fc590657..7a80fdf028f 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/UpdateDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/UpdateDstu3Test.java @@ -6,6 +6,7 @@ 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; @@ -19,11 +20,13 @@ import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.OperationOutcome; import org.hl7.fhir.dstu3.model.Patient; import org.junit.AfterClass; +import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.primitive.IdDt; +import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.Update; @@ -63,6 +66,53 @@ public class UpdateDstu3Test { } + @Test + public void testUpdateConditional() throws Exception { + + Patient patient = new Patient(); + patient.setId("001"); + patient.addIdentifier().setValue("002"); + + HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient?_id=001"); + httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); + + CloseableHttpResponse status = ourClient.execute(httpPost); + try { + String responseContent = IOUtils.toString(status.getEntity().getContent()); + ourLog.info("Response was:\n{}", responseContent); + assertEquals(200, status.getStatusLine().getStatusCode()); + + assertEquals("Patient?_id=001",ourConditionalUrl); + assertEquals(null, ourId); + } finally { + IOUtils.closeQuietly(status.getEntity().getContent()); + } + + } + + @Test + public void testUpdateNormal() throws Exception { + + Patient patient = new Patient(); + patient.addIdentifier().setValue("002"); + + HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient/001"); + httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); + + CloseableHttpResponse status = ourClient.execute(httpPost); + try { + String responseContent = IOUtils.toString(status.getEntity().getContent()); + ourLog.info("Response was:\n{}", responseContent); + assertEquals(200, status.getStatusLine().getStatusCode()); + + assertNull(ourConditionalUrl); + assertEquals("Patient/001", ourId.getValue()); + } finally { + IOUtils.closeQuietly(status.getEntity().getContent()); + } + + } + @Test public void testUpdateWrongUrlInBody() throws Exception { @@ -81,7 +131,9 @@ public class UpdateDstu3Test { ourLog.info("Response was:\n{}", responseContent); OperationOutcome oo = ourCtx.newXmlParser().parseResource(OperationOutcome.class, responseContent); - assertEquals("Can not update resource, resource body must contain an ID element which matches the request URL for update (PUT) operation - Resource body ID of \"3\" does not match URL ID of \"001\"", oo.getIssueFirstRep().getDiagnostics()); + assertEquals( + "Can not update resource, resource body must contain an ID element which matches the request URL for update (PUT) operation - Resource body ID of \"3\" does not match URL ID of \"001\"", + oo.getIssueFirstRep().getDiagnostics()); assertEquals(400, status.getStatusLine().getStatusCode()); assertNull(status.getFirstHeader("location")); @@ -115,7 +167,15 @@ public class UpdateDstu3Test { } + private static String ourConditionalUrl; + @Before + public void before() { + ourConditionalUrl = null; + ourId = null; + } + + private static IdType ourId; public static class PatientProvider implements IResourceProvider { @@ -125,11 +185,13 @@ public class UpdateDstu3Test { } @Update() - public MethodOutcome updatePatient(@IdParam IdType theId, @ResourceParam Patient thePatient) { - IdType id = theId.withVersion(thePatient.getIdentifierFirstRep().getValue()); + public MethodOutcome updatePatient(@IdParam IdType theId, @ResourceParam Patient thePatient, @ConditionalUrlParam String theConditionalUrl) { + ourId = theId; + ourConditionalUrl = theConditionalUrl; + IdType id = theId != null ? theId.withVersion(thePatient.getIdentifierFirstRep().getValue()) : new IdType("Patient/1"); OperationOutcome oo = new OperationOutcome(); oo.addIssue().setDiagnostics("OODETAILS"); - if (theId.getValueAsString().contains("CREATE")) { + if (id.getValueAsString().contains("CREATE")) { return new MethodOutcome(id, oo, true); } diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 8269dcb7647..c209c3d3c09 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -181,6 +181,11 @@ count would not appear in the self/prev/next links and would not actually be applied to the search results by the server. Thanks to Jim Steele for letting us know! + + Conditional update on server failed to process if the conditional URL did not have any + search parameters that did not start with an underscore. E.g. "Patient?_id=1" failed + even though this is a valid conditional reference. +