Accept conditional updates with _id as parameter

This commit is contained in:
James Agnew 2016-05-16 19:11:25 -04:00
parent 309b67c010
commit 7942d69d5d
6 changed files with 198 additions and 49 deletions

View File

@ -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);

View File

@ -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";

View File

@ -0,0 +1,57 @@
<Bundle xmlns="http://hl7.org/fhir">
<type value="transaction"/>
<id value="f9935843-19a2-4a1a-4016-7bea52de77f8"/>
<entry>
<fullUrl value="urn:uuid:47709cc7-b3ec-4abc-9d26-3df3d3d57907"/>
<resource>
<Practitioner xmlns="http://hl7.org/fhir">
<identifier>
<use value="official"/>
<!--Reuse value from CDA -->
<system value="urn:oid:2.16.840.1.113883.2.4.6.3"/>
<!--Reuse OIDs from CDA -->
<!-- BSN identification system -->
<value value="567IUI51C157"/>
</identifier>
<name>
<use value="official"/>
<family value="Heps"/>
<given value="Simone"/>
<suffix value="MD"/>
</name>
<telecom>
<system value="phone"/>
<value value="020556936"/>
<use value="work"/>
</telecom>
<telecom>
<system value="email"/>
<value value="S.M.Heps@bmc.nl"/>
<use value="work"/>
</telecom>
<telecom>
<system value="fax"/>
<value value="0205669283"/>
<use value="work"/>
</telecom>
<address>
<use value="work"/>
<line value="Galapagosweg 91"/>
<city value="Den Burg"/>
<postalCode value="9105 PZ"/>
<country value="NLD"/>
<!-- ISO 3166 Codes (Countries) -->
</address>
<gender value="female"/>
<birthDate value="1971-11-07"/>
</Practitioner>
</resource>
<request>
<!--For this entry we only want to create the practitioner based on the entry data if we dont find one -->
<method value="POST"/>
<url value="Practitioner"/>
<!--Use DrProviderNumber Paul to add more syntax below -->
<ifNoneExist value="Practitioner?identifier=567IUI51C157"/>
</request>
</entry>
</Bundle>

View File

@ -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,6 +50,26 @@ public class CreateDstu3Test {
private static int ourPort;
private static Server ourServer;
/**
* #342
*/
@Test
public void testCreateWithInvalidContent() throws Exception {
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
httpPost.setEntity(new StringEntity("FOO", ContentType.parse("application/xml+fhir; charset=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(400, status.getStatusLine().getStatusCode());
String expected = "<OperationOutcome xmlns=\"http://hl7.org/fhir\"><issue><severity value=\"error\"/><code value=\"processing\"/><diagnostics value=\"Failed to parse request body as XML resource. Error was: com.ctc.wstx.exc.WstxUnexpectedCharException: Unexpected character 'F' (code 70) in prolog; expected '&lt;'&#xa; at [row,col {unknown-source}]: [1,1]\"/></issue></OperationOutcome>";
assertEquals(expected, responseContent);
}
@Test
public void testRead() throws Exception {
@ -76,26 +97,6 @@ public class CreateDstu3Test {
//@formatter:on
}
/**
* #342
*/
@Test
public void testCreateWithInvalidContent() throws Exception {
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
httpPost.setEntity(new StringEntity("FOO", ContentType.parse("application/xml+fhir; charset=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(400, status.getStatusLine().getStatusCode());
String expected = "<OperationOutcome xmlns=\"http://hl7.org/fhir\"><issue><severity value=\"error\"/><code value=\"processing\"/><diagnostics value=\"Failed to parse request body as XML resource. Error was: com.ctc.wstx.exc.WstxUnexpectedCharException: Unexpected character 'F' (code 70) in prolog; expected '&lt;'&#xa; at [row,col {unknown-source}]: [1,1]\"/></issue></OperationOutcome>";
assertEquals(expected, responseContent);
}
@Test
public void testSearch() throws Exception {
@ -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<Patient> 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<IBaseResource> search() {
ArrayList<IBaseResource> retVal = new ArrayList<IBaseResource>();

View File

@ -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);
}

View File

@ -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!
</action>
<action type="fix">
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.
</action>
</release>
<release version="1.5" date="2016-04-20">
<action type="fix" issue="339">