mirror of
https://github.com/hapifhir/hapi-fhir.git
synced 2025-02-18 10:55:22 +00:00
Merge branch 'master' of github.com:jamesagnew/hapi-fhir
This commit is contained in:
commit
e762ac9d10
@ -36,6 +36,7 @@ public class ResourceGoneException extends BaseServerResponseException {
|
|||||||
|
|
||||||
public static final int STATUS_CODE = Constants.STATUS_HTTP_410_GONE;
|
public static final int STATUS_CODE = Constants.STATUS_HTTP_410_GONE;
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
private IIdType myResourceId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor which creates an error message based on a given resource ID
|
* Constructor which creates an error message based on a given resource ID
|
||||||
@ -44,6 +45,7 @@ public class ResourceGoneException extends BaseServerResponseException {
|
|||||||
*/
|
*/
|
||||||
public ResourceGoneException(IIdType theResourceId) {
|
public ResourceGoneException(IIdType theResourceId) {
|
||||||
super(STATUS_CODE, "Resource " + (theResourceId != null ? theResourceId.getValue() : "") + " is gone/deleted");
|
super(STATUS_CODE, "Resource " + (theResourceId != null ? theResourceId.getValue() : "") + " is gone/deleted");
|
||||||
|
myResourceId = theResourceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -53,6 +55,7 @@ public class ResourceGoneException extends BaseServerResponseException {
|
|||||||
@Deprecated
|
@Deprecated
|
||||||
public ResourceGoneException(Class<? extends IBaseResource> theClass, BaseIdentifierDt thePatientId) {
|
public ResourceGoneException(Class<? extends IBaseResource> theClass, BaseIdentifierDt thePatientId) {
|
||||||
super(STATUS_CODE, "Resource of type " + theClass.getSimpleName() + " with ID " + thePatientId + " is gone/deleted");
|
super(STATUS_CODE, "Resource of type " + theClass.getSimpleName() + " with ID " + thePatientId + " is gone/deleted");
|
||||||
|
myResourceId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -63,6 +66,7 @@ public class ResourceGoneException extends BaseServerResponseException {
|
|||||||
*/
|
*/
|
||||||
public ResourceGoneException(Class<? extends IBaseResource> theClass, IIdType theResourceId) {
|
public ResourceGoneException(Class<? extends IBaseResource> theClass, IIdType theResourceId) {
|
||||||
super(STATUS_CODE, "Resource of type " + theClass.getSimpleName() + " with ID " + theResourceId + " is gone/deleted");
|
super(STATUS_CODE, "Resource of type " + theClass.getSimpleName() + " with ID " + theResourceId + " is gone/deleted");
|
||||||
|
myResourceId = theResourceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -84,4 +88,12 @@ public class ResourceGoneException extends BaseServerResponseException {
|
|||||||
super(STATUS_CODE, theMessage);
|
super(STATUS_CODE, theMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IIdType getResourceId() {
|
||||||
|
return myResourceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResourceId(IIdType theResourceId) {
|
||||||
|
myResourceId = theResourceId;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,7 @@ import ca.uhn.fhir.util.*;
|
|||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.hl7.fhir.instance.model.api.*;
|
import org.hl7.fhir.instance.model.api.*;
|
||||||
import org.hl7.fhir.r4.model.InstantType;
|
import org.hl7.fhir.r4.model.InstantType;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@ -863,7 +864,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||||||
throw new ResourceNotFoundException("No resource found with PID " + thePid);
|
throw new ResourceNotFoundException("No resource found with PID " + thePid);
|
||||||
}
|
}
|
||||||
if (entity.get().getDeleted() != null) {
|
if (entity.get().getDeleted() != null) {
|
||||||
throw new ResourceGoneException("Resource was deleted at " + new InstantType(entity.get().getDeleted()).getValueAsString());
|
throw newResourceGoneException(entity.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
T retVal = toResource(myResourceType, entity.get(), null, false);
|
T retVal = toResource(myResourceType, entity.get(), null, false);
|
||||||
@ -872,6 +873,16 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private ResourceGoneException newResourceGoneException(BaseHasResource theResourceEntity) {
|
||||||
|
StringBuilder b = new StringBuilder();
|
||||||
|
b.append("Resource was deleted at ");
|
||||||
|
b.append(new InstantType(theResourceEntity.getDeleted()).getValueAsString());
|
||||||
|
ResourceGoneException retVal = new ResourceGoneException(b.toString());
|
||||||
|
retVal.setResourceId(theResourceEntity.getIdDt());
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public T read(IIdType theId) {
|
public T read(IIdType theId) {
|
||||||
@ -902,7 +913,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||||||
|
|
||||||
if (theDeletedOk == false) {
|
if (theDeletedOk == false) {
|
||||||
if (entity.getDeleted() != null) {
|
if (entity.getDeleted() != null) {
|
||||||
throw new ResourceGoneException("Resource was deleted at " + new InstantType(entity.getDeleted()).getValueAsString());
|
throw newResourceGoneException(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1204,7 +1215,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||||||
// Note that this will only fire if someone actually goes to use the
|
// Note that this will only fire if someone actually goes to use the
|
||||||
// resource in a response (it's their responsibility to call
|
// resource in a response (it's their responsibility to call
|
||||||
// outcome.fireResourceViewCallback())
|
// outcome.fireResourceViewCallback())
|
||||||
outcome.registerResourceViewCallback(()->{
|
outcome.registerResourceViewCallback(() -> {
|
||||||
if (outcome.getResource() != null) {
|
if (outcome.getResource() != null) {
|
||||||
SimplePreResourceShowDetails showDetails = new SimplePreResourceShowDetails(outcome.getResource());
|
SimplePreResourceShowDetails showDetails = new SimplePreResourceShowDetails(outcome.getResource());
|
||||||
HookParams params = new HookParams()
|
HookParams params = new HookParams()
|
||||||
|
@ -15,7 +15,7 @@ import ca.uhn.fhir.jpa.term.VersionIndependentConcept;
|
|||||||
import ca.uhn.fhir.jpa.util.CircularQueueCaptureQueriesListener;
|
import ca.uhn.fhir.jpa.util.CircularQueueCaptureQueriesListener;
|
||||||
import ca.uhn.fhir.jpa.util.ExpungeOptions;
|
import ca.uhn.fhir.jpa.util.ExpungeOptions;
|
||||||
import ca.uhn.fhir.jpa.util.JpaConstants;
|
import ca.uhn.fhir.jpa.util.JpaConstants;
|
||||||
import ca.uhn.fhir.jpa.util.LoggingRule;
|
import ca.uhn.fhir.test.utilities.LoggingRule;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
|
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
|
||||||
import ca.uhn.fhir.rest.api.Constants;
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
@ -25,7 +25,6 @@ import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
|||||||
import ca.uhn.fhir.util.BundleUtil;
|
import ca.uhn.fhir.util.BundleUtil;
|
||||||
import ca.uhn.fhir.util.StopWatch;
|
import ca.uhn.fhir.util.StopWatch;
|
||||||
import ca.uhn.fhir.util.TestUtil;
|
import ca.uhn.fhir.util.TestUtil;
|
||||||
import com.google.common.base.Charsets;
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.hibernate.HibernateException;
|
import org.hibernate.HibernateException;
|
||||||
import org.hibernate.Session;
|
import org.hibernate.Session;
|
||||||
@ -56,7 +55,6 @@ import java.util.*;
|
|||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.function.Supplier;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static ca.uhn.fhir.util.TestUtil.randomizeLocale;
|
import static ca.uhn.fhir.util.TestUtil.randomizeLocale;
|
||||||
|
@ -375,6 +375,34 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
|||||||
} catch (ResourceGoneException e) {
|
} catch (ResourceGoneException e) {
|
||||||
// good
|
// good
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testResourceGoneIncludesVersion() {
|
||||||
|
|
||||||
|
Patient p = new Patient();
|
||||||
|
p.addName().setFamily("FAM").addGiven("GIV");
|
||||||
|
IIdType id = myPatientDao.create(p).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
|
ourClient
|
||||||
|
.delete()
|
||||||
|
.resourceById(id)
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
CapturingInterceptor captureInterceptor = new CapturingInterceptor();
|
||||||
|
ourClient.registerInterceptor(captureInterceptor);
|
||||||
|
|
||||||
|
try {
|
||||||
|
ourClient.read().resource("Patient").withId(id.toUnqualifiedVersionless()).execute();
|
||||||
|
fail();
|
||||||
|
} catch (ResourceGoneException e) {
|
||||||
|
// good
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> locationHeader = captureInterceptor.getLastResponse().getHeaders(Constants.HEADER_LOCATION);
|
||||||
|
assertEquals(1, locationHeader.size());
|
||||||
|
assertThat(locationHeader.get(0), containsString(id.getValue() + "/_history/2"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -171,7 +171,7 @@ public class PointcutLatch implements IAnonymousInterceptor, IPointcutLatch {
|
|||||||
super(getName() + ": " + message + " called with values: " + hookParamsToString(theArgs));
|
super(getName() + ": " + message + " called with values: " + hookParamsToString(theArgs));
|
||||||
}
|
}
|
||||||
|
|
||||||
PointcutLatchException(String message) {
|
public PointcutLatchException(String message) {
|
||||||
super(getName() + ": " + message);
|
super(getName() + ": " + message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1046,6 +1046,20 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||||||
exception = DEFAULT_EXCEPTION_HANDLER.preProcessOutgoingException(requestDetails, e, theRequest);
|
exception = DEFAULT_EXCEPTION_HANDLER.preProcessOutgoingException(requestDetails, e, theRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If it's a 410 Gone, we want to include a location header inthe response
|
||||||
|
* if we can, since that can include the resource version which is nice
|
||||||
|
* for the user.
|
||||||
|
*/
|
||||||
|
if (exception instanceof ResourceGoneException) {
|
||||||
|
IIdType resourceId = ((ResourceGoneException) exception).getResourceId();
|
||||||
|
if (resourceId != null && resourceId.hasResourceType() && resourceId.hasIdPart()) {
|
||||||
|
String baseUrl = myServerAddressStrategy.determineServerBase(theRequest.getServletContext(), theRequest);
|
||||||
|
resourceId = resourceId.withServerBase(baseUrl, resourceId.getResourceType());
|
||||||
|
requestDetails.getResponse().addHeader(Constants.HEADER_LOCATION, resourceId.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Next, interceptors get a shot at handling the exception
|
* Next, interceptors get a shot at handling the exception
|
||||||
*/
|
*/
|
||||||
|
@ -42,6 +42,10 @@
|
|||||||
<artifactId>spring-context</artifactId>
|
<artifactId>spring-context</artifactId>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package ca.uhn.fhir.jpa.util;
|
package ca.uhn.fhir.test.utilities;
|
||||||
|
|
||||||
import org.junit.rules.TestRule;
|
import org.junit.rules.TestRule;
|
||||||
import org.junit.runner.Description;
|
import org.junit.runner.Description;
|
||||||
@ -40,19 +40,22 @@ public class LoggingRule implements TestRule {
|
|||||||
* @param description A description of the test implemented in <code>statement</code>.
|
* @param description A description of the test implemented in <code>statement</code>.
|
||||||
* @return The modified statement.
|
* @return The modified statement.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public Statement apply(final Statement statement, final Description description) {
|
public Statement apply(final Statement statement, final Description description) {
|
||||||
return new Statement() {
|
return new Statement() {
|
||||||
@Override
|
@Override
|
||||||
public void evaluate() throws Throwable {
|
public void evaluate() throws Throwable {
|
||||||
final Logger logger = LoggerFactory.getLogger(description.getTestClass());
|
final Logger logger = LoggerFactory.getLogger(description.getTestClass());
|
||||||
logger.info(MessageFormat.format("Starting test case [{0}]", description.getDisplayName()));
|
logger.info(MessageFormat.format("Starting test case [{0}]", description.getDisplayName()));
|
||||||
|
boolean success = false;
|
||||||
try {
|
try {
|
||||||
statement.evaluate();
|
statement.evaluate();
|
||||||
|
success = true;
|
||||||
} catch (final Throwable e) {
|
} catch (final Throwable e) {
|
||||||
logger.error(MessageFormat.format("Exception thrown in test case [{0}]: {1}", description.getDisplayName(), e.toString()), e);
|
logger.error(MessageFormat.format("Exception thrown in test case [{0}]: {1}", description.getDisplayName(), e.toString()), e);
|
||||||
throw e;
|
throw e;
|
||||||
} finally {
|
} finally {
|
||||||
logger.info(MessageFormat.format("Finished test case [{0}]", description.getDisplayName()));
|
logger.info(MessageFormat.format("Finished test case [{0}] (success={1})", description.getDisplayName(), success));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
5
pom.xml
5
pom.xml
@ -1492,6 +1492,11 @@
|
|||||||
<artifactId>maven-javadoc-plugin</artifactId>
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
<version>3.1.0</version>
|
<version>3.1.0</version>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
|
<version>3.1.1</version>
|
||||||
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-jxr-plugin</artifactId>
|
<artifactId>maven-jxr-plugin</artifactId>
|
||||||
|
@ -220,6 +220,11 @@
|
|||||||
request and add it to the response headers. Clients may supply the transaction
|
request and add it to the response headers. Clients may supply the transaction
|
||||||
header via the <![CDATA[<code>X-Request-ID</code>]]> header.
|
header via the <![CDATA[<code>X-Request-ID</code>]]> header.
|
||||||
</action>
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
When attempting to read a resource that is deleted, a Location header is now
|
||||||
|
returned that includes the resource ID and the version ID for the deleted
|
||||||
|
resource.
|
||||||
|
</action>
|
||||||
</release>
|
</release>
|
||||||
<release version="3.8.0" date="2019-05-30" description="Hippo">
|
<release version="3.8.0" date="2019-05-30" description="Hippo">
|
||||||
<action type="fix">
|
<action type="fix">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user