From cc283b7732fd7e6fbb87d880454168bdb323d759 Mon Sep 17 00:00:00 2001 From: Nathan Doef Date: Thu, 31 Aug 2023 14:08:45 -0400 Subject: [PATCH] Respect response status code set in MethodOutcome of a Resource provider (#5267) * failing test * fix * extract response code modifying resource provider for testing * comment * remove TestResponseCodeModifyingPatientProvider --- .../ca/uhn/fhir/rest/api/MethodOutcome.java | 8 +- .../fhir/rest/client/method/MethodUtil.java | 1 + ...method-outcome-from-resource-provider.yaml | 5 ++ .../BaseOutcomeReturningMethodBinding.java | 6 ++ ...onseCodeModifyingResourceProviderTest.java | 77 +++++++++++++++++++ 5 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5268-respect-response-status-code-of-method-outcome-from-resource-provider.yaml create mode 100644 hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ResponseCodeModifyingResourceProviderTest.java diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/MethodOutcome.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/MethodOutcome.java index c9bfa5aa2c9..909a9ad8dc8 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/MethodOutcome.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/MethodOutcome.java @@ -40,7 +40,7 @@ public class MethodOutcome { private IBaseResource myResource; private Map> myResponseHeaders; private Collection myResourceViewCallbacks; - private int myResponseStatusCode; + private Integer myResponseStatusCode; /** * Constructor @@ -258,6 +258,10 @@ public class MethodOutcome { } public int getResponseStatusCode() { - return myResponseStatusCode; + return isResponseStatusCodeSet() ? myResponseStatusCode : 0; + } + + public boolean isResponseStatusCodeSet() { + return myResponseStatusCode != null; } } diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/MethodUtil.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/MethodUtil.java index 5021374f9b6..f5b7b991e22 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/MethodUtil.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/MethodUtil.java @@ -544,6 +544,7 @@ public class MethodUtil { } MethodOutcome retVal = new MethodOutcome(); + retVal.setResponseStatusCode(theResponseStatusCode); if (locationHeaders.size() > 0) { String locationHeader = locationHeaders.get(0); BaseOutcomeReturningMethodBinding.parseContentLocation(theContext, retVal, locationHeader); diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5268-respect-response-status-code-of-method-outcome-from-resource-provider.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5268-respect-response-status-code-of-method-outcome-from-resource-provider.yaml new file mode 100644 index 00000000000..25e68ebabdb --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5268-respect-response-status-code-of-method-outcome-from-resource-provider.yaml @@ -0,0 +1,5 @@ +--- +type: fix +issue: 5268 +title: "Previously, the response status code set in a `MethodOutcome` of a Resource provider was not respected. +This has been fixed." diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseOutcomeReturningMethodBinding.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseOutcomeReturningMethodBinding.java index 93280afd985..e9c7e56458c 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseOutcomeReturningMethodBinding.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseOutcomeReturningMethodBinding.java @@ -80,6 +80,12 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding { protected abstract String getMatchingOperation(); private int getOperationStatus(MethodOutcome response) { + + // if the response status code is set (i.e. from a custom Resource provider) it should be respected + if (response.isResponseStatusCodeSet()) { + return response.getResponseStatusCode(); + } + switch (getRestOperationType()) { case CREATE: validateResponseNotNullIfItShouldntBe(response); diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ResponseCodeModifyingResourceProviderTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ResponseCodeModifyingResourceProviderTest.java new file mode 100644 index 00000000000..c4526ff9ee6 --- /dev/null +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ResponseCodeModifyingResourceProviderTest.java @@ -0,0 +1,77 @@ +package ca.uhn.fhir.rest.server; + +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.rest.annotation.Create; +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.rest.client.api.IGenericClient; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r4.model.IdType; +import org.hl7.fhir.r4.model.Patient; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import javax.servlet.ServletRequest; + +import static ca.uhn.fhir.rest.api.Constants.STATUS_HTTP_202_ACCEPTED; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class ResponseCodeModifyingResourceProviderTest { + + public static final int CUSTOM_RESPONSE_CODE = STATUS_HTTP_202_ACCEPTED; + + @RegisterExtension + private RestfulServerExtension myServer = new RestfulServerExtension(FhirVersionEnum.R4) + .registerProvider(new TestResponseCodeModifyingPatientProvider()); + + private IGenericClient myClient; + + private Patient myPatient; + + @BeforeEach + public void before() { + myPatient = new Patient(); + myPatient.getNameFirstRep().addGiven("John").setFamily("Smith"); + myClient = myServer.getFhirClient(); + } + + @Test + public void testCreatePatientReturnsModifiedResponseCode() { + MethodOutcome outcome = myClient.create().resource(myPatient).execute(); + assertEquals(CUSTOM_RESPONSE_CODE, outcome.getResponseStatusCode()); + } + + @Test + public void testUpdatePatientReturnsModifiedResponseCode() { + myPatient.setId("1"); + MethodOutcome outcome = myClient.update().resource(myPatient).execute(); + assertEquals(CUSTOM_RESPONSE_CODE, outcome.getResponseStatusCode()); + } + + class TestResponseCodeModifyingPatientProvider implements IResourceProvider { + + @Create() + public MethodOutcome createPatient(@ResourceParam Patient thePatient, ServletRequest theServletRequest) { + MethodOutcome methodOutcome = new MethodOutcome(); + methodOutcome.setResponseStatusCode(CUSTOM_RESPONSE_CODE); + return methodOutcome; + } + + @Update() + public MethodOutcome updatePatient(@IdParam IdType theId, @ResourceParam Patient thePatient) { + MethodOutcome methodOutcome = new MethodOutcome(); + methodOutcome.setResponseStatusCode(CUSTOM_RESPONSE_CODE); + return methodOutcome; + } + + @Override + public Class getResourceType() { + return Patient.class; + } + } + +}