package ca.uhn.fhir.rest.server.interceptor; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; import java.lang.reflect.Method; import java.util.*; 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.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 org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.dstu2.composite.HumanNameDt; import ca.uhn.fhir.model.dstu2.composite.IdentifierDt; import ca.uhn.fhir.model.dstu2.resource.Bundle; import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.model.dstu2.valueset.AdministrativeGenderEnum; import ca.uhn.fhir.model.dstu2.valueset.IdentifierUseEnum; import ca.uhn.fhir.model.primitive.DateDt; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.UriDt; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.annotation.RequiredParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.method.RequestDetails; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; import ca.uhn.fhir.util.PortUtil; import ca.uhn.fhir.util.TestUtil; public class InterceptorUserDataMapDstu2Test { private static CloseableHttpClient ourClient; private static FhirContext ourCtx = FhirContext.forDstu2(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(InterceptorUserDataMapDstu2Test.class); private static int ourPort; private static Server ourServer; private static RestfulServer servlet; private IServerInterceptor myInterceptor; private final Object myKey = "KEY"; private Map<Object, Object> myMap; private Set<String> myMapCheckMethods; private final Object myValue = "VALUE"; @Before public void before() { myInterceptor = mock(IServerInterceptor.class); servlet.setInterceptors(Collections.singletonList(myInterceptor)); } @Before public void beforePurgeMap() { myMap = null; myMapCheckMethods= new LinkedHashSet<String>(); } @Test public void testException() throws Exception { IServerInterceptor interceptor = mock(IServerInterceptor.class, new MyInterceptorAnswer()); servlet.setInterceptors(Collections.singletonList((IServerInterceptor) interceptor)); HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id=foo"); HttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); ourLog.info(myMapCheckMethods.toString()); assertThat(myMapCheckMethods, contains("incomingRequestPostProcessed", "incomingRequestPreHandled", "preProcessOutgoingException", "handleException")); } @Test public void testRead() throws Exception { IServerInterceptor interceptor = mock(IServerInterceptor.class, new MyInterceptorAnswer()); servlet.setInterceptors(Collections.singletonList((IServerInterceptor) interceptor)); HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1"); HttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); for (int i = 0; i < 10; i++) { if (!myMapCheckMethods.contains("processingCompletedNormally")) { Thread.sleep(100); } } ourLog.info(myMapCheckMethods.toString()); assertThat(myMapCheckMethods.toString(), myMapCheckMethods, contains("incomingRequestPostProcessed", "incomingRequestPreHandled", "outgoingResponse", "processingCompletedNormally")); } protected void updateMapUsing(Map<Object, Object> theUserData, Method theMethod) { assertNotNull(theUserData); if (myMap == null) { myMap = theUserData; myMap.put(myKey, myValue); } else { assertSame(myMap, theUserData); assertEquals(myValue, myMap.get(myKey)); } myMapCheckMethods.add(theMethod.getName()); } @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); ServletHandler proxyHandler = new ServletHandler(); servlet = new RestfulServer(ourCtx); servlet.setResourceProviders(new DummyPatientResourceProvider()); servlet.setPlainProviders(new PlainProvider()); 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 DummyPatientResourceProvider implements IResourceProvider { private Patient createPatient1() { Patient patient = new Patient(); patient.addIdentifier(); patient.getIdentifier().get(0).setUse(IdentifierUseEnum.OFFICIAL); patient.getIdentifier().get(0).setSystem(new UriDt("urn:hapitest:mrns")); patient.getIdentifier().get(0).setValue("00001"); patient.addName(); patient.getName().get(0).addFamily("Test"); patient.getName().get(0).addGiven("PatientOne"); patient.setGender(AdministrativeGenderEnum.MALE); patient.getId().setValue("1"); return patient; } public Map<String, Patient> getIdToPatient() { Map<String, Patient> idToPatient = new HashMap<String, Patient>(); { Patient patient = createPatient1(); idToPatient.put("1", patient); } { Patient patient = new Patient(); patient.getIdentifier().add(new IdentifierDt()); patient.getIdentifier().get(0).setUse(IdentifierUseEnum.OFFICIAL); patient.getIdentifier().get(0).setSystem(new UriDt("urn:hapitest:mrns")); patient.getIdentifier().get(0).setValue("00002"); patient.getName().add(new HumanNameDt()); patient.getName().get(0).addFamily("Test"); patient.getName().get(0).addGiven("PatientTwo"); patient.setGender(AdministrativeGenderEnum.FEMALE); patient.getId().setValue("2"); idToPatient.put("2", patient); } return idToPatient; } /** * Retrieve the resource by its identifier * * @param theId * The resource identity * @return The resource */ @Read() public Patient getResourceById(@IdParam IdDt theId) { if (theId.getIdPart().equals("EX")) { throw new InvalidRequestException("FOO"); } String key = theId.getIdPart(); Patient retVal = getIdToPatient().get(key); return retVal; } /** * Retrieve the resource by its identifier * * @param theId * The resource identity * @return The resource */ @Search() public List<Patient> getResourceById(@RequiredParam(name = "_id") String theId) { throw new InvalidRequestException("FOO"); } @Override public Class<Patient> getResourceType() { return Patient.class; } @Operation(name = "$everything", idempotent = true) public Bundle patientTypeOperation(@OperationParam(name = "start") DateDt theStart, @OperationParam(name = "end") DateDt theEnd) { Bundle retVal = new Bundle(); // Populate bundle with matching resources return retVal; } @Operation(name = "$everything", idempotent = true) public Bundle patientTypeOperation(@IdParam IdDt theId, @OperationParam(name = "start") DateDt theStart, @OperationParam(name = "end") DateDt theEnd) { Bundle retVal = new Bundle(); // Populate bundle with matching resources return retVal; } } private final class MyInterceptorAnswer implements Answer<Object> { @Override public Object answer(InvocationOnMock theInvocation) throws Throwable { int index = 0; for (Class<?> next : theInvocation.getMethod().getParameterTypes()) { if (RequestDetails.class.isAssignableFrom(next)) { updateMapUsing(((RequestDetails)theInvocation.getArguments()[index]).getUserData(), theInvocation.getMethod()); } if (ActionRequestDetails.class.isAssignableFrom(next)) { updateMapUsing(((ActionRequestDetails)theInvocation.getArguments()[index]).getUserData(), theInvocation.getMethod()); } index++; } if (theInvocation.getMethod().getReturnType().equals(boolean.class)) { return true; } return null; } } public static class PlainProvider { @Operation(name = "$everything", idempotent = true) public Bundle patientTypeOperation(@OperationParam(name = "start") DateDt theStart, @OperationParam(name = "end") DateDt theEnd) { Bundle retVal = new Bundle(); // Populate bundle with matching resources return retVal; } } }