package ca.uhn.fhir.jaxrs.server; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNotNull; 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.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent; import org.hl7.fhir.dstu3.model.CapabilityStatement; import org.hl7.fhir.dstu3.model.DateType; import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.Identifier; import org.hl7.fhir.dstu3.model.OperationOutcome; import org.hl7.fhir.dstu3.model.Parameters; import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.dstu3.model.Resource; import org.hl7.fhir.dstu3.model.StringType; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IBaseResource; 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.ArgumentMatcher; 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.TestJaxRsConformanceRestProviderDstu3; import ca.uhn.fhir.jaxrs.server.test.TestJaxRsMockPageProviderDstu3; import ca.uhn.fhir.jaxrs.server.test.TestJaxRsMockPatientRestProviderDstu3; import ca.uhn.fhir.model.base.resource.BaseOperationOutcome; import ca.uhn.fhir.model.primitive.UriDt; 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 AbstractJaxRsResourceProviderDstu3Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(AbstractJaxRsResourceProviderDstu3Test.class); private static IGenericClient client; private static FhirContext ourCtx = FhirContext.forDstu3(); private static final String PATIENT_NAME = "Van Houte"; private static int ourPort; private static String serverBase; private static Server jettyServer; private TestJaxRsMockPatientRestProviderDstu3 mock; private ArgumentCaptor<IdType> idCaptor; private ArgumentCaptor<String> conditionalCaptor; private ArgumentCaptor<Patient> patientCaptor; private void compareResultId(int id, IBaseResource resource) { assertEquals(id, Integer.parseInt(resource.getIdElement().getIdPart())); } @AfterClass public static void afterClassClearContext() { TestUtil.clearAllStaticFieldsForUnitTest(); } private void compareResultUrl(String url, IBaseResource resource) { assertEquals(url, resource.getIdElement().getValueAsString().substring(serverBase.length() - 1)); } private Patient createPatient(long id) { Patient theResource = new Patient(); theResource.setId(new IdType(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(IdType.class))).thenReturn(createPatient(1)); Patient result = client.read(Patient.class, "1"); compareResultId(1, result); compareResultUrl("/Patient/1", result); reset(mock); when(mock.find(eq(result.getIdElement()))).thenReturn(createPatient(1)); result = (Patient) client.read(new UriDt(result.getId())); 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); BundleEntryComponent entry = new BundleEntryComponent(); entry.setResource(patient); result.addEntry(entry); } return result; } @Before public void setUp() { this.mock = TestJaxRsMockPatientRestProviderDstu3.mock; idCaptor = ArgumentCaptor.forClass(IdType.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.getIdentifier().add(new Identifier().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().getIdentifier().get(0).getValue()); IBaseResource resource = response.getResource(); compareResultId(1, resource); } /** Conformance - Server */ @Test public void testConformance() { final CapabilityStatement conf = client.fetchConformance().ofType(CapabilityStatement.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.getIdentifier().add(new Identifier().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(); IBaseResource resource = (IBaseResource) response.getResource(); compareResultId(1, resource); assertEquals("myIdentifier", patientCaptor.getValue().getIdentifier().get(0).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 StringType("outputValue")); when(mock.someCustomOperation(any(IdType.class), argThat(new StringTypeMatcher(new StringType("myAwesomeDummyValue"))))).thenReturn(resultParameters); // Create the input parameters to pass to the server Parameters inParams = new Parameters(); inParams.addParameter().setName("start").setValue(new DateType("2001-01-01")); inParams.addParameter().setName("end").setValue(new DateType("2015-03-01")); inParams.addParameter().setName("dummy").setValue(new StringType("myAwesomeDummyValue")); //invoke Parameters outParams = client.operation().onInstance(new IdType("Patient", "1")).named("$someCustomOperation") .withParameters(inParams).execute(); //verify assertEquals("outputValue", ((StringType)outParams.getParameter().get(0).getValue()).getValueAsString()); } class StringTypeMatcher extends ArgumentMatcher<StringType> { private StringType myStringType; public StringTypeMatcher(StringType stringType) { myStringType = stringType; } @Override public boolean matches(Object argument) { return myStringType.getValue().equals(((StringType)argument).getValue()); } } @Test public void testExtendedOperationsUsingGet() { // prepare mock Parameters resultParameters = new Parameters(); resultParameters.addParameter().setName("return").setResource(createPatient(1)).setValue(new StringType("outputValue")); when(mock.someCustomOperation(any(IdType.class), argThat(new StringTypeMatcher(new StringType("myAwesomeDummyValue"))))).thenReturn(resultParameters); // Create the input parameters to pass to the server Parameters inParams = new Parameters(); inParams.addParameter().setName("start").setValue(new DateType("2001-01-01")); inParams.addParameter().setName("end").setValue(new DateType("2015-03-01")); inParams.addParameter().setName("dummy").setValue(new StringType("myAwesomeDummyValue")); // invoke Parameters outParams = client.operation().onInstance(new IdType("Patient", "1")).named("$someCustomOperation") .withParameters(inParams).useHttpGet().execute(); // verify assertEquals("outputValue", ((StringType)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(IdType.class))).thenReturn(Arrays.asList((IBaseResource) createPatient(1))); org.hl7.fhir.dstu3.model.Bundle response = client.search().forResource(Patient.class).withIdAndCompartment("1", "Condition") .returnBundle(org.hl7.fhir.dstu3.model.Bundle.class).execute(); Resource 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)); org.hl7.fhir.dstu3.model.Bundle result = client.search().forResource("Patient").usingStyle(SearchStyleEnum.POST) .returnBundle(org.hl7.fhir.dstu3.model.Bundle.class).execute(); Resource 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 Bundle results = client.search().forResource(Patient.class) .where(Patient.NAME.matchesExactly().value(PATIENT_NAME)).returnBundle(Bundle.class).execute(); verify(mock).search(any(StringParam.class), Matchers.isNull(StringAndListParam.class)); IBaseResource resource = results.getEntry().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 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")).returnBundle(Bundle.class).execute(); IBaseResource resource = results.getEntry().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 org.hl7.fhir.dstu3.model.Bundle results = client.search().forResource(Patient.class).count(8).returnBundle(org.hl7.fhir.dstu3.model.Bundle.class) .execute(); assertEquals(results.getEntry().size(), 8); IBaseResource 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 org.hl7.fhir.dstu3.model.Bundle nextPage = client.loadPage().next(results).execute(); resource = nextPage.getEntry().get(0).getResource(); compareResultId(9, resource); compareResultUrl("/Patient/9", resource); assertNull(nextPage.getLink(org.hl7.fhir.dstu3.model.Bundle.LINK_NEXT)); } /** Search - Subsetting (_summary and _elements) */ @Test @Ignore public void testSummary() { Object response = client.search().forResource(Patient.class) .returnBundle(org.hl7.fhir.dstu3.model.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.getName().get(0).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().getIdElement().getIdPart()); assertEquals(null, patientCaptor.getValue().getIdElement().getVersionIdPart()); 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 IdType("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 Identifier().setValue("1"))); //invoke final Parameters inParams = new Parameters(); inParams.addParameter().setResource(patient); final MethodOutcome mO = client.validate().resource(patient).execute(); //verify assertNotNull(mO.getOperationOutcome()); } @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( TestJaxRsMockPatientRestProviderDstu3.class.getCanonicalName(), JaxRsExceptionInterceptor.class.getCanonicalName(), TestJaxRsConformanceRestProviderDstu3.class.getCanonicalName(), TestJaxRsMockPageProviderDstu3.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) { } } }