package ca.uhn.fhir.rest.server; import static org.apache.commons.lang3.StringUtils.defaultString; import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import java.net.URLEncoder; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.utils.URLEncodedUtils; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.BundleEntry; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt; import ca.uhn.fhir.model.dstu.resource.Conformance; import ca.uhn.fhir.model.dstu.resource.Conformance.RestResource; import ca.uhn.fhir.model.dstu.resource.Conformance.RestResourceSearchParam; import ca.uhn.fhir.model.dstu.resource.Location; import ca.uhn.fhir.model.dstu.resource.Observation; import ca.uhn.fhir.model.dstu.resource.Organization; import ca.uhn.fhir.model.dstu.resource.Patient; import ca.uhn.fhir.model.dstu.valueset.ResourceTypeEnum; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.OptionalParam; import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.param.ReferenceParam; import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.util.PortUtil; import ca.uhn.fhir.util.TestUtil; /** * Created by dsotnikov on 2/25/2014. */ public class ReferenceParameterTest { private static CloseableHttpClient ourClient; private static FhirContext ourCtx = FhirContext.forDstu1(); private static ReferenceParam ourLastRefParam; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ReferenceParameterTest.class); private static int ourPort; private static Server ourServer; @Before public void before() { ourLastRefParam = null; } @Test public void testParamTypesInConformanceStatement() throws Exception { HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/metadata?_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); ourLog.info(responseContent); assertEquals(200, status.getStatusLine().getStatusCode()); Conformance conf = ourCtx.newXmlParser().parseResource(Conformance.class, responseContent); RestResource res = conf.getRestFirstRep().getResource().get(2); assertEquals("Patient", res.getType().getValue()); RestResourceSearchParam param = res.getSearchParamFirstRep(); assertEquals(Patient.SP_PROVIDER, param.getName().getValue()); assertEquals(1, param.getTarget().size()); assertEquals(ResourceTypeEnum.ORGANIZATION, param.getTarget().get(0).getValueAsEnum()); } @Test public void testReadReturnVersionedReferenceInResponse() throws Exception { HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/22"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); assertEquals(200, status.getStatusLine().getStatusCode()); Patient p = ourCtx.newXmlParser().parseResource(Patient.class, responseContent); assertThat(status.getFirstHeader("Content-Location").getValue(), containsString("Patient/22/_history/33")); assertEquals("44", p.getManagingOrganization().getReference().getIdPart()); assertEquals(null, p.getManagingOrganization().getReference().getVersionIdPart()); } @Test public void testReferenceParamViewToken() throws Exception { HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?provider.name=" + URLEncoder.encode("foo|bar", "UTF-8")); HttpResponse status = ourClient.execute(httpGet); IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); assertEquals(200, status.getStatusLine().getStatusCode()); assertEquals("foo|bar", ourLastRefParam.getValue()); assertEquals("foo", ourLastRefParam.toTokenParam(ourCtx).getSystem()); assertEquals("bar", ourLastRefParam.toTokenParam(ourCtx).getValue()); } @Test public void testSearchReturnVersionedReferenceInResponse() throws Exception { HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=findPatientWithVersion"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); assertEquals(200, status.getStatusLine().getStatusCode()); List<BundleEntry> entries = ourCtx.newXmlParser().parseBundle(responseContent).getEntries(); assertEquals(2, entries.size()); Patient p = (Patient) entries.get(0).getResource(); assertEquals(0, p.getContained().getContainedResources().size()); assertEquals("22", p.getId().getIdPart()); assertEquals("33", p.getId().getVersionIdPart()); assertEquals("44", p.getManagingOrganization().getReference().getIdPart()); assertEquals(null, p.getManagingOrganization().getReference().getVersionIdPart()); } @Test public void testSearchWithMultipleParamsOfTheSameName1() throws Exception { HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Organization?partof=po123"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); assertEquals(200, status.getStatusLine().getStatusCode()); assertThat(responseContent, containsString("value=\"thePartOfId po123 null\"")); } @Test public void testSearchWithMultipleParamsOfTheSameName2() throws Exception { HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Organization?partof.name=poname"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); assertEquals(200, status.getStatusLine().getStatusCode()); assertThat(responseContent, containsString("value=\"thePartOfName poname\"")); } @Test public void testSearchWithMultipleParamsOfTheSameName3() throws Exception { HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Organization?partof=po123&partof.name=poname"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); assertEquals(200, status.getStatusLine().getStatusCode()); assertThat(responseContent, containsString("value=\"thePartOfId po123 null\"")); assertThat(responseContent, containsString("value=\"thePartOfName poname\"")); } @Test public void testSearchWithMultipleParamsOfTheSameName4() throws Exception { HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Organization?partof.fooChain=po123&partof.name=poname"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); assertEquals(200, status.getStatusLine().getStatusCode()); assertThat(responseContent, containsString("value=\"thePartOfId po123 fooChain\"")); assertThat(responseContent, containsString("value=\"thePartOfName poname\"")); } @Test public void testSearchWithMultipleParamsOfTheSameName5() throws Exception { HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Organization?partof.bar=po123"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); assertEquals(200, status.getStatusLine().getStatusCode()); assertThat(responseContent, containsString("value=\"theBarId po123 bar\"")); } @Test public void testSearchWithMultipleParamsOfTheSameName6() throws Exception { HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Organization?partof:Organization.bar=po123"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); assertEquals(200, status.getStatusLine().getStatusCode()); assertThat(responseContent, containsString("value=\"theBarId Organization/po123 bar\"")); } @Test public void testSearchWithMultipleParamsOfTheSameName7() throws Exception { HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Organization?partof:Organization=po123"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); assertEquals(200, status.getStatusLine().getStatusCode()); assertThat(responseContent, containsString("value=\"thePartOfId Organization/po123 null\"")); } @Test public void testSearchWithMultipleParamsOfTheSameName8() throws Exception { HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Location?partof=po123"); HttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); assertEquals(400, status.getStatusLine().getStatusCode()); } @Test public void testSearchWithValue() throws Exception { HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?" + Patient.SP_PROVIDER + "=123"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); assertEquals(200, status.getStatusLine().getStatusCode()); List<BundleEntry> entries = ourCtx.newXmlParser().parseBundle(responseContent).getEntries(); assertEquals(1, entries.size()); Patient p = (Patient) entries.get(0).getResource(); assertEquals("0123", p.getName().get(0).getFamilyFirstRep().getValue()); assertEquals("1", p.getName().get(1).getFamilyFirstRep().getValue()); assertEquals("2", p.getName().get(2).getFamilyFirstRep().getValue()); } @Test public void testSearchWithValueAndChain() throws Exception { { HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?" + Patient.SP_PROVIDER + ".name=123"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); assertEquals(200, status.getStatusLine().getStatusCode()); List<BundleEntry> entries = ourCtx.newXmlParser().parseBundle(responseContent).getEntries(); assertEquals(1, entries.size()); Patient p = (Patient) entries.get(0).getResource(); assertEquals("0123", p.getName().get(0).getFamilyFirstRep().getValue()); assertEquals("1", p.getName().get(1).getFamilyFirstRep().getValue()); assertEquals("2name", p.getName().get(2).getFamilyFirstRep().getValue()); } } @Test public void testSearchWithValueAndType() throws Exception { HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?" + Patient.SP_PROVIDER + ":Organization=123"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); assertEquals(200, status.getStatusLine().getStatusCode()); List<BundleEntry> entries = ourCtx.newXmlParser().parseBundle(responseContent).getEntries(); assertEquals(1, entries.size()); Patient p = (Patient) entries.get(0).getResource(); assertEquals("0123", p.getName().get(0).getFamilyFirstRep().getValue()); assertEquals("1Organization", p.getName().get(1).getFamilyFirstRep().getValue()); assertEquals("2", p.getName().get(2).getFamilyFirstRep().getValue()); } @Test public void testSearchWithValueAndTypeAndChain() throws Exception { HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?" + Patient.SP_PROVIDER + ":Organization.name=123"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); assertEquals(200, status.getStatusLine().getStatusCode()); List<BundleEntry> entries = ourCtx.newXmlParser().parseBundle(responseContent).getEntries(); assertEquals(1, entries.size()); Patient p = (Patient) entries.get(0).getResource(); assertEquals("0123", p.getName().get(0).getFamilyFirstRep().getValue()); assertEquals("1Organization", p.getName().get(1).getFamilyFirstRep().getValue()); assertEquals("2name", p.getName().get(2).getFamilyFirstRep().getValue()); } @AfterClass public static void afterClassClearContext() throws Exception { ourServer.stop(); TestUtil.clearAllStaticFieldsForUnitTest(); } @BeforeClass public static void beforeClass() throws Exception { ourPort = PortUtil.findFreePort(); ourServer = new Server(ourPort); DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); ServletHandler proxyHandler = new ServletHandler(); RestfulServer servlet = new RestfulServer(ourCtx); servlet.setBundleInclusionRule(BundleInclusionRule.BASED_ON_RESOURCE_PRESENCE); servlet.setResourceProviders(patientProvider, new DummyOrganizationResourceProvider(), new DummyLocationResourceProvider()); ServletHolder servletHolder = new ServletHolder(servlet); proxyHandler.addServletWithMapping(servletHolder, "/*"); ourServer.setHandler(proxyHandler); ourServer.start(); PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); HttpClientBuilder builder = HttpClientBuilder.create(); builder.setConnectionManager(connectionManager); ourClient = builder.build(); } public static class DummyLocationResourceProvider implements IResourceProvider { @Override public Class<? extends IResource> getResourceType() { return Location.class; } //@formatter:off @Search public List<Location> searchByNameWithDifferentChain( @OptionalParam(name = "partof", chainWhitelist= {"bar"}) ReferenceParam theBarId) { //@formatter:on ArrayList<Location> retVal = new ArrayList<Location>(); if (theBarId != null) { Location loc = new Location(); loc.setId("1"); loc.getName().setValue("theBarId " + theBarId.getValue() + " " + theBarId.getChain()); retVal.add(loc); } if (retVal.isEmpty()) { ourLog.info("No values for bar - Going to fail"); throw new InternalErrorException("No Values for bar"); } return retVal; } } public static class DummyOrganizationResourceProvider implements IResourceProvider { @Override public Class<? extends IResource> getResourceType() { return Organization.class; } /** * https://github.com/jamesagnew/hapi-fhir/issues/19 */ //@formatter:off @Search public List<Organization> searchByName( @OptionalParam(name = "partof", chainWhitelist= {"", "fooChain"}) ReferenceParam thePartOfId, @OptionalParam(name = "partof.name") StringParam thePartOfName) { //@formatter:on ArrayList<Organization> retVal = new ArrayList<Organization>(); if (thePartOfId != null) { Organization org = new Organization(); org.setId("1"); org.getName().setValue("thePartOfId " + thePartOfId.getValue() + " " + thePartOfId.getChain()); retVal.add(org); } if (thePartOfName != null) { Organization org = new Organization(); org.setId("2"); org.getName().setValue("thePartOfName " + thePartOfName.getValue()); retVal.add(org); } if (retVal.isEmpty()) { ourLog.info("No values for foo - Going to fail"); throw new InternalErrorException("No Values for foo"); } return retVal; } //@formatter:off @Search public List<Organization> searchByNameWithDifferentChain( @OptionalParam(name = "partof", chainWhitelist= {"bar"}) ReferenceParam theBarId) { //@formatter:on ArrayList<Organization> retVal = new ArrayList<Organization>(); if (theBarId != null) { Organization org = new Organization(); org.setId("1"); org.getName().setValue("theBarId " + theBarId.getValue() + " " + theBarId.getChain()); retVal.add(org); } if (retVal.isEmpty()) { ourLog.info("No values for bar - Going to fail"); throw new InternalErrorException("No Values for bar"); } return retVal; } } /** * Created by dsotnikov on 2/25/2014. */ public static class DummyPatientResourceProvider implements IResourceProvider { private Patient createPatient() { Patient p = new Patient(); p.setId("Patient/22/_history/33"); p.addIdentifier("urn:foo", "findPatientWithVersion"); Observation org = new Observation(); org.setId("Observation/44/_history/55"); p.setManagingOrganization(new ResourceReferenceDt(org)); return p; } @Search public List<Patient> findPatient(@OptionalParam(name = Patient.SP_PROVIDER, targetTypes = { Organization.class }) ReferenceParam theParam) { ourLastRefParam = theParam; ArrayList<Patient> retVal = new ArrayList<Patient>(); Patient p = new Patient(); p.setId("1"); p.addName().addFamily("0" + theParam.getValueAsQueryToken(ourCtx)); p.addName().addFamily("1" + defaultString(theParam.getResourceType())); p.addName().addFamily("2" + defaultString(theParam.getChain())); retVal.add(p); return retVal; } @Search(queryName = "findPatientWithVersion") public List<Patient> findPatientWithVersion() { ArrayList<Patient> retVal = new ArrayList<Patient>(); Patient p = createPatient(); retVal.add(p); return retVal; } @Override public Class<? extends IResource> getResourceType() { return Patient.class; } @Read public Patient read(@IdParam IdDt theId) { return createPatient(); } } }