Added new callbacks to IServerOperationInterceptor to be invoked before
other operation methods
This commit is contained in:
parent
fe37c87e78
commit
b18e71d4f5
|
@ -196,6 +196,16 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
|
||||
T resourceToDelete = toResource(myResourceType, entity, false);
|
||||
|
||||
// Notify IServerOperationInterceptors about pre-action call
|
||||
if (theRequestDetails != null) {
|
||||
theRequestDetails.getRequestOperationCallback().resourcePreDelete(resourceToDelete);
|
||||
}
|
||||
for (IServerInterceptor next : getConfig().getInterceptors()) {
|
||||
if (next instanceof IServerOperationInterceptor) {
|
||||
((IServerOperationInterceptor) next).resourcePreDelete(theRequestDetails, resourceToDelete);
|
||||
}
|
||||
}
|
||||
|
||||
validateOkToDelete(theDeleteConflicts, entity);
|
||||
|
||||
preDelete(resourceToDelete, entity);
|
||||
|
@ -267,6 +277,17 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
deletedResources.add(entity);
|
||||
|
||||
T resourceToDelete = toResource(myResourceType, entity, false);
|
||||
|
||||
// Notify IServerOperationInterceptors about pre-action call
|
||||
if (theRequestDetails != null) {
|
||||
theRequestDetails.getRequestOperationCallback().resourcePreDelete(resourceToDelete);
|
||||
}
|
||||
for (IServerInterceptor next : getConfig().getInterceptors()) {
|
||||
if (next instanceof IServerOperationInterceptor) {
|
||||
((IServerOperationInterceptor) next).resourcePreDelete(theRequestDetails, resourceToDelete);
|
||||
}
|
||||
}
|
||||
|
||||
validateOkToDelete(deleteConflicts, entity);
|
||||
|
||||
// Notify interceptors
|
||||
|
@ -378,6 +399,16 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
notifyInterceptors(RestOperationTypeEnum.CREATE, requestDetails);
|
||||
}
|
||||
|
||||
// Notify JPA interceptors
|
||||
if (theRequestDetails != null) {
|
||||
theRequestDetails.getRequestOperationCallback().resourcePreCreate(theResource);
|
||||
}
|
||||
for (IServerInterceptor next : getConfig().getInterceptors()) {
|
||||
if (next instanceof IServerOperationInterceptor) {
|
||||
((IServerOperationInterceptor) next).resourcePreCreate(theRequestDetails, theResource);
|
||||
}
|
||||
}
|
||||
|
||||
// Perform actual DB update
|
||||
updateEntity(theResource, entity, null, thePerformIndexing, thePerformIndexing, theUpdateTime, false, thePerformIndexing);
|
||||
theResource.setId(entity.getIdDt());
|
||||
|
@ -558,10 +589,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
if (theRequestDetails == null || theRequestDetails.getServer() == null) {
|
||||
return false;
|
||||
}
|
||||
if (theRequestDetails.getServer().getPagingProvider() instanceof DatabaseBackedPagingProvider) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return theRequestDetails.getServer().getPagingProvider() instanceof DatabaseBackedPagingProvider;
|
||||
}
|
||||
|
||||
protected void markResourcesMatchingExpressionAsNeedingReindexing(String theExpression) {
|
||||
|
@ -816,9 +844,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
|
||||
@Override
|
||||
public BaseHasResource readEntity(IIdType theId) {
|
||||
boolean checkForForcedId = true;
|
||||
|
||||
BaseHasResource entity = readEntity(theId, checkForForcedId);
|
||||
BaseHasResource entity = readEntity(theId, true);
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
@ -1048,7 +1074,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
if (theResource instanceof IResource) {
|
||||
ResourceMetadataKeyEnum.UPDATED.put((IResource) theResource, theEntity.getUpdated());
|
||||
} else {
|
||||
IBaseMetaType meta = ((IAnyResource) theResource).getMeta();
|
||||
IBaseMetaType meta = theResource.getMeta();
|
||||
meta.setLastUpdated(theEntity.getUpdatedDate());
|
||||
}
|
||||
}
|
||||
|
@ -1183,8 +1209,17 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
|
||||
IBaseResource oldResource = toResource(entity, false);
|
||||
|
||||
// Notify IServerOperationInterceptors about pre-action call
|
||||
if (theRequestDetails != null) {
|
||||
theRequestDetails.getRequestOperationCallback().resourcePreUpdate(oldResource, theResource);
|
||||
}
|
||||
for (IServerInterceptor next : getConfig().getInterceptors()) {
|
||||
if (next instanceof IServerOperationInterceptor) {
|
||||
((IServerOperationInterceptor) next).resourcePreUpdate(theRequestDetails, oldResource, theResource);
|
||||
}
|
||||
}
|
||||
|
||||
// Perform update
|
||||
StopWatch sw = new StopWatch();
|
||||
ResourceTable savedEntity = updateEntity(theResource, entity, null, thePerformIndexing, thePerformIndexing, new Date(), theForceUpdateVersion, thePerformIndexing);
|
||||
|
||||
/*
|
||||
|
|
|
@ -18,10 +18,14 @@ import org.junit.AfterClass;
|
|||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Matchers.any;
|
||||
|
@ -32,6 +36,7 @@ public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test {
|
|||
private IServerOperationInterceptor myJpaInterceptor;
|
||||
private ServerOperationInterceptorAdapter myJpaInterceptorAdapter = new ServerOperationInterceptorAdapter();
|
||||
private IServerOperationInterceptor myServerOperationInterceptor;
|
||||
private List<IIdType> myIds = new ArrayList<>();
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
|
@ -43,17 +48,18 @@ public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test {
|
|||
@Before
|
||||
public void before() {
|
||||
myJpaInterceptor = mock(IServerOperationInterceptor.class);
|
||||
|
||||
myIds.clear();
|
||||
|
||||
myServerOperationInterceptor = mock(IServerOperationInterceptor.class, new Answer<Object>() {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
public Object answer(InvocationOnMock theInvocation) {
|
||||
if (theInvocation.getMethod().getReturnType().equals(boolean.class)) {
|
||||
return true;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
myDaoConfig.getInterceptors().add(myJpaInterceptor);
|
||||
myDaoConfig.getInterceptors().add(myJpaInterceptorAdapter);
|
||||
myDaoConfig.getInterceptors().add(myServerOperationInterceptor);
|
||||
|
@ -180,71 +186,24 @@ public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test {
|
|||
|
||||
doAnswer(new Answer<Void>() {
|
||||
@Override
|
||||
public Void answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
public Void answer(InvocationOnMock theInvocation) {
|
||||
IBaseResource res = (IBaseResource) theInvocation.getArguments()[0];
|
||||
Long id = res.getIdElement().getIdPartAsLong();
|
||||
assertEquals("Patient/" + id + "/_history/1", res.getIdElement().getValue());
|
||||
return null;
|
||||
}}).when(myRequestOperationCallback).resourceCreated(any(IBaseResource.class));
|
||||
}
|
||||
}).when(myRequestOperationCallback).resourceCreated(any(IBaseResource.class));
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addName().setFamily("PATIENT");
|
||||
IIdType id = myPatientDao.create(p, mySrd).getId();
|
||||
assertEquals(1L, id.getVersionIdPartAsLong().longValue());
|
||||
|
||||
|
||||
verify(myRequestOperationCallback, times(1)).resourcePreCreate(any(IBaseResource.class));
|
||||
verify(myRequestOperationCallback, times(1)).resourceCreated(any(IBaseResource.class));
|
||||
verifyNoMoreInteractions(myRequestOperationCallback);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testServerOperationCreate() {
|
||||
verify(myServerOperationInterceptor, times(0)).resourceCreated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class));
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addName().setFamily("PATIENT");
|
||||
IIdType id = myPatientDao.create(p, (RequestDetails)null).getId();
|
||||
assertEquals(1L, id.getVersionIdPartAsLong().longValue());
|
||||
|
||||
verify(myServerOperationInterceptor, times(1)).resourceCreated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class));
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Test
|
||||
public void testServerOperationUpdate() {
|
||||
verify(myServerOperationInterceptor, times(0)).resourceCreated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class));
|
||||
verify(myServerOperationInterceptor, times(0)).resourceUpdated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class));
|
||||
verify(myServerOperationInterceptor, times(0)).resourceUpdated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class), any(IBaseResource.class));
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addName().setFamily("PATIENT");
|
||||
IIdType id = myPatientDao.create(p, (RequestDetails)null).getId();
|
||||
assertEquals(1L, id.getVersionIdPartAsLong().longValue());
|
||||
|
||||
p.addName().setFamily("2");
|
||||
myPatientDao.update(p);
|
||||
|
||||
verify(myServerOperationInterceptor, times(1)).resourceCreated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class));
|
||||
verify(myServerOperationInterceptor, times(1)).resourceUpdated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class));
|
||||
verify(myServerOperationInterceptor, times(1)).resourceUpdated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class), any(IBaseResource.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testServerOperationDelete() {
|
||||
verify(myServerOperationInterceptor, times(0)).resourceCreated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class));
|
||||
verify(myServerOperationInterceptor, times(0)).resourceDeleted(Mockito.isNull(RequestDetails.class), any(IBaseResource.class));
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addName().setFamily("PATIENT");
|
||||
IIdType id = myPatientDao.create(p, (RequestDetails)null).getId();
|
||||
assertEquals(1L, id.getVersionIdPartAsLong().longValue());
|
||||
|
||||
p.addName().setFamily("2");
|
||||
myPatientDao.delete(p.getIdElement().toUnqualifiedVersionless());
|
||||
|
||||
verify(myServerOperationInterceptor, times(1)).resourceCreated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class));
|
||||
verify(myServerOperationInterceptor, times(1)).resourceDeleted(Mockito.isNull(RequestDetails.class), any(IBaseResource.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequestOperationDelete() {
|
||||
Patient p = new Patient();
|
||||
|
@ -253,16 +212,19 @@ public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test {
|
|||
|
||||
doAnswer(new Answer<Void>() {
|
||||
@Override
|
||||
public Void answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
public Void answer(InvocationOnMock theInvocation) {
|
||||
IBaseResource res = (IBaseResource) theInvocation.getArguments()[0];
|
||||
Long id = res.getIdElement().getIdPartAsLong();
|
||||
assertEquals("Patient/" + id + "/_history/2", res.getIdElement().getValue());
|
||||
return null;
|
||||
}}).when(myRequestOperationCallback).resourceDeleted(any(IBaseResource.class));
|
||||
}
|
||||
}).when(myRequestOperationCallback).resourceDeleted(any(IBaseResource.class));
|
||||
|
||||
IIdType newId = myPatientDao.delete(new IdType("Patient/" + id), mySrd).getId();
|
||||
assertEquals(2L, newId.getVersionIdPartAsLong().longValue());
|
||||
|
||||
verify(myRequestOperationCallback, times(1)).resourcePreDelete(any(IBaseResource.class));
|
||||
verify(myRequestOperationCallback, times(1)).resourcePreCreate(any(IBaseResource.class));
|
||||
verify(myRequestOperationCallback, times(1)).resourceDeleted(any(IBaseResource.class));
|
||||
verify(myRequestOperationCallback, times(1)).resourceCreated(any(IBaseResource.class));
|
||||
verifyNoMoreInteractions(myRequestOperationCallback);
|
||||
|
@ -271,7 +233,7 @@ public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test {
|
|||
@Test
|
||||
public void testRequestOperationDeleteMulti() {
|
||||
myDaoConfig.setAllowMultipleDelete(true);
|
||||
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addName().setFamily("PATIENT");
|
||||
Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong();
|
||||
|
@ -282,20 +244,23 @@ public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test {
|
|||
|
||||
doAnswer(new Answer<Void>() {
|
||||
@Override
|
||||
public Void answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
public Void answer(InvocationOnMock theInvocation) {
|
||||
IBaseResource res = (IBaseResource) theInvocation.getArguments()[0];
|
||||
Long id = res.getIdElement().getIdPartAsLong();
|
||||
assertEquals("Patient/" + id + "/_history/2", res.getIdElement().getValue());
|
||||
return null;
|
||||
}}).when(myRequestOperationCallback).resourceDeleted(any(IBaseResource.class));
|
||||
}
|
||||
}).when(myRequestOperationCallback).resourceDeleted(any(IBaseResource.class));
|
||||
|
||||
DeleteMethodOutcome outcome = myPatientDao.deleteByUrl("Patient?name=PATIENT", mySrd);
|
||||
String oo = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(outcome.getOperationOutcome());
|
||||
ourLog.info(oo);
|
||||
assertThat(oo, containsString("deleted 2 resource(s)"));
|
||||
|
||||
|
||||
verify(myRequestOperationCallback, times(2)).resourceDeleted(any(IBaseResource.class));
|
||||
verify(myRequestOperationCallback, times(2)).resourceCreated(any(IBaseResource.class));
|
||||
verify(myRequestOperationCallback, times(2)).resourcePreDelete(any(IBaseResource.class));
|
||||
verify(myRequestOperationCallback, times(2)).resourcePreCreate(any(IBaseResource.class));
|
||||
verifyNoMoreInteractions(myRequestOperationCallback);
|
||||
}
|
||||
|
||||
|
@ -306,27 +271,29 @@ public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test {
|
|||
|
||||
doAnswer(new Answer<Void>() {
|
||||
@Override
|
||||
public Void answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
public Void answer(InvocationOnMock theInvocation) {
|
||||
IBaseResource res = (IBaseResource) theInvocation.getArguments()[0];
|
||||
Long id = res.getIdElement().getIdPartAsLong();
|
||||
assertEquals("Patient/" + id + "/_history/1", res.getIdElement().getValue());
|
||||
return null;
|
||||
}}).when(myRequestOperationCallback).resourceCreated(any(IBaseResource.class));
|
||||
}
|
||||
}).when(myRequestOperationCallback).resourceCreated(any(IBaseResource.class));
|
||||
|
||||
Bundle xactBundle = new Bundle();
|
||||
xactBundle.setType(BundleType.TRANSACTION);
|
||||
xactBundle
|
||||
.addEntry()
|
||||
.setResource(p)
|
||||
.getRequest()
|
||||
.setUrl("Patient")
|
||||
.setMethod(HTTPVerb.POST);
|
||||
.addEntry()
|
||||
.setResource(p)
|
||||
.getRequest()
|
||||
.setUrl("Patient")
|
||||
.setMethod(HTTPVerb.POST);
|
||||
Bundle resp = mySystemDao.transaction(mySrd, xactBundle);
|
||||
|
||||
IdType newId = new IdType(resp.getEntry().get(0).getResponse().getLocation());
|
||||
assertEquals(1L, newId.getVersionIdPartAsLong().longValue());
|
||||
|
||||
|
||||
verify(myRequestOperationCallback, times(1)).resourceCreated(any(IBaseResource.class));
|
||||
verify(myRequestOperationCallback, times(1)).resourcePreCreate(any(IBaseResource.class));
|
||||
verifyNoMoreInteractions(myRequestOperationCallback);
|
||||
}
|
||||
|
||||
|
@ -338,25 +305,28 @@ public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test {
|
|||
|
||||
doAnswer(new Answer<Void>() {
|
||||
@Override
|
||||
public Void answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
public Void answer(InvocationOnMock theInvocation) {
|
||||
IBaseResource res = (IBaseResource) theInvocation.getArguments()[0];
|
||||
Long id = res.getIdElement().getIdPartAsLong();
|
||||
assertEquals("Patient/" + id + "/_history/2", res.getIdElement().getValue());
|
||||
return null;
|
||||
}}).when(myRequestOperationCallback).resourceDeleted(any(IBaseResource.class));
|
||||
}
|
||||
}).when(myRequestOperationCallback).resourceDeleted(any(IBaseResource.class));
|
||||
|
||||
Bundle xactBundle = new Bundle();
|
||||
xactBundle.setType(BundleType.TRANSACTION);
|
||||
xactBundle
|
||||
.addEntry()
|
||||
.getRequest()
|
||||
.setUrl("Patient/" + id)
|
||||
.setMethod(HTTPVerb.DELETE);
|
||||
.addEntry()
|
||||
.getRequest()
|
||||
.setUrl("Patient/" + id)
|
||||
.setMethod(HTTPVerb.DELETE);
|
||||
Bundle resp = mySystemDao.transaction(mySrd, xactBundle);
|
||||
|
||||
IdType newId = new IdType(resp.getEntry().get(0).getResponse().getLocation());
|
||||
assertEquals(2L, newId.getVersionIdPartAsLong().longValue());
|
||||
|
||||
verify(myRequestOperationCallback, times(1)).resourcePreDelete(any(IBaseResource.class));
|
||||
verify(myRequestOperationCallback, times(1)).resourcePreCreate(any(IBaseResource.class));
|
||||
verify(myRequestOperationCallback, times(1)).resourceDeleted(any(IBaseResource.class));
|
||||
verify(myRequestOperationCallback, times(1)).resourceCreated(any(IBaseResource.class));
|
||||
verifyNoMoreInteractions(myRequestOperationCallback);
|
||||
|
@ -365,7 +335,7 @@ public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test {
|
|||
@Test
|
||||
public void testRequestOperationTransactionDeleteMulti() {
|
||||
myDaoConfig.setAllowMultipleDelete(true);
|
||||
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addName().setFamily("PATIENT");
|
||||
Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong();
|
||||
|
@ -376,28 +346,31 @@ public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test {
|
|||
|
||||
doAnswer(new Answer<Void>() {
|
||||
@Override
|
||||
public Void answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
public Void answer(InvocationOnMock theInvocation) {
|
||||
IBaseResource res = (IBaseResource) theInvocation.getArguments()[0];
|
||||
Long id = res.getIdElement().getIdPartAsLong();
|
||||
assertEquals("Patient/" + id + "/_history/2", res.getIdElement().getValue());
|
||||
return null;
|
||||
}}).when(myRequestOperationCallback).resourceDeleted(any(IBaseResource.class));
|
||||
}
|
||||
}).when(myRequestOperationCallback).resourceDeleted(any(IBaseResource.class));
|
||||
|
||||
Bundle xactBundle = new Bundle();
|
||||
xactBundle.setType(BundleType.TRANSACTION);
|
||||
xactBundle
|
||||
.addEntry()
|
||||
.getRequest()
|
||||
.setUrl("Patient?name=PATIENT")
|
||||
.setMethod(HTTPVerb.DELETE);
|
||||
.addEntry()
|
||||
.getRequest()
|
||||
.setUrl("Patient?name=PATIENT")
|
||||
.setMethod(HTTPVerb.DELETE);
|
||||
Bundle resp = mySystemDao.transaction(mySrd, xactBundle);
|
||||
|
||||
String oo = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp);
|
||||
ourLog.info(oo);
|
||||
assertThat(oo, containsString("deleted 2 resource(s)"));
|
||||
|
||||
|
||||
verify(myRequestOperationCallback, times(2)).resourceDeleted(any(IBaseResource.class));
|
||||
verify(myRequestOperationCallback, times(2)).resourceCreated(any(IBaseResource.class));
|
||||
verify(myRequestOperationCallback, times(2)).resourcePreDelete(any(IBaseResource.class));
|
||||
verify(myRequestOperationCallback, times(2)).resourcePreCreate(any(IBaseResource.class));
|
||||
verifyNoMoreInteractions(myRequestOperationCallback);
|
||||
}
|
||||
|
||||
|
@ -413,20 +386,21 @@ public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test {
|
|||
|
||||
doAnswer(new Answer<Void>() {
|
||||
@Override
|
||||
public Void answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
public Void answer(InvocationOnMock theInvocation) {
|
||||
IBaseResource res = (IBaseResource) theInvocation.getArguments()[1];
|
||||
assertEquals("Patient/" + id + "/_history/2", res.getIdElement().getValue());
|
||||
return null;
|
||||
}}).when(myRequestOperationCallback).resourceUpdated(any(IBaseResource.class), any(IBaseResource.class));
|
||||
}
|
||||
}).when(myRequestOperationCallback).resourceUpdated(any(IBaseResource.class), any(IBaseResource.class));
|
||||
|
||||
Bundle xactBundle = new Bundle();
|
||||
xactBundle.setType(BundleType.TRANSACTION);
|
||||
xactBundle
|
||||
.addEntry()
|
||||
.setResource(p)
|
||||
.getRequest()
|
||||
.setUrl("Patient/" + id)
|
||||
.setMethod(HTTPVerb.PUT);
|
||||
.addEntry()
|
||||
.setResource(p)
|
||||
.getRequest()
|
||||
.setUrl("Patient/" + id)
|
||||
.setMethod(HTTPVerb.PUT);
|
||||
Bundle resp = mySystemDao.transaction(mySrd, xactBundle);
|
||||
|
||||
IdType newId = new IdType(resp.getEntry().get(0).getResponse().getLocation());
|
||||
|
@ -435,6 +409,8 @@ public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test {
|
|||
verify(myRequestOperationCallback, times(1)).resourceUpdated(any(IBaseResource.class));
|
||||
verify(myRequestOperationCallback, times(1)).resourceUpdated(any(IBaseResource.class), any(IBaseResource.class));
|
||||
verify(myRequestOperationCallback, times(1)).resourceCreated(any(IBaseResource.class));
|
||||
verify(myRequestOperationCallback, times(1)).resourcePreCreate(any(IBaseResource.class));
|
||||
verify(myRequestOperationCallback, times(1)).resourcePreUpdate(any(IBaseResource.class), any(IBaseResource.class));
|
||||
verifyNoMoreInteractions(myRequestOperationCallback);
|
||||
}
|
||||
|
||||
|
@ -446,13 +422,14 @@ public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test {
|
|||
|
||||
doAnswer(new Answer<Void>() {
|
||||
@Override
|
||||
public Void answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
public Void answer(InvocationOnMock theInvocation) {
|
||||
IBaseResource res = (IBaseResource) theInvocation.getArguments()[0];
|
||||
assertEquals("Patient/" + id + "/_history/1", res.getIdElement().getValue());
|
||||
res = (IBaseResource) theInvocation.getArguments()[1];
|
||||
assertEquals("Patient/" + id + "/_history/2", res.getIdElement().getValue());
|
||||
return null;
|
||||
}}).when(myRequestOperationCallback).resourceUpdated(any(IBaseResource.class), any(IBaseResource.class));
|
||||
}
|
||||
}).when(myRequestOperationCallback).resourceUpdated(any(IBaseResource.class), any(IBaseResource.class));
|
||||
|
||||
p = new Patient();
|
||||
p.setId(new IdType("Patient/" + id));
|
||||
|
@ -463,12 +440,197 @@ public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test {
|
|||
verify(myRequestOperationCallback, times(1)).resourceUpdated(any(IBaseResource.class));
|
||||
verify(myRequestOperationCallback, times(1)).resourceUpdated(any(IBaseResource.class), any(IBaseResource.class));
|
||||
verify(myRequestOperationCallback, times(1)).resourceCreated(any(IBaseResource.class));
|
||||
verify(myRequestOperationCallback, times(1)).resourcePreCreate(any(IBaseResource.class));
|
||||
verify(myRequestOperationCallback, times(1)).resourcePreUpdate(any(IBaseResource.class), any(IBaseResource.class));
|
||||
verifyNoMoreInteractions(myRequestOperationCallback);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testServerOperationCreate() {
|
||||
verify(myServerOperationInterceptor, times(0)).resourceCreated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class));
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addName().setFamily("PATIENT");
|
||||
IIdType id = myPatientDao.create(p, (RequestDetails) null).getId();
|
||||
assertEquals(1L, id.getVersionIdPartAsLong().longValue());
|
||||
|
||||
verify(myServerOperationInterceptor, times(1)).resourceCreated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testServerOperationDelete() {
|
||||
verify(myServerOperationInterceptor, times(0)).resourceCreated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class));
|
||||
verify(myServerOperationInterceptor, times(0)).resourceDeleted(Mockito.isNull(RequestDetails.class), any(IBaseResource.class));
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addName().setFamily("PATIENT");
|
||||
IIdType id = myPatientDao.create(p, (RequestDetails) null).getId();
|
||||
assertEquals(1L, id.getVersionIdPartAsLong().longValue());
|
||||
|
||||
p.addName().setFamily("2");
|
||||
myPatientDao.delete(p.getIdElement().toUnqualifiedVersionless());
|
||||
|
||||
verify(myServerOperationInterceptor, times(1)).resourceCreated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class));
|
||||
verify(myServerOperationInterceptor, times(1)).resourceDeleted(Mockito.isNull(RequestDetails.class), any(IBaseResource.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testServerOperationInterceptorCanModifyOnCreate() {
|
||||
|
||||
ServerOperationInterceptorAdapter interceptor = new ServerOperationInterceptorAdapter() {
|
||||
@Override
|
||||
public void resourcePreCreate(RequestDetails theRequest, IBaseResource theResource) {
|
||||
((Patient) theResource).setActive(true);
|
||||
}
|
||||
};
|
||||
myDaoConfig.getInterceptors().add(interceptor);
|
||||
try {
|
||||
|
||||
doAnswer(new MyOneResourceAnswer()).when(myJpaInterceptor).resourcePreCreate(any(RequestDetails.class), any(IBaseResource.class));
|
||||
doAnswer(new MyOneResourceAnswer()).when(myJpaInterceptor).resourceCreated(any(RequestDetails.class), any(IBaseResource.class));
|
||||
|
||||
Patient p = new Patient();
|
||||
p.setActive(false);
|
||||
IIdType id = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
InOrder inorder = inOrder(myJpaInterceptor, myRequestOperationCallback);
|
||||
inorder.verify(myRequestOperationCallback, times(1)).resourcePreCreate(any(IBaseResource.class));
|
||||
inorder.verify(myJpaInterceptor, times(1)).resourcePreCreate(any(RequestDetails.class), any(IBaseResource.class));
|
||||
inorder.verify(myRequestOperationCallback, times(1)).resourceCreated(any(IBaseResource.class));
|
||||
inorder.verify(myJpaInterceptor, times(1)).resourceCreated(any(RequestDetails.class), any(IBaseResource.class));
|
||||
|
||||
assertNull(myIds.get(0).getIdPart());
|
||||
assertNull(myIds.get(0).getVersionIdPart());
|
||||
assertNotNull(myIds.get(1).getIdPart());
|
||||
assertEquals("1", myIds.get(1).getVersionIdPart());
|
||||
|
||||
p = myPatientDao.read(id);
|
||||
assertEquals(true, p.getActive());
|
||||
|
||||
} finally {
|
||||
myDaoConfig.getInterceptors().remove(interceptor);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testServerOperationInterceptorCanModifyOnUpdate() {
|
||||
|
||||
ServerOperationInterceptorAdapter interceptor = new ServerOperationInterceptorAdapter() {
|
||||
@Override
|
||||
public void resourcePreUpdate(RequestDetails theRequest, IBaseResource theOldResource, IBaseResource theNewResource) {
|
||||
((Patient) theNewResource).setActive(true);
|
||||
}
|
||||
};
|
||||
myDaoConfig.getInterceptors().add(interceptor);
|
||||
try {
|
||||
|
||||
doAnswer(new MyTwoResourceAnswer()).when(myJpaInterceptor).resourcePreUpdate(any(RequestDetails.class), any(IBaseResource.class), any(IBaseResource.class));
|
||||
doAnswer(new MyTwoResourceAnswer()).when(myJpaInterceptor).resourceUpdated(any(RequestDetails.class), any(IBaseResource.class), any(IBaseResource.class));
|
||||
|
||||
Patient p = new Patient();
|
||||
p.setActive(false);
|
||||
IIdType id = myPatientDao.create(p).getId().toUnqualifiedVersionless();
|
||||
String idPart = id.getIdPart();
|
||||
|
||||
p = myPatientDao.read(id);
|
||||
assertEquals(false, p.getActive());
|
||||
|
||||
p.setId(p.getIdElement().toUnqualifiedVersionless());
|
||||
p.addAddress().setCity("CITY");
|
||||
myPatientDao.update(p, mySrd);
|
||||
|
||||
InOrder inorder = inOrder(myJpaInterceptor, myRequestOperationCallback);
|
||||
inorder.verify(myRequestOperationCallback, times(1)).resourcePreUpdate(any(IBaseResource.class), any(IBaseResource.class));
|
||||
inorder.verify(myJpaInterceptor, times(1)).resourcePreUpdate(any(RequestDetails.class), any(IBaseResource.class), any(IBaseResource.class));
|
||||
inorder.verify(myRequestOperationCallback, times(1)).resourceUpdated(any(IBaseResource.class), any(IBaseResource.class));
|
||||
inorder.verify(myJpaInterceptor, times(1)).resourceUpdated(any(RequestDetails.class), any(IBaseResource.class), any(IBaseResource.class));
|
||||
|
||||
// resourcePreUpdate
|
||||
assertEquals(idPart, myIds.get(0).getIdPart());
|
||||
assertEquals("1", myIds.get(0).getVersionIdPart());
|
||||
assertEquals(idPart, myIds.get(1).getIdPart());
|
||||
assertEquals(null, myIds.get(1).getVersionIdPart());
|
||||
// resourceUpdated
|
||||
assertEquals(idPart, myIds.get(2).getIdPart());
|
||||
assertEquals("1", myIds.get(2).getVersionIdPart());
|
||||
assertEquals(idPart, myIds.get(3).getIdPart());
|
||||
assertEquals("2", myIds.get(3).getVersionIdPart());
|
||||
|
||||
p = myPatientDao.read(id);
|
||||
assertEquals(true, p.getActive());
|
||||
|
||||
} finally {
|
||||
myDaoConfig.getInterceptors().remove(interceptor);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testServerOperationPreDelete() {
|
||||
|
||||
doAnswer(new MyOneResourceAnswer()).when(myJpaInterceptor).resourcePreDelete(any(RequestDetails.class), any(IBaseResource.class));
|
||||
doAnswer(new MyOneResourceAnswer()).when(myJpaInterceptor).resourceDeleted(any(RequestDetails.class), any(IBaseResource.class));
|
||||
|
||||
Patient p = new Patient();
|
||||
p.setActive(false);
|
||||
IIdType id = myPatientDao.create(p).getId().toUnqualifiedVersionless();
|
||||
String idPart = id.getIdPart();
|
||||
|
||||
myPatientDao.delete(id);
|
||||
|
||||
InOrder inorder = inOrder(myJpaInterceptor);
|
||||
inorder.verify(myJpaInterceptor, times(1)).resourcePreDelete(any(RequestDetails.class), any(IBaseResource.class));
|
||||
inorder.verify(myJpaInterceptor, times(1)).resourceDeleted(any(RequestDetails.class), any(IBaseResource.class));
|
||||
// resourcePreDelete
|
||||
assertEquals(idPart, myIds.get(0).getIdPart());
|
||||
assertEquals("1", myIds.get(0).getVersionIdPart());
|
||||
// resourceDeleted
|
||||
assertEquals(idPart, myIds.get(1).getIdPart());
|
||||
assertEquals("2", myIds.get(1).getVersionIdPart());
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Test
|
||||
public void testServerOperationUpdate() {
|
||||
verify(myServerOperationInterceptor, times(0)).resourceCreated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class));
|
||||
verify(myServerOperationInterceptor, times(0)).resourceUpdated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class));
|
||||
verify(myServerOperationInterceptor, times(0)).resourceUpdated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class), any(IBaseResource.class));
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addName().setFamily("PATIENT");
|
||||
IIdType id = myPatientDao.create(p, (RequestDetails) null).getId();
|
||||
assertEquals(1L, id.getVersionIdPartAsLong().longValue());
|
||||
|
||||
p.addName().setFamily("2");
|
||||
myPatientDao.update(p);
|
||||
|
||||
verify(myServerOperationInterceptor, times(1)).resourceCreated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class));
|
||||
verify(myServerOperationInterceptor, times(1)).resourceUpdated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class));
|
||||
verify(myServerOperationInterceptor, times(1)).resourceUpdated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class), any(IBaseResource.class));
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() {
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
private class MyOneResourceAnswer implements Answer {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) {
|
||||
IIdType id = ((IBaseResource) invocation.getArguments()[1]).getIdElement();
|
||||
myIds.add(new IdType(id.getValue()));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private class MyTwoResourceAnswer implements Answer {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) {
|
||||
IIdType id = ((IBaseResource) invocation.getArguments()[1]).getIdElement();
|
||||
myIds.add(new IdType(id.getValue()));
|
||||
id = ((IBaseResource) invocation.getArguments()[2]).getIdElement();
|
||||
myIds.add(new IdType(id.getValue()));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,9 +9,9 @@ package ca.uhn.fhir.rest.api.server;
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -20,16 +20,30 @@ package ca.uhn.fhir.rest.api.server;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public interface IRequestOperationCallback {
|
||||
|
||||
void resourceCreated(IBaseResource theResource);
|
||||
|
||||
void resourceDeleted(IBaseResource theResource);
|
||||
|
||||
void resourcePreCreate(IBaseResource theResource);
|
||||
|
||||
void resourcePreDelete(IBaseResource theResource);
|
||||
|
||||
void resourcePreUpdate(IBaseResource theOldResource, IBaseResource theNewResource);
|
||||
|
||||
/**
|
||||
* @deprecated Deprecated in HAPI FHIR 2.6 - Use {@link IRequestOperationCallback#resourceUpdated(IBaseResource, IBaseResource)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
void resourceUpdated(IBaseResource theResource);
|
||||
|
||||
void resourceUpdated(IBaseResource theOldResource, IBaseResource theNewResource);
|
||||
|
||||
void resourcesCreated(Collection<? extends IBaseResource> theResource);
|
||||
|
||||
void resourcesDeleted(Collection<? extends IBaseResource> theResource);
|
||||
|
@ -39,12 +53,4 @@ public interface IRequestOperationCallback {
|
|||
*/
|
||||
@Deprecated
|
||||
void resourcesUpdated(Collection<? extends IBaseResource> theResource);
|
||||
|
||||
/**
|
||||
* @deprecated Deprecated in HAPI FHIR 2.6 - Use {@link IRequestOperationCallback#resourceUpdated(IBaseResource, IBaseResource)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
void resourceUpdated(IBaseResource theResource);
|
||||
|
||||
void resourceUpdated(IBaseResource theOldResource, IBaseResource theNewResource);
|
||||
}
|
||||
|
|
|
@ -1,12 +1,24 @@
|
|||
package ca.uhn.fhir.rest.api.server;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.server.IRestfulServerDefaults;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerOperationInterceptor;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.*;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR - Server Framework
|
||||
|
@ -16,9 +28,9 @@ import java.nio.charset.Charset;
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -26,26 +38,316 @@ import java.nio.charset.Charset;
|
|||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.server.IRestfulServerDefaults;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerOperationInterceptor;
|
||||
|
||||
public abstract class RequestDetails {
|
||||
|
||||
private String myCompartmentName;
|
||||
private String myCompleteUrl;
|
||||
private String myFhirServerBase;
|
||||
private IIdType myId;
|
||||
private String myOperation;
|
||||
private Map<String, String[]> myParameters;
|
||||
private byte[] myRequestContents;
|
||||
private IRequestOperationCallback myRequestOperationCallback = new RequestOperationCallback();
|
||||
private String myRequestPath;
|
||||
private RequestTypeEnum myRequestType;
|
||||
private String myResourceName;
|
||||
private boolean myRespondGzip;
|
||||
private IRestfulResponse myResponse;
|
||||
private RestOperationTypeEnum myRestOperationType;
|
||||
private String mySecondaryOperation;
|
||||
private boolean mySubRequest;
|
||||
private Map<String, List<String>> myUnqualifiedToQualifiedNames;
|
||||
private Map<Object, Object> myUserData;
|
||||
|
||||
protected abstract byte[] getByteStreamRequestContents();
|
||||
|
||||
/**
|
||||
* Return the charset as defined by the header contenttype. Return null if it is not set.
|
||||
*/
|
||||
public abstract Charset getCharset();
|
||||
|
||||
public String getCompartmentName() {
|
||||
return myCompartmentName;
|
||||
}
|
||||
|
||||
public void setCompartmentName(String theCompartmentName) {
|
||||
myCompartmentName = theCompartmentName;
|
||||
}
|
||||
|
||||
public String getCompleteUrl() {
|
||||
return myCompleteUrl;
|
||||
}
|
||||
|
||||
public void setCompleteUrl(String theCompleteUrl) {
|
||||
myCompleteUrl = theCompleteUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <b>conditional URL</b> if this request has one, or <code>null</code> otherwise. For an
|
||||
* update or delete method, this is the part of the URL after the <code>?</code>. For a create, this
|
||||
* is the value of the <code>If-None-Exist</code> header.
|
||||
*
|
||||
* @param theOperationType The operation type to find the conditional URL for
|
||||
* @return Returns the <b>conditional URL</b> if this request has one, or <code>null</code> otherwise
|
||||
*/
|
||||
public String getConditionalUrl(RestOperationTypeEnum theOperationType) {
|
||||
if (theOperationType == RestOperationTypeEnum.CREATE) {
|
||||
String retVal = this.getHeader(Constants.HEADER_IF_NONE_EXIST);
|
||||
if (isBlank(retVal)) {
|
||||
return null;
|
||||
}
|
||||
if (retVal.startsWith(this.getFhirServerBase())) {
|
||||
retVal = retVal.substring(this.getFhirServerBase().length());
|
||||
}
|
||||
return retVal;
|
||||
} else if (theOperationType != RestOperationTypeEnum.DELETE && theOperationType != RestOperationTypeEnum.UPDATE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (this.getId() != null && this.getId().hasIdPart()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int questionMarkIndex = this.getCompleteUrl().indexOf('?');
|
||||
if (questionMarkIndex == -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.getResourceName() + this.getCompleteUrl().substring(questionMarkIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the HAPI FHIR Context associated with this request
|
||||
*/
|
||||
public abstract FhirContext getFhirContext();
|
||||
|
||||
/**
|
||||
* The fhir server base url, independant of the query being executed
|
||||
*
|
||||
* @return the fhir server base url
|
||||
*/
|
||||
public String getFhirServerBase() {
|
||||
return myFhirServerBase;
|
||||
}
|
||||
|
||||
public void setFhirServerBase(String theFhirServerBase) {
|
||||
myFhirServerBase = theFhirServerBase;
|
||||
}
|
||||
|
||||
public abstract String getHeader(String name);
|
||||
|
||||
public abstract List<String> getHeaders(String name);
|
||||
|
||||
public IIdType getId() {
|
||||
return myId;
|
||||
}
|
||||
|
||||
public void setId(IIdType theId) {
|
||||
myId = theId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the body of the request as binary data. Either this method or {@link #getReader} may be called to read
|
||||
* the body, not both.
|
||||
*
|
||||
* @return a {@link InputStream} object containing the body of the request
|
||||
* @throws IllegalStateException if the {@link #getReader} method has already been called for this request
|
||||
* @throws IOException if an input or output exception occurred
|
||||
*/
|
||||
public abstract InputStream getInputStream() throws IOException;
|
||||
|
||||
public String getOperation() {
|
||||
return myOperation;
|
||||
}
|
||||
|
||||
public void setOperation(String theOperation) {
|
||||
myOperation = theOperation;
|
||||
}
|
||||
|
||||
public Map<String, String[]> getParameters() {
|
||||
if (myParameters == null) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
return myParameters;
|
||||
}
|
||||
|
||||
public void setParameters(Map<String, String[]> theParams) {
|
||||
myParameters = theParams;
|
||||
|
||||
for (String next : theParams.keySet()) {
|
||||
for (int i = 0; i < next.length(); i++) {
|
||||
char nextChar = next.charAt(i);
|
||||
if (nextChar == ':' || nextChar == '.') {
|
||||
if (myUnqualifiedToQualifiedNames == null) {
|
||||
myUnqualifiedToQualifiedNames = new HashMap<>();
|
||||
}
|
||||
String unqualified = next.substring(0, i);
|
||||
List<String> list = myUnqualifiedToQualifiedNames.get(unqualified);
|
||||
if (list == null) {
|
||||
list = new ArrayList<>(4);
|
||||
myUnqualifiedToQualifiedNames.put(unqualified, list);
|
||||
}
|
||||
list.add(next);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (myUnqualifiedToQualifiedNames == null) {
|
||||
myUnqualifiedToQualifiedNames = Collections.emptyMap();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the body of the request as character data using a <code>BufferedReader</code>. The reader translates the
|
||||
* character data according to the character encoding used on the body. Either this method or {@link #getInputStream}
|
||||
* may be called to read the body, not both.
|
||||
*
|
||||
* @return a <code>Reader</code> containing the body of the request
|
||||
* @throws UnsupportedEncodingException if the character set encoding used is not supported and the text cannot be decoded
|
||||
* @throws IllegalStateException if {@link #getInputStream} method has been called on this request
|
||||
* @throws IOException if an input or output exception occurred
|
||||
* @see javax.servlet.http.HttpServletRequest#getInputStream
|
||||
*/
|
||||
public abstract Reader getReader() throws IOException;
|
||||
|
||||
/**
|
||||
* Returns an invoker that can be called from user code to advise the server interceptors
|
||||
* of any nested operations being invoked within operations. This invoker acts as a proxy for
|
||||
* all interceptors
|
||||
*/
|
||||
public IRequestOperationCallback getRequestOperationCallback() {
|
||||
return myRequestOperationCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* The part of the request URL that comes after the server base.
|
||||
* <p>
|
||||
* Will not contain a leading '/'
|
||||
* </p>
|
||||
*/
|
||||
public String getRequestPath() {
|
||||
return myRequestPath;
|
||||
}
|
||||
|
||||
public void setRequestPath(String theRequestPath) {
|
||||
assert theRequestPath.length() == 0 || theRequestPath.charAt(0) != '/';
|
||||
myRequestPath = theRequestPath;
|
||||
}
|
||||
|
||||
public RequestTypeEnum getRequestType() {
|
||||
return myRequestType;
|
||||
}
|
||||
|
||||
public void setRequestType(RequestTypeEnum theRequestType) {
|
||||
myRequestType = theRequestType;
|
||||
}
|
||||
|
||||
public String getResourceName() {
|
||||
return myResourceName;
|
||||
}
|
||||
|
||||
public void setResourceName(String theResourceName) {
|
||||
myResourceName = theResourceName;
|
||||
}
|
||||
|
||||
public IRestfulResponse getResponse() {
|
||||
return myResponse;
|
||||
}
|
||||
|
||||
public void setResponse(IRestfulResponse theResponse) {
|
||||
this.myResponse = theResponse;
|
||||
}
|
||||
|
||||
public RestOperationTypeEnum getRestOperationType() {
|
||||
return myRestOperationType;
|
||||
}
|
||||
|
||||
public void setRestOperationType(RestOperationTypeEnum theRestOperationType) {
|
||||
myRestOperationType = theRestOperationType;
|
||||
}
|
||||
|
||||
public String getSecondaryOperation() {
|
||||
return mySecondaryOperation;
|
||||
}
|
||||
|
||||
public void setSecondaryOperation(String theSecondaryOperation) {
|
||||
mySecondaryOperation = theSecondaryOperation;
|
||||
}
|
||||
|
||||
public abstract IRestfulServerDefaults getServer();
|
||||
|
||||
/**
|
||||
* Returns the server base URL (with no trailing '/') for a given request
|
||||
*/
|
||||
public abstract String getServerBaseForRequest();
|
||||
|
||||
public Map<String, List<String>> getUnqualifiedToQualifiedNames() {
|
||||
return myUnqualifiedToQualifiedNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a map which can be used to hold any user specific data to pass it from one
|
||||
* part of the request handling chain to another. Data in this map can use any key, although
|
||||
* user code should try to use keys which are specific enough to avoid conflicts.
|
||||
* <p>
|
||||
* A new map is created for each individual request that is handled by the server,
|
||||
* so this map can be used (for example) to pass authorization details from an interceptor
|
||||
* to the resource providers, or from an interceptor's {@link IServerInterceptor#incomingRequestPreHandled(RestOperationTypeEnum, ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails)}
|
||||
* method to the {@link IServerInterceptor#outgoingResponse(RequestDetails, org.hl7.fhir.instance.model.api.IBaseResource, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)}
|
||||
* method.
|
||||
* </p>
|
||||
*/
|
||||
public Map<Object, Object> getUserData() {
|
||||
if (myUserData == null) {
|
||||
myUserData = new HashMap<>();
|
||||
}
|
||||
return myUserData;
|
||||
}
|
||||
|
||||
public boolean isRespondGzip() {
|
||||
return myRespondGzip;
|
||||
}
|
||||
|
||||
public void setRespondGzip(boolean theRespondGzip) {
|
||||
myRespondGzip = theRespondGzip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this request a sub-request (i.e. a request within a batch or transaction)? This
|
||||
* flag is used internally by hapi-fhir-jpaserver-base, but not used in the plain server
|
||||
* library. You may use it in your client code as a hint when implementing transaction logic in the plain
|
||||
* server.
|
||||
* <p>
|
||||
* Defaults to {@literal false}
|
||||
* </p>
|
||||
*/
|
||||
public boolean isSubRequest() {
|
||||
return mySubRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this request a sub-request (i.e. a request within a batch or transaction)? This
|
||||
* flag is used internally by hapi-fhir-jpaserver-base, but not used in the plain server
|
||||
* library. You may use it in your client code as a hint when implementing transaction logic in the plain
|
||||
* server.
|
||||
* <p>
|
||||
* Defaults to {@literal false}
|
||||
* </p>
|
||||
*/
|
||||
public void setSubRequest(boolean theSubRequest) {
|
||||
mySubRequest = theSubRequest;
|
||||
}
|
||||
|
||||
public final byte[] loadRequestContents() {
|
||||
if (myRequestContents == null) {
|
||||
myRequestContents = getByteStreamRequestContents();
|
||||
}
|
||||
return myRequestContents;
|
||||
}
|
||||
|
||||
private class RequestOperationCallback implements IRequestOperationCallback {
|
||||
|
||||
private List<IServerInterceptor> getInterceptors() {
|
||||
|
@ -73,6 +375,55 @@ public abstract class RequestDetails {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resourcePreCreate(IBaseResource theResource) {
|
||||
for (IServerInterceptor next : getInterceptors()) {
|
||||
if (next instanceof IServerOperationInterceptor) {
|
||||
((IServerOperationInterceptor) next).resourcePreCreate(RequestDetails.this, theResource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resourcePreDelete(IBaseResource theResource) {
|
||||
for (IServerInterceptor next : getInterceptors()) {
|
||||
if (next instanceof IServerOperationInterceptor) {
|
||||
((IServerOperationInterceptor) next).resourcePreDelete(RequestDetails.this, theResource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resourcePreUpdate(IBaseResource theOldResource, IBaseResource theNewResource) {
|
||||
for (IServerInterceptor next : getInterceptors()) {
|
||||
if (next instanceof IServerOperationInterceptor) {
|
||||
((IServerOperationInterceptor) next).resourcePreUpdate(RequestDetails.this, theOldResource, theNewResource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Deprecated in HAPI FHIR 2.6 - Use {@link IRequestOperationCallback#resourceUpdated(IBaseResource, IBaseResource)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
@Override
|
||||
public void resourceUpdated(IBaseResource theResource) {
|
||||
for (IServerInterceptor next : getInterceptors()) {
|
||||
if (next instanceof IServerOperationInterceptor) {
|
||||
((IServerOperationInterceptor) next).resourceUpdated(RequestDetails.this, theResource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resourceUpdated(IBaseResource theOldResource, IBaseResource theNewResource) {
|
||||
for (IServerInterceptor next : getInterceptors()) {
|
||||
if (next instanceof IServerOperationInterceptor) {
|
||||
((IServerOperationInterceptor) next).resourceUpdated(RequestDetails.this, theOldResource, theNewResource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resourcesCreated(Collection<? extends IBaseResource> theResource) {
|
||||
for (IBaseResource next : theResource) {
|
||||
|
@ -97,345 +448,6 @@ public abstract class RequestDetails {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated Deprecated in HAPI FHIR 2.6 - Use {@link IRequestOperationCallback#resourceUpdated(IBaseResource, IBaseResource)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
@Override
|
||||
public void resourceUpdated(IBaseResource theResource) {
|
||||
for (IServerInterceptor next : getInterceptors()) {
|
||||
if (next instanceof IServerOperationInterceptor) {
|
||||
((IServerOperationInterceptor) next).resourceUpdated(RequestDetails.this, theResource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resourceUpdated(IBaseResource theOldResource, IBaseResource theNewResource) {
|
||||
for (IServerInterceptor next : getInterceptors()) {
|
||||
if (next instanceof IServerOperationInterceptor) {
|
||||
((IServerOperationInterceptor) next).resourceUpdated(RequestDetails.this, theOldResource, theNewResource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
private String myCompartmentName;
|
||||
private String myCompleteUrl;
|
||||
private String myFhirServerBase;
|
||||
private IIdType myId;
|
||||
private String myOperation;
|
||||
private Map<String, String[]> myParameters;
|
||||
private byte[] myRequestContents;
|
||||
private IRequestOperationCallback myRequestOperationCallback = new RequestOperationCallback();
|
||||
private String myRequestPath;
|
||||
private RequestTypeEnum myRequestType;
|
||||
private String myResourceName;
|
||||
private boolean myRespondGzip;
|
||||
private IRestfulResponse myResponse;
|
||||
private RestOperationTypeEnum myRestOperationType;
|
||||
private String mySecondaryOperation;
|
||||
private boolean mySubRequest;
|
||||
private Map<String, List<String>> myUnqualifiedToQualifiedNames;
|
||||
private Map<Object, Object> myUserData;
|
||||
|
||||
protected abstract byte[] getByteStreamRequestContents();
|
||||
|
||||
/**
|
||||
* Return the charset as defined by the header contenttype. Return null if it is not set.
|
||||
*/
|
||||
public abstract Charset getCharset();
|
||||
|
||||
public String getCompartmentName() {
|
||||
return myCompartmentName;
|
||||
}
|
||||
public String getCompleteUrl() {
|
||||
return myCompleteUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <b>conditional URL</b> if this request has one, or <code>null</code> otherwise. For an
|
||||
* update or delete method, this is the part of the URL after the <code>?</code>. For a create, this
|
||||
* is the value of the <code>If-None-Exist</code> header.
|
||||
*
|
||||
* @param theOperationType The operation type to find the conditional URL for
|
||||
* @return Returns the <b>conditional URL</b> if this request has one, or <code>null</code> otherwise
|
||||
*/
|
||||
public String getConditionalUrl(RestOperationTypeEnum theOperationType) {
|
||||
if (theOperationType == RestOperationTypeEnum.CREATE) {
|
||||
String retVal = this.getHeader(Constants.HEADER_IF_NONE_EXIST);
|
||||
if (isBlank(retVal)) {
|
||||
return null;
|
||||
}
|
||||
if (retVal.startsWith(this.getFhirServerBase())) {
|
||||
retVal = retVal.substring(this.getFhirServerBase().length());
|
||||
}
|
||||
return retVal;
|
||||
} else if (theOperationType != RestOperationTypeEnum.DELETE && theOperationType != RestOperationTypeEnum.UPDATE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (this.getId() != null && this.getId().hasIdPart()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int questionMarkIndex = this.getCompleteUrl().indexOf('?');
|
||||
if (questionMarkIndex == -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.getResourceName() + this.getCompleteUrl().substring(questionMarkIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the HAPI FHIR Context associated with this request
|
||||
*/
|
||||
public abstract FhirContext getFhirContext();
|
||||
|
||||
/**
|
||||
* The fhir server base url, independant of the query being executed
|
||||
*
|
||||
* @return the fhir server base url
|
||||
*/
|
||||
public String getFhirServerBase() {
|
||||
return myFhirServerBase;
|
||||
}
|
||||
|
||||
public abstract String getHeader(String name);
|
||||
|
||||
public abstract List<String> getHeaders(String name);
|
||||
|
||||
public IIdType getId() {
|
||||
return myId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the body of the request as binary data. Either this method or {@link #getReader} may be called to read
|
||||
* the body, not both.
|
||||
*
|
||||
* @return a {@link InputStream} object containing the body of the request
|
||||
*
|
||||
* @exception IllegalStateException
|
||||
* if the {@link #getReader} method has already been called for this request
|
||||
*
|
||||
* @exception IOException
|
||||
* if an input or output exception occurred
|
||||
*/
|
||||
public abstract InputStream getInputStream() throws IOException;
|
||||
|
||||
public String getOperation() {
|
||||
return myOperation;
|
||||
}
|
||||
|
||||
public Map<String, String[]> getParameters() {
|
||||
if (myParameters == null) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
return myParameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the body of the request as character data using a <code>BufferedReader</code>. The reader translates the
|
||||
* character data according to the character encoding used on the body. Either this method or {@link #getInputStream}
|
||||
* may be called to read the body, not both.
|
||||
*
|
||||
* @return a <code>Reader</code> containing the body of the request
|
||||
*
|
||||
* @exception UnsupportedEncodingException
|
||||
* if the character set encoding used is not supported and the text cannot be decoded
|
||||
*
|
||||
* @exception IllegalStateException
|
||||
* if {@link #getInputStream} method has been called on this request
|
||||
*
|
||||
* @exception IOException
|
||||
* if an input or output exception occurred
|
||||
*
|
||||
* @see javax.servlet.http.HttpServletRequest#getInputStream
|
||||
*/
|
||||
public abstract Reader getReader() throws IOException;
|
||||
|
||||
/**
|
||||
* Returns an invoker that can be called from user code to advise the server interceptors
|
||||
* of any nested operations being invoked within operations. This invoker acts as a proxy for
|
||||
* all interceptors
|
||||
*/
|
||||
public IRequestOperationCallback getRequestOperationCallback() {
|
||||
return myRequestOperationCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* The part of the request URL that comes after the server base.
|
||||
* <p>
|
||||
* Will not contain a leading '/'
|
||||
* </p>
|
||||
*/
|
||||
public String getRequestPath() {
|
||||
return myRequestPath;
|
||||
}
|
||||
|
||||
public RequestTypeEnum getRequestType() {
|
||||
return myRequestType;
|
||||
}
|
||||
|
||||
public String getResourceName() {
|
||||
return myResourceName;
|
||||
}
|
||||
|
||||
public IRestfulResponse getResponse() {
|
||||
return myResponse;
|
||||
}
|
||||
|
||||
public RestOperationTypeEnum getRestOperationType() {
|
||||
return myRestOperationType;
|
||||
}
|
||||
|
||||
public String getSecondaryOperation() {
|
||||
return mySecondaryOperation;
|
||||
}
|
||||
|
||||
public abstract IRestfulServerDefaults getServer();
|
||||
|
||||
/**
|
||||
* Returns the server base URL (with no trailing '/') for a given request
|
||||
*/
|
||||
public abstract String getServerBaseForRequest();
|
||||
|
||||
public Map<String, List<String>> getUnqualifiedToQualifiedNames() {
|
||||
return myUnqualifiedToQualifiedNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a map which can be used to hold any user specific data to pass it from one
|
||||
* part of the request handling chain to another. Data in this map can use any key, although
|
||||
* user code should try to use keys which are specific enough to avoid conflicts.
|
||||
* <p>
|
||||
* A new map is created for each individual request that is handled by the server,
|
||||
* so this map can be used (for example) to pass authorization details from an interceptor
|
||||
* to the resource providers, or from an interceptor's {@link IServerInterceptor#incomingRequestPreHandled(RestOperationTypeEnum, ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails)}
|
||||
* method to the {@link IServerInterceptor#outgoingResponse(RequestDetails, org.hl7.fhir.instance.model.api.IBaseResource, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)}
|
||||
* method.
|
||||
* </p>
|
||||
*/
|
||||
public Map<Object, Object> getUserData() {
|
||||
if (myUserData == null) {
|
||||
myUserData = new HashMap<Object, Object>();
|
||||
}
|
||||
return myUserData;
|
||||
}
|
||||
|
||||
public boolean isRespondGzip() {
|
||||
return myRespondGzip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this request a sub-request (i.e. a request within a batch or transaction)? This
|
||||
* flag is used internally by hapi-fhir-jpaserver-base, but not used in the plain server
|
||||
* library. You may use it in your client code as a hint when implementing transaction logic in the plain
|
||||
* server.
|
||||
* <p>
|
||||
* Defaults to {@literal false}
|
||||
* </p>
|
||||
*/
|
||||
public boolean isSubRequest() {
|
||||
return mySubRequest;
|
||||
}
|
||||
|
||||
public final byte[] loadRequestContents() {
|
||||
if (myRequestContents == null) {
|
||||
myRequestContents = getByteStreamRequestContents();
|
||||
}
|
||||
return myRequestContents;
|
||||
}
|
||||
|
||||
public void setCompartmentName(String theCompartmentName) {
|
||||
myCompartmentName = theCompartmentName;
|
||||
}
|
||||
|
||||
public void setCompleteUrl(String theCompleteUrl) {
|
||||
myCompleteUrl = theCompleteUrl;
|
||||
}
|
||||
|
||||
public void setFhirServerBase(String theFhirServerBase) {
|
||||
myFhirServerBase = theFhirServerBase;
|
||||
}
|
||||
|
||||
public void setId(IIdType theId) {
|
||||
myId = theId;
|
||||
}
|
||||
|
||||
public void setOperation(String theOperation) {
|
||||
myOperation = theOperation;
|
||||
}
|
||||
|
||||
public void setParameters(Map<String, String[]> theParams) {
|
||||
myParameters = theParams;
|
||||
|
||||
for (String next : theParams.keySet()) {
|
||||
for (int i = 0; i < next.length(); i++) {
|
||||
char nextChar = next.charAt(i);
|
||||
if (nextChar == ':' || nextChar == '.') {
|
||||
if (myUnqualifiedToQualifiedNames == null) {
|
||||
myUnqualifiedToQualifiedNames = new HashMap<String, List<String>>();
|
||||
}
|
||||
String unqualified = next.substring(0, i);
|
||||
List<String> list = myUnqualifiedToQualifiedNames.get(unqualified);
|
||||
if (list == null) {
|
||||
list = new ArrayList<String>(4);
|
||||
myUnqualifiedToQualifiedNames.put(unqualified, list);
|
||||
}
|
||||
list.add(next);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (myUnqualifiedToQualifiedNames == null) {
|
||||
myUnqualifiedToQualifiedNames = Collections.emptyMap();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void setRequestPath(String theRequestPath) {
|
||||
assert theRequestPath.length() == 0 || theRequestPath.charAt(0) != '/';
|
||||
myRequestPath = theRequestPath;
|
||||
}
|
||||
|
||||
public void setRequestType(RequestTypeEnum theRequestType) {
|
||||
myRequestType = theRequestType;
|
||||
}
|
||||
|
||||
public void setResourceName(String theResourceName) {
|
||||
myResourceName = theResourceName;
|
||||
}
|
||||
|
||||
public void setRespondGzip(boolean theRespondGzip) {
|
||||
myRespondGzip = theRespondGzip;
|
||||
}
|
||||
|
||||
public void setResponse(IRestfulResponse theResponse) {
|
||||
this.myResponse = theResponse;
|
||||
}
|
||||
|
||||
public void setRestOperationType(RestOperationTypeEnum theRestOperationType) {
|
||||
myRestOperationType = theRestOperationType;
|
||||
}
|
||||
|
||||
public void setSecondaryOperation(String theSecondaryOperation) {
|
||||
mySecondaryOperation = theSecondaryOperation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this request a sub-request (i.e. a request within a batch or transaction)? This
|
||||
* flag is used internally by hapi-fhir-jpaserver-base, but not used in the plain server
|
||||
* library. You may use it in your client code as a hint when implementing transaction logic in the plain
|
||||
* server.
|
||||
* <p>
|
||||
* Defaults to {@literal false}
|
||||
* </p>
|
||||
*/
|
||||
public void setSubRequest(boolean theSubRequest) {
|
||||
mySubRequest = theSubRequest;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,9 +9,9 @@ package ca.uhn.fhir.rest.server.interceptor;
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -20,49 +20,103 @@ package ca.uhn.fhir.rest.server.interceptor;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
/**
|
||||
* Server interceptor with added methods which can be called within the lifecycle of
|
||||
* write operations (create/update/delete) or within transaction and batch
|
||||
* operations that call these sub-operations.
|
||||
*
|
||||
*
|
||||
* @see ServerOperationInterceptorAdapter
|
||||
*/
|
||||
public interface IServerOperationInterceptor extends IServerInterceptor {
|
||||
|
||||
/**
|
||||
* User code may call this method to indicate to an interceptor that
|
||||
* a resource is being created
|
||||
* This method is called by the server immediately after a resource has
|
||||
* been created, within the database transaction scope of the operation.
|
||||
* <p>
|
||||
* If an exception is thrown by an interceptor during this method,
|
||||
* the transaction will be rolled back.
|
||||
* </p>
|
||||
*/
|
||||
void resourceCreated(RequestDetails theRequest, IBaseResource theResource);
|
||||
|
||||
/**
|
||||
* User code may call this method to indicate to an interceptor that
|
||||
* a resource is being deleted
|
||||
* This method is called by the server immediately after a resource has
|
||||
* been deleted, within the database transaction scope of the operation.
|
||||
* <p>
|
||||
* If an exception is thrown by an interceptor during this method,
|
||||
* the transaction will be rolled back.
|
||||
* </p>
|
||||
*/
|
||||
void resourceDeleted(RequestDetails theRequest, IBaseResource theResource);
|
||||
|
||||
/**
|
||||
* User code may call this method to indicate to an interceptor that
|
||||
* a resource is being updated
|
||||
*
|
||||
* This method is called by the server immediately before a resource is about
|
||||
* to be created, within the database transaction scope of the operation.
|
||||
* <p>
|
||||
* This method may be used to modify the resource
|
||||
* </p>
|
||||
* <p>
|
||||
* If an exception is thrown by an interceptor during this method,
|
||||
* the transaction will be rolled back.
|
||||
* </p>
|
||||
*
|
||||
* @param theResource The resource that has been provided by the client as the payload
|
||||
* to create. Interceptors may modify this
|
||||
* resource, and modifications will affect what is saved in the database.
|
||||
*/
|
||||
void resourcePreCreate(RequestDetails theRequest, IBaseResource theResource);
|
||||
|
||||
/**
|
||||
* This method is called by the server immediately before a resource is about
|
||||
* to be deleted, within the database transaction scope of the operation.
|
||||
* <p>
|
||||
* If an exception is thrown by an interceptor during this method,
|
||||
* the transaction will be rolled back.
|
||||
* </p>
|
||||
*
|
||||
* @param theResource The resource which is about to be deleted
|
||||
*/
|
||||
void resourcePreDelete(RequestDetails theRequest, IBaseResource theResource);
|
||||
|
||||
/**
|
||||
* This method is called by the server immediately before a resource is about
|
||||
* to be updated, within the database transaction scope of the operation.
|
||||
* <p>
|
||||
* This method may be used to modify the resource
|
||||
* </p>
|
||||
* <p>
|
||||
* If an exception is thrown by an interceptor during this method,
|
||||
* the transaction will be rolled back.
|
||||
* </p>
|
||||
*
|
||||
* @param theOldResource The previous version of the resource, or <code>null</code> if this is not available. Interceptors should be able to handle situations where this is null, since it is not always
|
||||
* convenient or possible to provide a value for this field, but servers should try to populate it.
|
||||
* @param theNewResource The resource that has been provided by the client as the payload
|
||||
* to update to the resource to. Interceptors may modify this
|
||||
* resource, and modifications will affect what is saved in the database.
|
||||
*/
|
||||
void resourcePreUpdate(RequestDetails theRequest, IBaseResource theOldResource, IBaseResource theNewResource);
|
||||
|
||||
/**
|
||||
* @deprecated Deprecated in HAPI FHIR 3.0.0 in favour of {@link #resourceUpdated(RequestDetails, IBaseResource, IBaseResource)}
|
||||
*/
|
||||
@Deprecated
|
||||
void resourceUpdated(RequestDetails theRequest, IBaseResource theResource);
|
||||
|
||||
/**
|
||||
* User code may call this method to indicate to an interceptor that
|
||||
* a resource is being updated
|
||||
*
|
||||
* @param theOldResource
|
||||
* The resource as it was before the update, or <code>null</code> if this is not available. Interceptors should be able to handle situations where this is null, since it is not always
|
||||
* convenient or possible to provide a value for this field, but servers should try to populate it.
|
||||
* @param theNewResource
|
||||
* The resource as it will be after the update
|
||||
* This method is called by the server immediately after a resource has
|
||||
* been created, within the database transaction scope of the operation.
|
||||
* <p>
|
||||
* If an exception is thrown by an interceptor during this method,
|
||||
* the transaction will be rolled back.
|
||||
* </p>
|
||||
*
|
||||
* @param theOldResource The resource as it was before the update, or <code>null</code> if this is not available. Interceptors should be able to handle situations where this is null, since it is not always
|
||||
* convenient or possible to provide a value for this field, but servers should try to populate it.
|
||||
* @param theNewResource The resource as it will be after the update
|
||||
*/
|
||||
void resourceUpdated(RequestDetails theRequest, IBaseResource theOldResource, IBaseResource theNewResource);
|
||||
|
||||
|
|
|
@ -34,6 +34,21 @@ public class ServerOperationInterceptorAdapter extends InterceptorAdapter implem
|
|||
// nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resourcePreCreate(RequestDetails theRequest, IBaseResource theResource) {
|
||||
// nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resourcePreDelete(RequestDetails theRequest, IBaseResource theResource) {
|
||||
// nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resourcePreUpdate(RequestDetails theRequest, IBaseResource theOldResource, IBaseResource theNewResource) {
|
||||
// nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resourceCreated(RequestDetails theRequest, IBaseResource theResource) {
|
||||
// nothing
|
||||
|
|
|
@ -49,6 +49,19 @@
|
|||
Fix an issue in JPA server where updating a resource sometimes caused date search indexes to
|
||||
be incorrectly deleted. Thanks to Kyle Meadows for the pull request!
|
||||
</action>
|
||||
<action type="add">
|
||||
A new set of methods have been added to
|
||||
<![CDATA[<code>IServerOperationInterceptor</code>]]>
|
||||
called
|
||||
<![CDATA[<code>resourcePreCreate</code>]]>,
|
||||
<![CDATA[<code>resourcePreUpdate</code>]]>, and
|
||||
<![CDATA[<code>resourcePreDelete</code>]]>. These
|
||||
methods are called within the database transaction
|
||||
(just as the existing methods were) but are invoked
|
||||
prior to the contents being saved to the database. This
|
||||
can be useful in order to allow interceptors to
|
||||
change payload contents being saved.
|
||||
</action>
|
||||
</release>
|
||||
<release version="3.1.0" date="2017-11-23">
|
||||
<action type="add">
|
||||
|
|
Loading…
Reference in New Issue