Patch modifications
This commit is contained in:
parent
40286f49c2
commit
d8c99363db
|
@ -10,6 +10,8 @@ import java.util.Set;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.hl7.fhir.dstu3.model.IdType;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.BundleEntry;
|
||||
|
@ -108,6 +110,23 @@ public List<Organization> getAllOrganizations() {
|
|||
}
|
||||
//END SNIPPET: searchAll
|
||||
|
||||
//START SNIPPET: updateEtag
|
||||
@Update
|
||||
public MethodOutcome update(@IdParam IdType theId, @ResourceParam Patient thePatient) {
|
||||
String resourceId = theId.getIdPart();
|
||||
String versionId = theId.getVersionIdPart(); // this will contain the ETag
|
||||
|
||||
String currentVersion = "1"; // populate this with the current version
|
||||
|
||||
if (!versionId.equals(currentVersion)) {
|
||||
throw new ResourceVersionConflictException("Expected version " + currentVersion);
|
||||
}
|
||||
|
||||
// ... perform the update ...
|
||||
return new MethodOutcome();
|
||||
|
||||
}
|
||||
//END SNIPPET: updateEtag
|
||||
|
||||
//START SNIPPET: summaryAndElements
|
||||
@Search
|
||||
|
|
|
@ -180,7 +180,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
}
|
||||
final ResourceTable entity = readEntityLatestVersion(theId);
|
||||
if (theId.hasVersionIdPart() && Long.parseLong(theId.getVersionIdPart()) != entity.getVersion()) {
|
||||
throw new InvalidRequestException("Trying to delete " + theId + " but this is not the current version");
|
||||
throw new ResourceVersionConflictException("Trying to delete " + theId + " but this is not the current version");
|
||||
}
|
||||
|
||||
validateOkToDelete(deleteConflicts, entity);
|
||||
|
@ -1038,7 +1038,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
}
|
||||
|
||||
if (resourceId.hasVersionIdPart() && Long.parseLong(resourceId.getVersionIdPart()) != entity.getVersion()) {
|
||||
throw new InvalidRequestException("Trying to update " + resourceId + " but this is not the current version");
|
||||
throw new ResourceVersionConflictException("Trying to update " + resourceId + " but this is not the current version");
|
||||
}
|
||||
|
||||
if (resourceId.hasResourceType() && !resourceId.getResourceType().equals(getResourceName())) {
|
||||
|
@ -1080,7 +1080,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
ResourceTable entityToUpdate = readEntityLatestVersion(theId);
|
||||
if (theId.hasVersionIdPart()) {
|
||||
if (theId.getVersionIdPartAsLong() != entityToUpdate.getVersion()) {
|
||||
throw new PreconditionFailedException("Version " + theId.getVersionIdPart() + " is not the most recent version of this resource, unable to apply patch");
|
||||
throw new ResourceVersionConflictException("Version " + theId.getVersionIdPart() + " is not the most recent version of this resource, unable to apply patch");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,13 +3,13 @@ package ca.uhn.fhir.jpa.util.xmlpatch;
|
|||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import com.github.dnault.xmlpatch.Patcher;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
|
||||
public class XmlPatchUtils {
|
||||
|
@ -23,12 +23,12 @@ public class XmlPatchUtils {
|
|||
|
||||
ByteArrayOutputStream result = new ByteArrayOutputStream();
|
||||
try {
|
||||
Patcher.patch(new ByteArrayInputStream(inputResource.getBytes(StandardCharsets.UTF_8)), new ByteArrayInputStream(thePatchBody.getBytes(StandardCharsets.UTF_8)), result);
|
||||
Patcher.patch(new ByteArrayInputStream(inputResource.getBytes(Constants.CHARSET_UTF8)), new ByteArrayInputStream(thePatchBody.getBytes(Constants.CHARSET_UTF8)), result);
|
||||
} catch (IOException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
|
||||
String resultString = new String(result.toByteArray(), StandardCharsets.UTF_8);
|
||||
String resultString = new String(result.toByteArray(), Constants.CHARSET_UTF8);
|
||||
T retVal = theCtx.newXmlParser().parseResource(clazz, resultString);
|
||||
|
||||
return retVal;
|
||||
|
|
|
@ -880,7 +880,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
|
|||
try {
|
||||
myPatientDao.delete(id2, mySrd);
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
} catch (ResourceVersionConflictException e) {
|
||||
// good
|
||||
}
|
||||
|
||||
|
|
|
@ -1046,7 +1046,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
|
|||
try {
|
||||
myPatientDao.delete(id2, mySrd);
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
} catch (ResourceVersionConflictException e) {
|
||||
// good
|
||||
}
|
||||
|
||||
|
|
|
@ -85,9 +85,6 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearchPagingKeepsOldSearches() throws Exception {
|
||||
String methodName = "testSearchPagingKeepsOldSearches";
|
||||
|
@ -108,13 +105,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
|
||||
List<String> linkNext = Lists.newArrayList();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
Bundle bundle = ourClient
|
||||
.search()
|
||||
.forResource(Patient.class)
|
||||
.where(Patient.NAME.matches().value("testSearchPagingKeepsOldSearches"))
|
||||
.count(5)
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
Bundle bundle = ourClient.search().forResource(Patient.class).where(Patient.NAME.matches().value("testSearchPagingKeepsOldSearches")).count(5).returnBundle(Bundle.class).execute();
|
||||
assertTrue(isNotBlank(bundle.getLink("next").getUrl()));
|
||||
assertEquals(5, bundle.getEntry().size());
|
||||
linkNext.add(bundle.getLink("next").getUrl());
|
||||
|
@ -207,7 +198,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
|
||||
CloseableHttpResponse response = ourHttpClient.execute(patch);
|
||||
try {
|
||||
assertEquals(412, response.getStatusLine().getStatusCode());
|
||||
assertEquals(409, response.getStatusLine().getStatusCode());
|
||||
String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
assertThat(responseString, containsString("<OperationOutcome"));
|
||||
assertThat(responseString, containsString("<diagnostics value=\"Version 9 is not the most recent version of this resource, unable to apply patch\"/>"));
|
||||
|
@ -973,7 +964,8 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
|
||||
String encoded = myFhirCtx.newXmlParser().encodeResourceToString(response);
|
||||
ourLog.info(encoded);
|
||||
assertThat(encoded, containsString("<issue><severity value=\"information\"/><code value=\"informational\"/><diagnostics value=\"Successfully deleted Patient?identifier=testDeleteConditionalMultiple resource(s) in 2ms\"/></issue>"));
|
||||
assertThat(encoded, containsString(
|
||||
"<issue><severity value=\"information\"/><code value=\"informational\"/><diagnostics value=\"Successfully deleted Patient?identifier=testDeleteConditionalMultiple resource(s) in 2ms\"/></issue>"));
|
||||
try {
|
||||
ourClient.read().resource("Patient").withId(id1).execute();
|
||||
fail();
|
||||
|
@ -999,7 +991,8 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
String response = IOUtils.toString(resp.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(response);
|
||||
assertEquals(200, resp.getStatusLine().getStatusCode());
|
||||
assertThat(response, containsString("<issue><severity value=\"warning\"/><code value=\"not-found\"/><diagnostics value=\"Unable to find resource matching URL "Patient?identifier=testDeleteConditionalNoMatches". Deletion failed.\"/></issue>"));
|
||||
assertThat(response, containsString(
|
||||
"<issue><severity value=\"warning\"/><code value=\"not-found\"/><diagnostics value=\"Unable to find resource matching URL "Patient?identifier=testDeleteConditionalNoMatches". Deletion failed.\"/></issue>"));
|
||||
} finally {
|
||||
IOUtils.closeQuietly(resp);
|
||||
}
|
||||
|
@ -2648,6 +2641,47 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateWithETag() throws IOException, Exception {
|
||||
String methodName = "testUpdateWithETag";
|
||||
|
||||
Patient pt = new Patient();
|
||||
pt.addName().addFamily(methodName);
|
||||
IIdType id = ourClient.create().resource(pt).execute().getId().toUnqualifiedVersionless();
|
||||
|
||||
pt.addName().addFamily("FAM2");
|
||||
String resource = myFhirCtx.newXmlParser().encodeResourceToString(pt);
|
||||
|
||||
HttpPut put = new HttpPut(ourServerBase + "/Patient/" + id.getIdPart());
|
||||
put.addHeader(Constants.HEADER_IF_MATCH, "W/\"44\"");
|
||||
put.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||
CloseableHttpResponse response = ourHttpClient.execute(put);
|
||||
try {
|
||||
String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(responseString);
|
||||
assertEquals(409, response.getStatusLine().getStatusCode());
|
||||
OperationOutcome oo = myFhirCtx.newXmlParser().parseResource(OperationOutcome.class, responseString);
|
||||
assertThat(oo.getIssue().get(0).getDiagnostics(), containsString("Trying to update Patient/" + id.getIdPart() + "/_history/44 but this is not the current version"));
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
|
||||
// Now a good one
|
||||
put = new HttpPut(ourServerBase + "/Patient/" + id.getIdPart());
|
||||
put.addHeader(Constants.HEADER_IF_MATCH, "W/\"1\"");
|
||||
put.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||
response = ourHttpClient.execute(put);
|
||||
try {
|
||||
String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(responseString);
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testUpdateInvalidReference2() throws IOException, Exception {
|
||||
String methodName = "testUpdateInvalidReference2";
|
||||
|
|
|
@ -7,6 +7,18 @@
|
|||
</properties>
|
||||
<body>
|
||||
<release version="2.1" date="TBD">
|
||||
<action type="add">
|
||||
Client, Server, and JPA server now support experimental support
|
||||
for
|
||||
<![CDATA[HTTP PATCH]]>
|
||||
using the XML Patch and JSON Patch syntax as explored during the
|
||||
September 2016 Baltimore Connectathon. See
|
||||
<![CDATA[<a href="http://wiki.hl7.org/index.php?title=201609_PATCH_Connectathon_Track_Proposal">this wiki page</a>]]>
|
||||
for a description of the syntax.
|
||||
<![CDATA[<br/>]]>
|
||||
Thanks to Pater Girard for all of his help during the connectathon
|
||||
in implementing this feature!
|
||||
</action>
|
||||
<action type="fix">
|
||||
In server, when returning a list of resources, the server sometimes failed to add
|
||||
<![CDATA[<code>_include</code>]]> resources to the response bundle if they were
|
||||
|
@ -62,6 +74,13 @@
|
|||
and the new MimeTypes
|
||||
(e.g. <![CDATA[<code>application/fhir+xml</code>]]>)
|
||||
</action>
|
||||
<action type="fix">
|
||||
JPA server now sends correct
|
||||
<![CDATA[<code>HTTP 409 Version Conflict</code>]]>
|
||||
when a
|
||||
DELETE fails because of constraint issues, instead of
|
||||
<![CDATA[<code>HTTP 400 Invalid Request</code>]]>
|
||||
</action>
|
||||
</release>
|
||||
<release version="2.0" date="2016-08-30">
|
||||
<action type="fix">
|
||||
|
|
|
@ -236,6 +236,17 @@
|
|||
<pre><![CDATA[PUT [serverBase]/Patient/123
|
||||
If-Match: W/"3"]]></pre>
|
||||
|
||||
<p>
|
||||
If a client performs a contention aware update, the ETag version will be
|
||||
placed in the version part of the IdDt/IdType that is passed into the
|
||||
method. For example:
|
||||
</p>
|
||||
|
||||
<macro name="snippet">
|
||||
<param name="id" value="updateEtag" />
|
||||
<param name="file" value="examples/src/main/java/example/RestfulPatientResourceProviderMore.java" />
|
||||
</macro>
|
||||
|
||||
<a name="instance_delete" />
|
||||
</section>
|
||||
|
||||
|
|
Loading…
Reference in New Issue