package ca.uhn.fhir.jaxrs.server; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Matchers.any; import static org.mockito.Matchers.argThat; import static org.mockito.Matchers.eq; import static org.mockito.Matchers.isNull; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.apache.commons.lang3.StringUtils; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.FixMethodOrder; import org.junit.Ignore; import org.junit.Test; import org.junit.runners.MethodSorters; import org.mockito.ArgumentCaptor; import org.mockito.Matchers; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jaxrs.client.JaxRsRestfulClientFactory; import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsExceptionInterceptor; import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsResponseException; import ca.uhn.fhir.jaxrs.server.test.RandomServerPortProvider; import ca.uhn.fhir.jaxrs.server.test.TestJaxRsConformanceRestProvider; import ca.uhn.fhir.jaxrs.server.test.TestJaxRsMockPageProvider; import ca.uhn.fhir.jaxrs.server.test.TestJaxRsMockPatientRestProvider; import ca.uhn.fhir.model.api.BundleEntry; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.base.resource.BaseOperationOutcome; import ca.uhn.fhir.model.dstu2.composite.IdentifierDt; import ca.uhn.fhir.model.dstu2.resource.Bundle; import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry; import ca.uhn.fhir.model.dstu2.resource.Conformance; import ca.uhn.fhir.model.dstu2.resource.OperationOutcome; import ca.uhn.fhir.model.dstu2.resource.Parameters; import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.model.primitive.BoundCodeDt; import ca.uhn.fhir.model.primitive.DateDt; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.UriDt; import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.PreferReturnEnum; import ca.uhn.fhir.rest.client.IGenericClient; import ca.uhn.fhir.rest.client.ServerValidationModeEnum; import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; import ca.uhn.fhir.rest.method.SearchStyleEnum; import ca.uhn.fhir.rest.param.StringAndListParam; import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.util.TestUtil; @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class AbstractJaxRsResourceProviderTest { private static IGenericClient client; private static FhirContext ourCtx = FhirContext.forDstu2(); private static final String PATIENT_NAME = "Van Houte"; private static int ourPort; private static String serverBase; private static Server jettyServer; private TestJaxRsMockPatientRestProvider mock; private ArgumentCaptor<IdDt> idCaptor; private ArgumentCaptor<String> conditionalCaptor; private ArgumentCaptor<Patient> patientCaptor; private void compareResultId(int id, IResource resource) { assertEquals(id, resource.getId().getIdPartAsLong().intValue()); } private void compareResultUrl(String url, IResource resource) { assertEquals(url, resource.getId().getValueAsString().substring(serverBase.length() - 1)); } @AfterClass public static void afterClassClearContext() { TestUtil.clearAllStaticFieldsForUnitTest(); } private Patient createPatient(long id) { Patient theResource = new Patient(); theResource.setId(new IdDt(id)); return theResource; } private List<Patient> createPatients(int firstId, int lastId) { List<Patient> result = new ArrayList<Patient>(lastId - firstId); for (long i = firstId; i <= lastId; i++) { result.add(createPatient(i)); } return result; } /** Find By Id */ @Test public void findUsingGenericClientById() { when(mock.find(any(IdDt.class))).thenReturn(createPatient(1)); Patient result = client.read(Patient.class, "1"); compareResultId(1, result); compareResultUrl("/Patient/1", result); reset(mock); when(mock.find(withId(result.getId()))).thenReturn(createPatient(1)); result = (Patient) client.read(new UriDt(result.getId().getValue())); compareResultId(1, result); compareResultUrl("/Patient/1", result); } private Bundle getPatientBundle(int size) { Bundle result = new Bundle(); for (long i = 0; i < size; i++) { Patient patient = createPatient(i); Entry entry = new Entry().setResource(patient); result.addEntry(entry); } return result; } @Before public void setUp() { this.mock = TestJaxRsMockPatientRestProvider.mock; idCaptor = ArgumentCaptor.forClass(IdDt.class); patientCaptor = ArgumentCaptor.forClass(Patient.class); conditionalCaptor = ArgumentCaptor.forClass(String.class); reset(mock); } /** Conditional Creates */ @Test public void testConditionalCreate() throws Exception { Patient toCreate = createPatient(1); MethodOutcome outcome = new MethodOutcome(); toCreate.getIdentifierFirstRep().setValue("myIdentifier"); outcome.setResource(toCreate); when(mock.create(patientCaptor.capture(), eq("/Patient?_format=json&identifier=2"))).thenReturn(outcome); client.setEncoding(EncodingEnum.JSON); MethodOutcome response = client.create().resource(toCreate).conditional() .where(Patient.IDENTIFIER.exactly().identifier("2")).prefer(PreferReturnEnum.REPRESENTATION).execute(); assertEquals("myIdentifier", patientCaptor.getValue().getIdentifierFirstRep().getValue()); IResource resource = (IResource) response.getResource(); compareResultId(1, resource); } /** Conformance - Server */ @Test public void testConformance() { final Conformance conf = client.fetchConformance().ofType(Conformance.class).execute(); assertEquals(conf.getRest().get(0).getResource().get(0).getType().toString(), "Patient"); } @Test public void testCreatePatient() throws Exception { Patient toCreate = createPatient(1); MethodOutcome outcome = new MethodOutcome(); toCreate.getIdentifierFirstRep().setValue("myIdentifier"); outcome.setResource(toCreate); when(mock.create(patientCaptor.capture(), isNull(String.class))).thenReturn(outcome); client.setEncoding(EncodingEnum.JSON); final MethodOutcome response = client.create().resource(toCreate).prefer(PreferReturnEnum.REPRESENTATION) .execute(); IResource resource = (IResource) response.getResource(); compareResultId(1, resource); assertEquals("myIdentifier", patientCaptor.getValue().getIdentifierFirstRep().getValue()); } @Test public void testDeletePatient() { when(mock.delete(idCaptor.capture(), conditionalCaptor.capture())).thenReturn(new MethodOutcome()); final IBaseOperationOutcome results = client.delete().resourceById("Patient", "1").execute(); assertEquals("1", idCaptor.getValue().getIdPart()); } @Test public void testConditionalDelete() throws Exception { when(mock.delete(idCaptor.capture(), conditionalCaptor.capture())).thenReturn(new MethodOutcome()); client.delete().resourceConditionalByType("Patient").where(Patient.IDENTIFIER.exactly().identifier("2")).execute(); assertEquals("Patient?identifier=2&_format=json", conditionalCaptor.getValue()); } /** Extended Operations */ @Test public void testExtendedOperations() { // prepare mock Parameters resultParameters = new Parameters(); resultParameters.addParameter().setName("return").setResource(createPatient(1)).setValue(new StringDt("outputValue")); when(mock.someCustomOperation(any(IdDt.class), eq(new StringDt("myAwesomeDummyValue")))).thenReturn(resultParameters); // Create the input parameters to pass to the server Parameters inParams = new Parameters(); inParams.addParameter().setName("start").setValue(new DateDt("2001-01-01")); inParams.addParameter().setName("end").setValue(new DateDt("2015-03-01")); inParams.addParameter().setName("dummy").setValue(new StringDt("myAwesomeDummyValue")); //invoke Parameters outParams = client.operation().onInstance(new IdDt("Patient", "1")).named("$someCustomOperation") .withParameters(inParams).execute(); //verify assertEquals("outputValue", ((StringDt)outParams.getParameter().get(0).getValue()).getValueAsString()); } @Test public void testExtendedOperationsUsingGet() { // prepare mock Parameters resultParameters = new Parameters(); resultParameters.addParameter().setName("return").setResource(createPatient(1)).setValue(new StringDt("outputValue")); when(mock.someCustomOperation(any(IdDt.class), eq(new StringDt("myAwesomeDummyValue")))).thenReturn(resultParameters); // Create the input parameters to pass to the server Parameters inParams = new Parameters(); inParams.addParameter().setName("start").setValue(new DateDt("2001-01-01")); inParams.addParameter().setName("end").setValue(new DateDt("2015-03-01")); inParams.addParameter().setName("dummy").setValue(new StringDt("myAwesomeDummyValue")); // invoke Parameters outParams = client.operation().onInstance(new IdDt("Patient", "1")).named("$someCustomOperation") .withParameters(inParams).useHttpGet().execute(); // verify assertEquals("outputValue", ((StringDt)outParams.getParameter().get(0).getValue()).getValueAsString()); } /** Search using other query options */ public void testOther() { // missing } @Test public void testRead() { when(mock.find(idCaptor.capture())).thenReturn(createPatient(1)); final Patient patient = client.read(Patient.class, "1"); compareResultId(1, patient); compareResultUrl("/Patient/1", patient); assertEquals("1", idCaptor.getValue().getIdPart()); } /** Search - Compartments */ @Test public void testSearchCompartements() { when(mock.searchCompartment(any(IdDt.class))).thenReturn(Arrays.asList((IResource) createPatient(1))); Bundle response = client.search().forResource(Patient.class).withIdAndCompartment("1", "Condition") .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class).execute(); IResource resource = response.getEntry().get(0).getResource(); compareResultId(1, resource); compareResultUrl("/Patient/1", resource); } /** */ @Test public void testSearchPost() { when(mock.search(any(StringParam.class), Matchers.isNull(StringAndListParam.class))) .thenReturn(createPatients(1, 13)); Bundle result = client.search().forResource("Patient").usingStyle(SearchStyleEnum.POST) .returnBundle(Bundle.class).execute(); IResource resource = result.getEntry().get(0).getResource(); compareResultId(1, resource); compareResultUrl("/Patient/1", resource); } /** Search/Query - Type */ @Test public void testSearchUsingGenericClientBySearch() { // Perform a search when(mock.search(any(StringParam.class), Matchers.isNull(StringAndListParam.class))) .thenReturn(Arrays.asList(createPatient(1))); final ca.uhn.fhir.model.api.Bundle results = client.search().forResource(Patient.class) .where(Patient.NAME.matchesExactly().value(PATIENT_NAME)).execute(); verify(mock).search(any(StringParam.class), Matchers.isNull(StringAndListParam.class)); IResource resource = results.getEntries().get(0).getResource(); compareResultId(1, resource); compareResultUrl("/Patient/1", resource); } /** Search - Multi-valued Parameters (ANY/OR) */ @Test public void testSearchUsingGenericClientBySearchWithMultiValues() { when(mock.search(any(StringParam.class), Matchers.isNotNull(StringAndListParam.class))) .thenReturn(Arrays.asList(createPatient(1))); final ca.uhn.fhir.model.api.Bundle results = client.search().forResource(Patient.class) .where(Patient.ADDRESS.matches().values("Toronto")).and(Patient.ADDRESS.matches().values("Ontario")) .and(Patient.ADDRESS.matches().values("Canada")) .where(Patient.IDENTIFIER.exactly().systemAndIdentifier("SHORTNAME", "TOYS")).execute(); IResource resource = results.getEntries().get(0).getResource(); compareResultId(1, resource); compareResultUrl("/Patient/1", resource); } /** Search - Paging */ @Test public void testSearchWithPaging() { // Perform a search when(mock.search(any(StringParam.class), Matchers.isNull(StringAndListParam.class))) .thenReturn(createPatients(1, 13)); final Bundle results = client.search().forResource(Patient.class).limitTo(8).returnBundle(Bundle.class) .execute(); assertEquals(results.getEntry().size(), 8); IResource resource = results.getEntry().get(0).getResource(); compareResultId(1, resource); compareResultUrl("/Patient/1", resource); compareResultId(8, results.getEntry().get(7).getResource()); // ourLog.info("Next: " + results.getLink("next").getUrl()); // String url = results.getLink("next").getUrl().replace("?", "Patient?"); // results.getLink("next").setUrl(url); // ourLog.info("New Next: " + results.getLink("next").getUrl()); // load next page final Bundle nextPage = client.loadPage().next(results).execute(); resource = nextPage.getEntry().get(0).getResource(); compareResultId(9, resource); compareResultUrl("/Patient/9", resource); assertNull(nextPage.getLink(Bundle.LINK_NEXT)); } /** Search - Subsetting (_summary and _elements) */ @Test @Ignore public void testSummary() { Object response = client.search().forResource(Patient.class) .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class).execute(); } /** Transaction - Server */ @Ignore @Test public void testTransaction() { ca.uhn.fhir.model.api.Bundle bundle = new ca.uhn.fhir.model.api.Bundle(); BundleEntry entry = bundle.addEntry(); final Patient existing = new Patient(); existing.getNameFirstRep().addFamily("Created with bundle"); entry.setResource(existing); BoundCodeDt<BundleEntryTransactionMethodEnum> theTransactionOperation = new BoundCodeDt( BundleEntryTransactionMethodEnum.VALUESET_BINDER, BundleEntryTransactionMethodEnum.POST); entry.setTransactionMethod(theTransactionOperation); ca.uhn.fhir.model.api.Bundle response = client.transaction().withBundle(bundle).execute(); } @Test public void testUpdateById() throws Exception { when(mock.update(idCaptor.capture(), patientCaptor.capture(), conditionalCaptor.capture())).thenReturn(new MethodOutcome()); client.update("1", createPatient(1)); assertEquals("1", idCaptor.getValue().getIdPart()); compareResultId(1, patientCaptor.getValue()); } @Test public void testConditionalUpdate() throws Exception { when(mock.update(idCaptor.capture(), patientCaptor.capture(), conditionalCaptor.capture())).thenReturn(new MethodOutcome()); client.update().resource(createPatient(1)).conditional().where(Patient.IDENTIFIER.exactly().identifier("2")).execute(); assertEquals(null, patientCaptor.getValue().getId().getIdPart()); assertEquals("Patient?identifier=2&_format=json", conditionalCaptor.getValue()); } @SuppressWarnings("unchecked") @Ignore @Test public void testResourceNotFound() throws Exception { when(mock.update(idCaptor.capture(), patientCaptor.capture(), conditionalCaptor.capture())).thenThrow(ResourceNotFoundException.class); try { client.update("1", createPatient(2)); fail(); } catch (ResourceNotFoundException e) { // good } } @Test public void testVRead() { when(mock.findHistory(idCaptor.capture())).thenReturn(createPatient(1)); final Patient patient = client.vread(Patient.class, "1", "2"); compareResultId(1, patient); compareResultUrl("/Patient/1", patient); assertEquals("1", idCaptor.getValue().getIdPart()); assertEquals("2", idCaptor.getValue().getVersionIdPart()); } @Test public void testXFindUnknownPatient() { try { JaxRsResponseException notFoundException = new JaxRsResponseException(new ResourceNotFoundException(new IdDt("999955541264"))); when(mock.find(idCaptor.capture())).thenThrow(notFoundException); client.read(Patient.class, "999955541264"); fail(); } catch (final ResourceNotFoundException e) { assertEquals(ResourceNotFoundException.STATUS_CODE, e.getStatusCode()); assertTrue(e.getMessage().contains("999955541264")); } } @Test public void testValidate() { // prepare mock final OperationOutcome oo = new OperationOutcome(); final Patient patient = new Patient(); patient.addIdentifier((new IdentifierDt().setValue("1"))); //invoke final Parameters inParams = new Parameters(); inParams.addParameter().setResource(patient); final MethodOutcome mO = client.validate().resource(patient).execute(); //verify assertNotNull(mO.getOperationOutcome()); } private <T> T withId(final T id) { return argThat(new BaseMatcher<T>() { @Override public void describeTo(Description arg0) { } @Override public boolean matches(Object other) { IdDt thisId; IdDt otherId; if (id instanceof IdDt) { thisId = (IdDt) id; otherId = (IdDt) other; } else { thisId = ((IResource) id).getId(); otherId = ((IResource) other).getId(); } return thisId.getIdPartAsLong().equals(otherId.getIdPartAsLong()); } }); } @BeforeClass public static void setUpClass() throws Exception { ourPort = RandomServerPortProvider.findFreePort(); ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); context.setContextPath("/"); System.out.println(ourPort); jettyServer = new Server(ourPort); jettyServer.setHandler(context); ServletHolder jerseyServlet = context.addServlet(org.glassfish.jersey.servlet.ServletContainer.class, "/*"); jerseyServlet.setInitOrder(0); //@formatter:off jerseyServlet.setInitParameter("jersey.config.server.provider.classnames", StringUtils.join(Arrays.asList( TestJaxRsMockPatientRestProvider.class.getCanonicalName(), JaxRsExceptionInterceptor.class.getCanonicalName(), TestJaxRsConformanceRestProvider.class.getCanonicalName(), TestJaxRsMockPageProvider.class.getCanonicalName() ), ";")); //@formatter:on jettyServer.start(); ourCtx.setRestfulClientFactory(new JaxRsRestfulClientFactory(ourCtx)); ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); ourCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000); serverBase = "http://localhost:" + ourPort + "/"; client = ourCtx.newRestfulGenericClient(serverBase); client.setEncoding(EncodingEnum.JSON); client.registerInterceptor(new LoggingInterceptor(true)); } @AfterClass public static void tearDownClass() throws Exception { try { jettyServer.destroy(); } catch (Exception e) { } } }