Merge branch 'master' of https://github.com/hugosoares/hapi-fhir into hugosoares-master

This commit is contained in:
James 2017-03-16 21:32:51 -04:00
commit 9501ea0961
9 changed files with 324 additions and 6 deletions

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.rest.gclient;
import org.hl7.fhir.instance.model.api.IBaseResource;
/*
* #%L
* HAPI FHIR - Core Library
@ -19,9 +21,42 @@ package ca.uhn.fhir.rest.gclient;
* limitations under the License.
* #L%
*/
public interface IOperation extends IBaseOn<IOperationUnnamed> {
/**
* This operation is called <b><a href="https://www.hl7.org/fhir/messaging.html">$process-message</a></b> as defined by FHIR
* DSTU2.<br><br>
* Usage :<br>
* <code>
* <pre>
* Bundle response = client
* .operation()
* .onServer()
* .processMessage()
* .setResponseUrlParam("http://myserver/fhir")
* .setMessageBundle(msgBundle)
* .synchronous(Bundle.class)
* .execute();
*
* //if you want to send an async message
*
* OperationOutcome response = client
* .operation()
* .onServer()
* .processMessage()
* .setResponseUrlParam("http://myserver/fhir")
* .setMessageBundle(msgBundle)
* .asynchronous(OperationOutcome.class)
* .execute();
*
* </pre>
* </code>
*
* @see <a href="https://www.hl7.org/fhir/messaging.html">2.4 Messaging
* using FHIR Resources</a>
*
* @return An interface that defines the operation related to sending
* Messages to a Messaging Server
*/
IOperationProcessMsg processMessage();
}

View File

@ -0,0 +1,34 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package ca.uhn.fhir.rest.gclient;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseResource;
/**
*
* @author HGS
*/
public interface IOperationProcessMsg{
/**
* Set the Message Bundle to POST to the messaging server.<br>
* After this call you must choose either the method synchronous or asynchronous to set the processing mode.
*
* @param <R>
* @param theMsgBundle A Bundle of type message
* @return
*/
<R extends IBaseResource> IOperationProcessMsgMode<R> setMessageBundle(IBaseBundle theMsgBundle);
/**
* An optional query parameter indicating that responses from the receiving server should be sent to this url
*
* @param respondToUri The receiving endpoint to witch server response messages should be sent.
* @return
*/
IOperationProcessMsg setResponseUrlParam(String respondToUri);
}

View File

@ -0,0 +1,34 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package ca.uhn.fhir.rest.gclient;
import org.hl7.fhir.instance.model.api.IBaseResource;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2017 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public interface IOperationProcessMsgMode<T extends IBaseResource> extends IClientExecutable<IOperationProcessMsgMode<T>, T> {
<R extends IBaseResource> IOperationProcessMsgMode<R> asynchronous(Class<R> theResponseClass);
<R extends IBaseResource> IOperationProcessMsgMode<R> synchronous(Class<R> theResponseClass);
}

View File

@ -24,5 +24,4 @@ package ca.uhn.fhir.rest.gclient;
public interface IOperationUnnamed {
IOperationUntyped named(String theName);
}

View File

