Treat missing ID in resource body as warning

This commit is contained in:
James Agnew 2016-05-13 18:04:07 -04:00
parent abd8bc2f14
commit 84a7856c19
4 changed files with 156 additions and 17 deletions

View File

@ -91,12 +91,6 @@ public class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithRe
super.addParametersForServerRequest(theRequest, theParams);
}
@Override
public boolean incomingServerRequestMatchesMethod(RequestDetails theRequest) {
// TODO Auto-generated method stub
return super.incomingServerRequestMatchesMethod(theRequest);
}
@Override
protected BaseHttpClientInvocation createClientInvocation(Object[] theArgs, IResource theResource) {
IdDt idDt = (IdDt) theArgs[myIdParameterIndex];
@ -142,17 +136,18 @@ public class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithRe
protected void validateResourceIdAndUrlIdForNonConditionalOperation(String theResourceId, String theUrlId, String theMatchUrl) {
if (isBlank(theMatchUrl)) {
if (isBlank(theResourceId)) {
String msg = getContext().getLocalizer().getMessage(BaseOutcomeReturningMethodBindingWithResourceParam.class, "noIdInBodyForUpdate");
throw new InvalidRequestException(msg);
// String msg = getContext().getLocalizer().getMessage(BaseOutcomeReturningMethodBindingWithResourceParam.class, "noIdInBodyForUpdate");
ourLog.warn("No resource ID found in resource body for update");
} else {
if (!theResourceId.equals(theUrlId)) {
String msg = getContext().getLocalizer().getMessage(BaseOutcomeReturningMethodBindingWithResourceParam.class, "incorrectIdForUpdate", theResourceId, theUrlId);
throw new InvalidRequestException(msg);
}
}
if (isBlank(theUrlId)) {
String msg = getContext().getLocalizer().getMessage(BaseOutcomeReturningMethodBindingWithResourceParam.class, "noIdInUrlForUpdate");
throw new InvalidRequestException(msg);
}
if (!theResourceId.equals(theUrlId)) {
String msg = getContext().getLocalizer().getMessage(BaseOutcomeReturningMethodBindingWithResourceParam.class, "incorrectIdForUpdate", theResourceId, theUrlId);
throw new InvalidRequestException(msg);
}
}
}

View File

@ -39,13 +39,10 @@ import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
/**
* Created by dsotnikov on 2/25/2014.
*/
public class UpdateTest {
public class UpdateDstu1Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forDstu1();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(UpdateTest.class);
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(UpdateDstu1Test.class);
private static int ourPort;
private static DiagnosticReportProvider ourReportProvider;
private static Server ourServer;

View File

@ -0,0 +1,140 @@
package ca.uhn.fhir.rest.server;
import static org.junit.Assert.*;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
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.BeforeClass;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Update;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
public class UpdateDstu3Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forDstu3();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(UpdateDstu3Test.class);
private static int ourPort;
private static Server ourServer;
@Test
public void testUpdateMissingUrlInBody() 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")));
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
OperationOutcome oo = ourCtx.newXmlParser().parseResource(OperationOutcome.class, responseContent);
assertEquals("OODETAILS", oo.getIssueFirstRep().getDiagnostics());
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue());
assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue());
}
@Test
public void testUpdateWrongUrlInBody() throws Exception {
Patient patient = new Patient();
patient.setId("3");
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")));
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
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(400, status.getStatusLine().getStatusCode());
assertNull(status.getFirstHeader("location"));
assertNull(status.getFirstHeader("content-location"));
}
@AfterClass
public static void afterClassClearContext() throws Exception {
ourServer.stop();
TestUtil.clearAllStaticFieldsForUnitTest();
}
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer(ourCtx);
servlet.setResourceProviders(new PatientProvider());
ServletHolder servletHolder = new ServletHolder(servlet);
proxyHandler.addServletWithMapping(servletHolder, "/*");
ourServer.setHandler(proxyHandler);
ourServer.start();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourClient = builder.build();
}
public static class PatientProvider implements IResourceProvider {
@Override
public Class<Patient> getResourceType() {
return Patient.class;
}
@Update()
public MethodOutcome updatePatient(@IdParam IdType theId, @ResourceParam Patient thePatient) {
IdType id = theId.withVersion(thePatient.getIdentifierFirstRep().getValue());
OperationOutcome oo = new OperationOutcome();
oo.addIssue().setDiagnostics("OODETAILS");
if (theId.getValueAsString().contains("CREATE")) {
return new MethodOutcome(id, oo, true);
}
return new MethodOutcome(id, oo);
}
}
}

View File

@ -163,6 +163,13 @@
This option disables some features (e.g. fulltext search) in order to allow the
server to start in memory-constrained environments (e.g Raspberry Pi)
</action>
<action type="add">
When updating a resource via an update operation on the server, if the ID of the
resource is not present in the resource body but is present on the URL, this will
now be treated as a warning instead of as a failure in order to be a bit more
tolerant of errors. If the ID is present in the body but does not agree with the
ID in the URL this remains an error.
</action>
</release>
<release version="1.5" date="2016-04-20">
<action type="fix" issue="339">