Accept conditional updates with _id as parameter
This commit is contained in:
parent
309b67c010
commit
7942d69d5d
|
@ -83,16 +83,6 @@ class ConditionalParamBinder implements IParameter {
|
||||||
if (theRequest.getId() != null && theRequest.getId().hasIdPart()) {
|
if (theRequest.getId() != null && theRequest.getId().hasIdPart()) {
|
||||||
return null;
|
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('?');
|
int questionMarkIndex = theRequest.getCompleteUrl().indexOf('?');
|
||||||
return theRequest.getResourceName() + theRequest.getCompleteUrl().substring(questionMarkIndex);
|
return theRequest.getResourceName() + theRequest.getCompleteUrl().substring(questionMarkIndex);
|
||||||
|
|
|
@ -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
|
@Test
|
||||||
public void testTransactionCreateInlineMatchUrlWithNoMatches() {
|
public void testTransactionCreateInlineMatchUrlWithNoMatches() {
|
||||||
String methodName = "testTransactionCreateInlineMatchUrlWithNoMatches";
|
String methodName = "testTransactionCreateInlineMatchUrlWithNoMatches";
|
||||||
|
|
|
@ -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>
|
|
@ -31,6 +31,7 @@ import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
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.Create;
|
||||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||||
import ca.uhn.fhir.rest.annotation.Read;
|
import ca.uhn.fhir.rest.annotation.Read;
|
||||||
|
@ -49,6 +50,26 @@ public class CreateDstu3Test {
|
||||||
private static int ourPort;
|
private static int ourPort;
|
||||||
private static Server ourServer;
|
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 '<'
 at [row,col {unknown-source}]: [1,1]\"/></issue></OperationOutcome>";
|
||||||
|
assertEquals(expected, responseContent);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRead() throws Exception {
|
public void testRead() throws Exception {
|
||||||
|
|
||||||
|
@ -76,26 +97,6 @@ public class CreateDstu3Test {
|
||||||
//@formatter:on
|
//@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 '<'
 at [row,col {unknown-source}]: [1,1]\"/></issue></OperationOutcome>";
|
|
||||||
assertEquals(expected, responseContent);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSearch() throws Exception {
|
public void testSearch() throws Exception {
|
||||||
|
|
||||||
|
@ -156,6 +157,11 @@ public class CreateDstu3Test {
|
||||||
|
|
||||||
public static class PatientProvider implements IResourceProvider {
|
public static class PatientProvider implements IResourceProvider {
|
||||||
|
|
||||||
|
@Create()
|
||||||
|
public MethodOutcome create(@ResourceParam Patient theIdParam) {
|
||||||
|
return new MethodOutcome(new IdType("Patient", "1"), true);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<Patient> getResourceType() {
|
public Class<Patient> getResourceType() {
|
||||||
return Patient.class;
|
return Patient.class;
|
||||||
|
@ -169,11 +175,6 @@ public class CreateDstu3Test {
|
||||||
return p0;
|
return p0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Create()
|
|
||||||
public MethodOutcome read(@ResourceParam Patient theIdParam) {
|
|
||||||
return new MethodOutcome(new IdType("Patient", "1"), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Search
|
@Search
|
||||||
public List<IBaseResource> search() {
|
public List<IBaseResource> search() {
|
||||||
ArrayList<IBaseResource> retVal = new ArrayList<IBaseResource>();
|
ArrayList<IBaseResource> retVal = new ArrayList<IBaseResource>();
|
||||||
|
|
|
@ -6,6 +6,7 @@ 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.methods.CloseableHttpResponse;
|
||||||
import org.apache.http.client.methods.HttpPut;
|
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;
|
||||||
|
@ -19,11 +20,13 @@ import org.hl7.fhir.dstu3.model.IdType;
|
||||||
import org.hl7.fhir.dstu3.model.OperationOutcome;
|
import org.hl7.fhir.dstu3.model.OperationOutcome;
|
||||||
import org.hl7.fhir.dstu3.model.Patient;
|
import org.hl7.fhir.dstu3.model.Patient;
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.Before;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
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.IdParam;
|
||||||
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||||
import ca.uhn.fhir.rest.annotation.Update;
|
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
|
@Test
|
||||||
public void testUpdateWrongUrlInBody() throws Exception {
|
public void testUpdateWrongUrlInBody() throws Exception {
|
||||||
|
|
||||||
|
@ -81,7 +131,9 @@ public class UpdateDstu3Test {
|
||||||
ourLog.info("Response was:\n{}", responseContent);
|
ourLog.info("Response was:\n{}", responseContent);
|
||||||
|
|
||||||
OperationOutcome oo = ourCtx.newXmlParser().parseResource(OperationOutcome.class, 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());
|
assertEquals(400, status.getStatusLine().getStatusCode());
|
||||||
assertNull(status.getFirstHeader("location"));
|
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 {
|
public static class PatientProvider implements IResourceProvider {
|
||||||
|
|
||||||
|
@ -125,11 +185,13 @@ public class UpdateDstu3Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Update()
|
@Update()
|
||||||
public MethodOutcome updatePatient(@IdParam IdType theId, @ResourceParam Patient thePatient) {
|
public MethodOutcome updatePatient(@IdParam IdType theId, @ResourceParam Patient thePatient, @ConditionalUrlParam String theConditionalUrl) {
|
||||||
IdType id = theId.withVersion(thePatient.getIdentifierFirstRep().getValue());
|
ourId = theId;
|
||||||
|
ourConditionalUrl = theConditionalUrl;
|
||||||
|
IdType id = theId != null ? theId.withVersion(thePatient.getIdentifierFirstRep().getValue()) : new IdType("Patient/1");
|
||||||
OperationOutcome oo = new OperationOutcome();
|
OperationOutcome oo = new OperationOutcome();
|
||||||
oo.addIssue().setDiagnostics("OODETAILS");
|
oo.addIssue().setDiagnostics("OODETAILS");
|
||||||
if (theId.getValueAsString().contains("CREATE")) {
|
if (id.getValueAsString().contains("CREATE")) {
|
||||||
return new MethodOutcome(id, oo, true);
|
return new MethodOutcome(id, oo, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -181,6 +181,11 @@
|
||||||
count would not appear in the self/prev/next links and would not actually be applied
|
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!
|
to the search results by the server. Thanks to Jim Steele for letting us know!
|
||||||
</action>
|
</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>
|
||||||
<release version="1.5" date="2016-04-20">
|
<release version="1.5" date="2016-04-20">
|
||||||
<action type="fix" issue="339">
|
<action type="fix" issue="339">
|
||||||
|
|
Loading…
Reference in New Issue