package ca.uhn.fhir.jpa.dao.dstu3; import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import java.util.Arrays; import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.Bundle.BundleType; import org.hl7.fhir.dstu3.model.Bundle.HTTPVerb; import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.jpa.dao.DaoMethodOutcome; import ca.uhn.fhir.jpa.dao.DeleteMethodOutcome; import ca.uhn.fhir.jpa.entity.ResourceTable; import ca.uhn.fhir.jpa.interceptor.IJpaServerInterceptor; import ca.uhn.fhir.jpa.interceptor.JpaServerInterceptorAdapter; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.rest.method.RequestDetails; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; import ca.uhn.fhir.rest.server.interceptor.IServerOperationInterceptor; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.TestUtil; public class FhirResourceDaoDstu3InterceptorTest extends BaseJpaDstu3Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu3InterceptorTest.class); private IJpaServerInterceptor myJpaInterceptor; private JpaServerInterceptorAdapter myJpaInterceptorAdapter = new JpaServerInterceptorAdapter(); private IServerOperationInterceptor myServerOperationInterceptor; @After public void after() { myDaoConfig.getInterceptors().remove(myJpaInterceptor); myDaoConfig.getInterceptors().remove(myJpaInterceptorAdapter); myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete()); } @Before public void before() { myJpaInterceptor = mock(IJpaServerInterceptor.class); myServerOperationInterceptor = mock(IServerOperationInterceptor.class, new Answer<Object>() { @Override public Object answer(InvocationOnMock theInvocation) throws Throwable { if (theInvocation.getMethod().getReturnType().equals(boolean.class)) { return true; } return null; } }); myDaoConfig.getInterceptors().add(myJpaInterceptor); myDaoConfig.getInterceptors().add(myJpaInterceptorAdapter); myDaoConfig.getInterceptors().add(myServerOperationInterceptor); } @Test public void testJpaCreate() { Patient p = new Patient(); p.addName().setFamily("PATIENT"); Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong(); ArgumentCaptor<ActionRequestDetails> detailsCapt; ArgumentCaptor<ResourceTable> tableCapt; detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); tableCapt = ArgumentCaptor.forClass(ResourceTable.class); verify(myJpaInterceptor, times(1)).resourceCreated(detailsCapt.capture(), tableCapt.capture()); assertNotNull(tableCapt.getValue().getId()); assertEquals(id, tableCapt.getValue().getId()); detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); tableCapt = ArgumentCaptor.forClass(ResourceTable.class); verify(myJpaInterceptor, times(0)).resourceUpdated(detailsCapt.capture(), tableCapt.capture()); /* * Not do a conditional create */ p = new Patient(); p.addName().setFamily("PATIENT1"); Long id2 = myPatientDao.create(p, "Patient?family=PATIENT", mySrd).getId().getIdPartAsLong(); assertEquals(id, id2); detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); tableCapt = ArgumentCaptor.forClass(ResourceTable.class); verify(myJpaInterceptor, times(1)).resourceCreated(detailsCapt.capture(), tableCapt.capture()); verify(myJpaInterceptor, times(0)).resourceUpdated(detailsCapt.capture(), tableCapt.capture()); } /* * ***************************************************** * Note that non JPA specific operations get tested in individual * operation test methods too * ***************************************************** */ @Test public void testJpaDelete() { Patient p = new Patient(); p.addName().setFamily("PATIENT"); Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong(); myPatientDao.delete(new IdType("Patient", id), mySrd); ArgumentCaptor<ActionRequestDetails> detailsCapt; ArgumentCaptor<ResourceTable> tableCapt; detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); tableCapt = ArgumentCaptor.forClass(ResourceTable.class); verify(myJpaInterceptor, times(1)).resourceDeleted(detailsCapt.capture(), tableCapt.capture()); assertNotNull(tableCapt.getValue().getId()); assertEquals(id, tableCapt.getValue().getId()); } @Test public void testJpaUpdate() { Patient p = new Patient(); p.addName().setFamily("PATIENT"); Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong(); p = new Patient(); p.setId(new IdType(id)); p.addName().setFamily("PATIENT1"); Long id2 = myPatientDao.update(p, mySrd).getId().getIdPartAsLong(); assertEquals(id, id2); ArgumentCaptor<ActionRequestDetails> detailsCapt; ArgumentCaptor<ResourceTable> tableCapt; detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); tableCapt = ArgumentCaptor.forClass(ResourceTable.class); verify(myJpaInterceptor, times(1)).resourceUpdated(detailsCapt.capture(), tableCapt.capture()); assertNotNull(tableCapt.getValue().getId()); assertEquals(id, tableCapt.getValue().getId()); /* * Now do a conditional update */ p = new Patient(); p.setId(new IdType(id)); p.addName().setFamily("PATIENT2"); id2 = myPatientDao.update(p, "Patient?family=PATIENT1", mySrd).getId().getIdPartAsLong(); assertEquals(id, id2); detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); tableCapt = ArgumentCaptor.forClass(ResourceTable.class); verify(myJpaInterceptor, times(1)).resourceCreated(detailsCapt.capture(), tableCapt.capture()); verify(myJpaInterceptor, times(2)).resourceUpdated(detailsCapt.capture(), tableCapt.capture()); assertEquals(id, tableCapt.getAllValues().get(2).getId()); /* * Now do a conditional update where none will match (so this is actually a create) */ p = new Patient(); p.addName().setFamily("PATIENT3"); id2 = myPatientDao.update(p, "Patient?family=ZZZ", mySrd).getId().getIdPartAsLong(); assertNotEquals(id, id2); detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); tableCapt = ArgumentCaptor.forClass(ResourceTable.class); verify(myJpaInterceptor, times(2)).resourceUpdated(detailsCapt.capture(), tableCapt.capture()); verify(myJpaInterceptor, times(2)).resourceCreated(detailsCapt.capture(), tableCapt.capture()); assertEquals(id2, tableCapt.getAllValues().get(3).getId()); } @Test public void testRequestOperationCreate() { IServerOperationInterceptor interceptor = mock(IServerOperationInterceptor.class); myServerInterceptorList.add(interceptor); doAnswer(new Answer<Void>() { @Override public Void answer(InvocationOnMock theInvocation) throws Throwable { 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)); Patient p = new Patient(); p.addName().setFamily("PATIENT"); IIdType id = myPatientDao.create(p, mySrd).getId(); assertEquals(1L, id.getVersionIdPartAsLong().longValue()); 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)); } @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)); 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)); } @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(); p.addName().setFamily("PATIENT"); Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong(); doAnswer(new Answer<Void>() { @Override public Void answer(InvocationOnMock theInvocation) throws Throwable { 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)); IIdType newId = myPatientDao.delete(new IdType("Patient/" + id), mySrd).getId(); assertEquals(2L, newId.getVersionIdPartAsLong().longValue()); verify(myRequestOperationCallback, times(1)).resourceDeleted(any(IBaseResource.class)); verify(myRequestOperationCallback, times(1)).resourceCreated(any(IBaseResource.class)); verifyNoMoreInteractions(myRequestOperationCallback); } @Test public void testRequestOperationDeleteMulti() { myDaoConfig.setAllowMultipleDelete(true); Patient p = new Patient(); p.addName().setFamily("PATIENT"); Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong(); p = new Patient(); p.addName().setFamily("PATIENT"); Long id2 = myPatientDao.create(p, mySrd).getId().getIdPartAsLong(); doAnswer(new Answer<Void>() { @Override public Void answer(InvocationOnMock theInvocation) throws Throwable { 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)); 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)); verifyNoMoreInteractions(myRequestOperationCallback); } @Test public void testRequestOperationTransactionCreate() { Patient p = new Patient(); p.addName().setFamily("PATIENT"); doAnswer(new Answer<Void>() { @Override public Void answer(InvocationOnMock theInvocation) throws Throwable { 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)); Bundle xactBundle = new Bundle(); xactBundle.setType(BundleType.TRANSACTION); xactBundle .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)); verifyNoMoreInteractions(myRequestOperationCallback); } @Test public void testRequestOperationTransactionDelete() { Patient p = new Patient(); p.addName().setFamily("PATIENT"); Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong(); doAnswer(new Answer<Void>() { @Override public Void answer(InvocationOnMock theInvocation) throws Throwable { 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)); Bundle xactBundle = new Bundle(); xactBundle.setType(BundleType.TRANSACTION); xactBundle .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)).resourceDeleted(any(IBaseResource.class)); verify(myRequestOperationCallback, times(1)).resourceCreated(any(IBaseResource.class)); verifyNoMoreInteractions(myRequestOperationCallback); } @Test public void testRequestOperationTransactionDeleteMulti() { myDaoConfig.setAllowMultipleDelete(true); Patient p = new Patient(); p.addName().setFamily("PATIENT"); Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong(); p = new Patient(); p.addName().setFamily("PATIENT"); Long id2 = myPatientDao.create(p, mySrd).getId().getIdPartAsLong(); doAnswer(new Answer<Void>() { @Override public Void answer(InvocationOnMock theInvocation) throws Throwable { 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)); Bundle xactBundle = new Bundle(); xactBundle.setType(BundleType.TRANSACTION); xactBundle .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)); verifyNoMoreInteractions(myRequestOperationCallback); } @Test public void testRequestOperationTransactionUpdate() { Patient p = new Patient(); p.addName().setFamily("PATIENT"); final Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong(); p = new Patient(); p.setId(new IdType("Patient/" + id)); p.addName().setFamily("PATIENT2"); doAnswer(new Answer<Void>() { @Override public Void answer(InvocationOnMock theInvocation) throws Throwable { IBaseResource res = (IBaseResource) theInvocation.getArguments()[0]; assertEquals("Patient/" + id + "/_history/2", res.getIdElement().getValue()); return null; }}).when(myRequestOperationCallback).resourceUpdated(any(IBaseResource.class)); Bundle xactBundle = new Bundle(); xactBundle.setType(BundleType.TRANSACTION); xactBundle .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()); assertEquals(2L, newId.getVersionIdPartAsLong().longValue()); verify(myRequestOperationCallback, times(1)).resourceUpdated(any(IBaseResource.class)); verify(myRequestOperationCallback, times(1)).resourceCreated(any(IBaseResource.class)); verifyNoMoreInteractions(myRequestOperationCallback); } @Test public void testRequestOperationUpdate() { Patient p = new Patient(); p.addName().setFamily("PATIENT"); final Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong(); doAnswer(new Answer<Void>() { @Override public Void answer(InvocationOnMock theInvocation) throws Throwable { IBaseResource res = (IBaseResource) theInvocation.getArguments()[0]; assertEquals("Patient/" + id + "/_history/2", res.getIdElement().getValue()); return null; }}).when(myRequestOperationCallback).resourceUpdated(any(IBaseResource.class)); p = new Patient(); p.setId(new IdType("Patient/" + id)); p.addName().setFamily("PATIENT2"); IIdType newId = myPatientDao.update(p, mySrd).getId(); assertEquals(2L, newId.getVersionIdPartAsLong().longValue()); verify(myRequestOperationCallback, times(1)).resourceUpdated(any(IBaseResource.class)); verify(myRequestOperationCallback, times(1)).resourceCreated(any(IBaseResource.class)); verifyNoMoreInteractions(myRequestOperationCallback); } @AfterClass public static void afterClassClearContext() { TestUtil.clearAllStaticFieldsForUnitTest(); } }