@ -321,6 +321,23 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
myDescription = theDescription;
}
public static BaseHttpClientInvocation createProcessMsgInvocation(FhirContext theContext, String theOperationName, IBaseBundle theInput, Map<String, List<String>> urlParams) {
StringBuilder b = new StringBuilder();
if (b.length() > 0) {
b.append('/');
}
if (!theOperationName.startsWith("$")) {
b.append("$");
}
b.append(theOperationName);
BaseHttpClientInvocation.appendExtraParamsWithQuestionMark(urlParams,b, b.indexOf("?") == -1);
return new HttpPostClientInvocation(theContext, theInput, b.toString());
}
public static BaseHttpClientInvocation createOperationInvocation(FhirContext theContext, String theResourceName, String theId, String theOperationName, IBaseParameters theInput, boolean theUseHttpGet) {
StringBuilder b = new StringBuilder();
if (theResourceName != null) {

View File

@ -146,6 +146,9 @@ public class Constants {
public static final String PARAM_TAGS = "_tags";
public static final String PARAM_TEXT = "_text";
public static final String PARAM_VALIDATE = "_validate";
public static final String PARAM_ASYNC = "async"; //Used in messaging
public static final String PARAM_RESPONSE_URL = "response-url"; //Used in messaging
public static final String EXTOP_PROCESS_MESSAGE = "$process-message"; //Used in messaging
public static final String PARAMQUALIFIER_MISSING = ":missing";
public static final String PARAMQUALIFIER_MISSING_FALSE = "false";
public static final String PARAMQUALIFIER_MISSING_TRUE = "true";

View File

@ -190,7 +190,7 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>${argLine} -Dfile.encoding=UTF-8 -Xmx712m</argLine>
<argLine>-Dfile.encoding=UTF-8 -Xmx712m</argLine>
</configuration>
</plugin>
<plugin>

View File

@ -0,0 +1,196 @@
package ca.uhn.fhir.rest.client;
import java.io.InputStream;
import java.io.StringReader;
import java.nio.charset.Charset;
import org.apache.commons.io.input.ReaderInputStream;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicStatusLine;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.MessageHeader;
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome;
import ca.uhn.fhir.model.dstu2.valueset.BundleTypeEnum;
import ca.uhn.fhir.model.dstu2.valueset.ResponseTypeEnum;
import ca.uhn.fhir.rest.client.apache.ApacheRestfulClientFactory;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.util.TestUtil;
import java.util.Date;
import java.util.UUID;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class MessageClientDstu2Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(MessageClientDstu2Test.class);
private FhirContext ourCtx;
private HttpClient myHttpClient;
private HttpResponse myHttpResponse;
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@Before
public void before() {
ourCtx = FhirContext.forDstu2();
myHttpClient = mock(HttpClient.class, new ReturnsDeepStubs());
ourCtx.setRestfulClientFactory(new ApacheRestfulClientFactory(ourCtx));
ourCtx.getRestfulClientFactory().setConnectionRequestTimeout(10000);
ourCtx.getRestfulClientFactory().setConnectTimeout(10000);
ourCtx.getRestfulClientFactory().setPoolMaxPerRoute(100);
ourCtx.getRestfulClientFactory().setPoolMaxTotal(100);
ourCtx.getRestfulClientFactory().setHttpClient(myHttpClient);
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
myHttpResponse = mock(HttpResponse.class, new ReturnsDeepStubs());
System.setProperty(BaseClient.HAPI_CLIENT_KEEPRESPONSES, "true");
}
@Test
public void testSendMessageAsync() throws Exception {
OperationOutcome oo = new OperationOutcome();
oo.addIssue().setDiagnostics("FOOBAR");
final String msg = ourCtx.newJsonParser().encodeResourceToString(oo);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<InputStream>() {
@Override
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.getRestfulClientFactory().newGenericClient("http://192.168.4.93:83/fhirServer");
client.setEncoding(EncodingEnum.JSON);
// Create the input message to pass to the server
final Bundle msgBundle = getMessageBundle(
"myEvent", "Test Event",
"MySource", "http://myServer/fhir/", "MyDestination", "http://myDestinationServer/fhir/");
// Invoke $process-message
OperationOutcome response = client
.operation()
.processMessage()
.setResponseUrlParam("http://myserver/fhir")
.setMessageBundle(msgBundle)
.asynchronous(OperationOutcome.class)
.execute();
//System.out.println(response);
assertEquals("http://192.168.4.93:83/fhirServer/$process-message?async=true&response-url=http%3A%2F%2Fmyserver%2Ffhir&_format=json", capt.getAllValues().get(0).getURI().toASCIIString());
assertEquals("POST", capt.getAllValues().get(0).getRequestLine().getMethod());
//assertEquals("<Parameters xmlns=\"http://hl7.org/fhir\"><parameter><name value=\"resource\"/><resource><Patient xmlns=\"http://hl7.org/fhir\"><name><given value=\"GIVEN\"/></name></Patient></resource></parameter></Parameters>", extractBody(capt, 0));
//assertNotNull(response.getOperationOutcome());
assertEquals("FOOBAR", ((OperationOutcome) response).getIssueFirstRep().getDiagnosticsElement().getValue());
}
@Test
public void testSendMessage() throws Exception {
final Bundle msgBundleResponse = getMessageBundle(
"myEvent", "Test Event",
"MySource", "http://myServer/fhir/", "MyDestination", "http://myDestinationServer/fhir/");
((MessageHeader) msgBundleResponse.getEntryFirstRep().getResource()).getResponse().setCode(ResponseTypeEnum.OK);
final String msg = ourCtx.newJsonParser().encodeResourceToString(msgBundleResponse);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<InputStream>() {
@Override
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.getRestfulClientFactory().newGenericClient("http://192.168.4.93:83/fhirServer");
client.setEncoding(EncodingEnum.JSON);
// Create the input message to pass to the server
final Bundle msgBundle = getMessageBundle(
"myEvent", "Test Event",
"MySource", "http://myServer/fhir/", "MyDestination", "http://myDestinationServer/fhir/");
// Invoke $process-message
Bundle response = client
.operation()
.processMessage()
.setMessageBundle(msgBundle)
.synchronous(Bundle.class)
.execute();
//System.out.println(response);
assertEquals("http://192.168.4.93:83/fhirServer/$process-message?async=false&_format=json", capt.getAllValues().get(0).getURI().toASCIIString());
assertEquals("POST", capt.getAllValues().get(0).getRequestLine().getMethod());
//assertEquals("<Parameters xmlns=\"http://hl7.org/fhir\"><parameter><name value=\"resource\"/><resource><Patient xmlns=\"http://hl7.org/fhir\"><name><given value=\"GIVEN\"/></name></Patient></resource></parameter></Parameters>", extractBody(capt, 0));
//assertNotNull(response.getOperationOutcome());
assertEquals("MessageHeader", ((Bundle) response).getEntryFirstRep().getResource().getResourceName());
}
/**
* Criar um FHIR Message Bundle pre-preenchido com os parametros
*
* @param eventCode
* @param eventDisplay
* @param sourceName
* @param sourceEnpoint
* @param destinationName
* @param destinationEndpoint
* @return Message Bundle
*/
public static Bundle getMessageBundle(String eventCode, String eventDisplay, String sourceName, String sourceEnpoint, String destinationName, String destinationEndpoint) {
/*
Init Bundle
*/
Bundle msgBundle = new Bundle();
msgBundle.getMeta().setLastUpdated(new Date());
msgBundle.setType(BundleTypeEnum.MESSAGE); //Document Type
msgBundle.setId(UUID.randomUUID().toString()); // Random ID
/*
Init MessageHeader
*/
MessageHeader msh = new MessageHeader();
msh.setId(UUID.randomUUID().toString());
msh.setTimestampWithMillisPrecision(new Date());
msh.getEvent().setSystem("http://mybServer/fhir/events");
msh.getEvent().setCode(eventCode);
msh.getEvent().setDisplay(eventDisplay);
msh.getSource().setName(sourceName);
msh.getSource().setEndpoint(sourceEnpoint);
msh.getDestinationFirstRep().setName(destinationName);
msh.getDestinationFirstRep().setEndpoint(destinationEndpoint);
Bundle.Entry entry = new Bundle.Entry();
entry.setFullUrl("http://mybase/fhirServer/Bundle/" + msh.getId().getValue());
entry.setResource(msh);
msgBundle.addEntry(entry);
return msgBundle;
}
}