diff --git a/hapi-fhir-okhttp/pom.xml b/hapi-fhir-okhttp/pom.xml new file mode 100644 index 00000000000..c2ff2001c09 --- /dev/null +++ b/hapi-fhir-okhttp/pom.xml @@ -0,0 +1,95 @@ + + + + hapi-fhir + ca.uhn.hapi.fhir + 2.0-SNAPSHOT + + 4.0.0 + + hapi-fhir-okhttp + jar + + HAPI FHIR OkHttp Client + + + + + ca.uhn.hapi.fhir + hapi-fhir-base + 2.0-SNAPSHOT + + + commons-logging + commons-logging + + + org.apache.httpcomponents + httpclient + + + org.apache.httpcomponents + httpcore + + + + + + ca.uhn.hapi.fhir + hapi-fhir-structures-dstu2 + 2.0-SNAPSHOT + + + ca.uhn.hapi.fhir + hapi-fhir-structures-dstu3 + 2.0-SNAPSHOT + + + + com.squareup.okhttp3 + okhttp + 3.4.1 + + + + + com.google.guava + guava + test + + + org.eclipse.jetty + jetty-server + test + + + org.eclipse.jetty + jetty-servlet + test + + + org.glassfish.jersey.core + jersey-server + test + + + org.glassfish.jersey.containers + jersey-container-servlet-core + test + + + org.glassfish.jersey.containers + jersey-container-jetty-http + test + + + org.glassfish.jersey.media + jersey-media-moxy + test + + + + + \ No newline at end of file diff --git a/hapi-fhir-okhttp/src/main/java/ca/uhn/fhir/okhttp/client/OkHttpRestfulClient.java b/hapi-fhir-okhttp/src/main/java/ca/uhn/fhir/okhttp/client/OkHttpRestfulClient.java new file mode 100644 index 00000000000..0d469f72527 --- /dev/null +++ b/hapi-fhir-okhttp/src/main/java/ca/uhn/fhir/okhttp/client/OkHttpRestfulClient.java @@ -0,0 +1,183 @@ +package ca.uhn.fhir.okhttp.client; + +/* + * #%L + * HAPI FHIR - Core Library + * %% + * Copyright (C) 2014 - 2016 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% + */ +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.rest.api.RequestTypeEnum; +import ca.uhn.fhir.rest.client.BaseHttpClientInvocation; +import ca.uhn.fhir.rest.client.api.Header; +import ca.uhn.fhir.rest.client.api.HttpClientUtil; +import ca.uhn.fhir.rest.client.api.IHttpClient; +import ca.uhn.fhir.rest.client.api.IHttpRequest; +import ca.uhn.fhir.rest.server.Constants; +import ca.uhn.fhir.rest.server.EncodingEnum; +import okhttp3.*; +import okhttp3.internal.Version; +import org.hl7.fhir.instance.model.api.IBaseBinary; + +import java.util.List; +import java.util.Map; + +import static ca.uhn.fhir.okhttp.utils.UrlStringUtils.*; + +/** + * A Http Request based on OkHttp. This is an adapter around the class + * {@link OkHttpClient} + * + * @author Matthew Clarke | matthew.clarke@orionhealth.com | Orion Health + */ +public class OkHttpRestfulClient implements IHttpClient { + + private OkHttpClient myClient; + private StringBuilder myUrl; + private Map> myIfNoneExistParams; + private String myIfNoneExistString; + private RequestTypeEnum myRequestType; + private List
myHeaders; + private OkHttpRestfulRequest myRequest; + + public OkHttpRestfulClient(OkHttpClient theClient, + StringBuilder theUrl, + Map> theIfNoneExistParams, + String theIfNoneExistString, + RequestTypeEnum theRequestType, + List
theHeaders) { + myClient = theClient; + myUrl = theUrl; + myIfNoneExistParams = theIfNoneExistParams; + myIfNoneExistString = theIfNoneExistString; + myRequestType = theRequestType; + myHeaders = theHeaders; + } + + @Override + public IHttpRequest createByteRequest(FhirContext theContext, String theContents, String theContentType, EncodingEnum theEncoding) { + initBaseRequest(theContext, theEncoding, createPostBody(theContents, theContentType)); + return myRequest; + } + + private void initBaseRequest(FhirContext theContext, EncodingEnum theEncoding, RequestBody body) { + String sanitisedUrl = withTrailingQuestionMarkRemoved(myUrl.toString()); + myRequest = new OkHttpRestfulRequest(myClient, sanitisedUrl, myRequestType, body); + addHeadersToRequest(myRequest, theEncoding, theContext); + } + + private RequestBody createPostBody(String theContents, String theContentType) { + return RequestBody.create(MediaType.parse(theContentType), theContents); + } + + @Override + public IHttpRequest createParamRequest(FhirContext theContext, Map> theParams, EncodingEnum theEncoding) { + initBaseRequest(theContext, theEncoding, getFormBodyFromParams(theParams)); + return myRequest; + } + + private RequestBody getFormBodyFromParams(Map> queryParams) { + FormBody.Builder formBuilder = new FormBody.Builder(); + for (Map.Entry> paramEntry : queryParams.entrySet()) { + for (String value : paramEntry.getValue()) { + formBuilder.add(paramEntry.getKey(), value); + } + } + + return formBuilder.build(); + } + + @Override + public IHttpRequest createBinaryRequest(FhirContext theContext, IBaseBinary theBinary) { + initBaseRequest(theContext, null, createPostBody(theBinary.getContent(), theBinary.getContentType())); + return myRequest; + } + + private RequestBody createPostBody(byte[] theContents, String theContentType) { + return RequestBody.create(MediaType.parse(theContentType), theContents); + } + + @Override + public IHttpRequest createGetRequest(FhirContext theContext, EncodingEnum theEncoding) { + initBaseRequest(theContext, theEncoding, null); + return myRequest; + } + + private void addHeadersToRequest(OkHttpRestfulRequest theHttpRequest, EncodingEnum theEncoding, FhirContext theContext) { + if (myHeaders != null) { + for (Header next : myHeaders) { + theHttpRequest.addHeader(next.getName(), next.getValue()); + } + } + + addUserAgentHeader(theHttpRequest, theContext); + addAcceptCharsetHeader(theHttpRequest); + addAcceptHeader(theHttpRequest, theEncoding); + addIfNoneExistHeader(theHttpRequest); + } + + private void addUserAgentHeader(OkHttpRestfulRequest theHttpRequest, FhirContext theContext) { + theHttpRequest.addHeader("User-Agent", HttpClientUtil.createUserAgentString(theContext, Version.userAgent())); + } + + private void addAcceptCharsetHeader(OkHttpRestfulRequest theHttpRequest) { + theHttpRequest.addHeader("Accept-Charset", "utf-8"); + } + + private void addAcceptHeader(OkHttpRestfulRequest theHttpRequest, EncodingEnum theEncoding) { + Request.Builder builder = theHttpRequest.getRequest(); + + if (theEncoding == null) { + builder.addHeader(Constants.HEADER_ACCEPT, Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY); + } else if (theEncoding == EncodingEnum.JSON) { + builder.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_JSON); + } else if (theEncoding == EncodingEnum.XML) { + builder.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_XML); + } + } + + private void addIfNoneExistHeader(IHttpRequest result) { + if (myIfNoneExistParams != null) { + addIfNoneExistHeaderFromParams(result, myIfNoneExistParams); + } else if (myIfNoneExistString != null) { + addIfNoneExistHeaderFromString(result, myIfNoneExistString); + } + } + + private void addIfNoneExistHeaderFromString(IHttpRequest result, String ifNoneExistString) { + StringBuilder sb = newHeaderBuilder(myUrl); + boolean shouldAddQuestionMark = !hasQuestionMark(sb); + sb.append(shouldAddQuestionMark ? '?' : '&'); + sb.append(everythingAfterFirstQuestionMark(ifNoneExistString)); + result.addHeader(Constants.HEADER_IF_NONE_EXIST, sb.toString()); + } + + private void addIfNoneExistHeaderFromParams(IHttpRequest result, Map> ifNoneExistParams) { + StringBuilder sb = newHeaderBuilder(myUrl); + boolean shouldAddInitialQuestionMark = !hasQuestionMark(sb); + BaseHttpClientInvocation.appendExtraParamsWithQuestionMark(ifNoneExistParams, sb, shouldAddInitialQuestionMark); + result.addHeader(Constants.HEADER_IF_NONE_EXIST, sb.toString()); + } + + public static StringBuilder newHeaderBuilder(StringBuilder baseUrl) { + StringBuilder sb = new StringBuilder(baseUrl); + if (endsWith(baseUrl, '/')) { + deleteLastCharacter(sb); + } + return sb; + } + +} diff --git a/hapi-fhir-okhttp/src/main/java/ca/uhn/fhir/okhttp/client/OkHttpRestfulClientFactory.java b/hapi-fhir-okhttp/src/main/java/ca/uhn/fhir/okhttp/client/OkHttpRestfulClientFactory.java new file mode 100644 index 00000000000..4ed2168c79c --- /dev/null +++ b/hapi-fhir-okhttp/src/main/java/ca/uhn/fhir/okhttp/client/OkHttpRestfulClientFactory.java @@ -0,0 +1,96 @@ +package ca.uhn.fhir.okhttp.client; + +/* + * #%L + * HAPI FHIR - Core Library + * %% + * Copyright (C) 2014 - 2016 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% + */ + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.rest.api.RequestTypeEnum; +import ca.uhn.fhir.rest.client.RestfulClientFactory; +import ca.uhn.fhir.rest.client.api.Header; +import ca.uhn.fhir.rest.client.api.IHttpClient; +import okhttp3.OkHttpClient; + +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.util.List; +import java.util.Map; + +/** + * A Restful client factory based on OkHttp. + * + * @author Matthew Clarke | matthew.clarke@orionhealth.com | Orion Health + */ +public class OkHttpRestfulClientFactory extends RestfulClientFactory { + + private OkHttpClient myNativeClient; + + public OkHttpRestfulClientFactory() { + super(); + } + + public OkHttpRestfulClientFactory(FhirContext theFhirContext) { + super(theFhirContext); + } + + @Override + protected IHttpClient getHttpClient(String theServerBase) { + return new OkHttpRestfulClient(getNativeClient(), new StringBuilder(theServerBase), null, null, null, null); + } + + @Override + protected void resetHttpClient() { + myNativeClient = null; + } + + public synchronized OkHttpClient getNativeClient() { + if (myNativeClient == null) { + myNativeClient = new OkHttpClient(); + } + + return myNativeClient; + } + + @Override + public IHttpClient getHttpClient(StringBuilder theUrl, + Map> theIfNoneExistParams, + String theIfNoneExistString, + RequestTypeEnum theRequestType, + List
theHeaders) { + return new OkHttpRestfulClient(getNativeClient(), theUrl, theIfNoneExistParams, theIfNoneExistString, theRequestType, theHeaders); + } + + /** + * Only accepts clients of type {@link OkHttpClient} + * + * @param okHttpClient + */ + @Override + public void setHttpClient(Object okHttpClient) { + myNativeClient = (OkHttpClient) okHttpClient; + } + + @Override + public void setProxy(String theHost, Integer thePort) { + Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(theHost, thePort)); + OkHttpClient.Builder builder = getNativeClient().newBuilder().proxy(proxy); + setHttpClient(builder.build()); + } + +} diff --git a/hapi-fhir-okhttp/src/main/java/ca/uhn/fhir/okhttp/client/OkHttpRestfulRequest.java b/hapi-fhir-okhttp/src/main/java/ca/uhn/fhir/okhttp/client/OkHttpRestfulRequest.java new file mode 100644 index 00000000000..6d3bb20eae7 --- /dev/null +++ b/hapi-fhir-okhttp/src/main/java/ca/uhn/fhir/okhttp/client/OkHttpRestfulRequest.java @@ -0,0 +1,94 @@ +package ca.uhn.fhir.okhttp.client; + +/* + * #%L + * HAPI FHIR - Core Library + * %% + * Copyright (C) 2014 - 2016 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% + */ + +import ca.uhn.fhir.rest.api.RequestTypeEnum; +import ca.uhn.fhir.rest.client.api.IHttpRequest; +import ca.uhn.fhir.rest.client.api.IHttpResponse; +import okhttp3.Call; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +/** + * Adapter for building an OkHttp-specific request. + * + * @author Matthew Clarke | matthew.clarke@orionhealth.com | Orion Health + */ +public class OkHttpRestfulRequest implements IHttpRequest { + + private final Request.Builder myRequestBuilder; + private OkHttpClient myClient; + private String myUrl; + private RequestTypeEnum myRequestTypeEnum; + private RequestBody myRequestBody; + + public OkHttpRestfulRequest(OkHttpClient theClient, String theUrl, RequestTypeEnum theRequestTypeEnum, RequestBody theRequestBody) { + myClient = theClient; + myUrl = theUrl; + myRequestTypeEnum = theRequestTypeEnum; + myRequestBody = theRequestBody; + + myRequestBuilder = new Request.Builder().url(theUrl); + } + + public Request.Builder getRequest() { + return myRequestBuilder; + } + + @Override + public void addHeader(String theName, String theValue) { + myRequestBuilder.addHeader(theName, theValue); + } + + @Override + public IHttpResponse execute() throws IOException { + myRequestBuilder.method(getHttpVerbName(), myRequestBody); + Call call = myClient.newCall(myRequestBuilder.build()); + return new OkHttpRestfulResponse(call.execute()); + } + + @Override + public Map> getAllHeaders() { + return myRequestBuilder.build().headers().toMultimap(); + } + + @Override + public String getRequestBodyFromStream() throws IOException { + // returning null to indicate this is not supported, as documented in IHttpRequest's contract + return null; + } + + @Override + public String getUri() { + return myUrl; + } + + @Override + public String getHttpVerbName() { + return myRequestTypeEnum.name(); + } + +} diff --git a/hapi-fhir-okhttp/src/main/java/ca/uhn/fhir/okhttp/client/OkHttpRestfulResponse.java b/hapi-fhir-okhttp/src/main/java/ca/uhn/fhir/okhttp/client/OkHttpRestfulResponse.java new file mode 100644 index 00000000000..aba0e540b6a --- /dev/null +++ b/hapi-fhir-okhttp/src/main/java/ca/uhn/fhir/okhttp/client/OkHttpRestfulResponse.java @@ -0,0 +1,129 @@ +package ca.uhn.fhir.okhttp.client; + +/* + * #%L + * HAPI FHIR - Core Library + * %% + * Copyright (C) 2014 - 2016 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% + */ + +import ca.uhn.fhir.rest.client.api.IHttpResponse; +import ca.uhn.fhir.rest.server.Constants; +import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import okhttp3.MediaType; +import okhttp3.Response; +import org.apache.commons.io.IOUtils; + +import java.io.*; +import java.util.List; +import java.util.Map; + +/** + * Wraps an OkHttp {@link Response} + * + * @author Matthew Clarke | matthew.clarke@orionhealth.com | Orion Health + */ +public class OkHttpRestfulResponse implements IHttpResponse { + + private Response myResponse; + private boolean myEntityBuffered = false; + private byte[] myEntityBytes; + + public OkHttpRestfulResponse(Response theResponse) { + this.myResponse = theResponse; + } + + @Override + public int getStatus() { + return myResponse.code(); + } + + @Override + public Object getResponse() { + return myResponse; + } + + @Override + public String getMimeType() { + String contentType = myResponse.header(Constants.HEADER_CONTENT_TYPE); + if (contentType == null) { + return null; + } + + MediaType mediaType = MediaType.parse(contentType); + if (mediaType == null) { + return null; + } + + return typeAndSubtypeOnly(mediaType).toString(); + } + + private MediaType typeAndSubtypeOnly(MediaType input) { + return MediaType.parse(input.type() + "/" + input.subtype()); + } + + @Override + public Map> getAllHeaders() { + return myResponse.headers().toMultimap(); + } + + @Override + public String getStatusInfo() { + return myResponse.message(); + } + + @Override + public Reader createReader() throws IOException { + if (!myEntityBuffered && myResponse.body() == null) { + return new StringReader(""); + } else { + return new InputStreamReader(readEntity()); + } + } + + @Override + public InputStream readEntity() throws IOException { + if (this.myEntityBuffered) { + return new ByteArrayInputStream(myEntityBytes); + } else if (myResponse.body() != null) { + return myResponse.body().byteStream(); + } else { + return null; + } + } + + @Override + public void close() { + myResponse.close(); + } + + @Override + public void bufferEntitity() throws IOException { + if (myEntityBuffered) { + return; + } + InputStream responseEntity = readEntity(); + if (responseEntity != null) { + myEntityBuffered = true; + try { + myEntityBytes = IOUtils.toByteArray(responseEntity); + } catch (IllegalStateException e) { + throw new InternalErrorException(e); + } + } + } + +} diff --git a/hapi-fhir-okhttp/src/main/java/ca/uhn/fhir/okhttp/utils/UrlStringUtils.java b/hapi-fhir-okhttp/src/main/java/ca/uhn/fhir/okhttp/utils/UrlStringUtils.java new file mode 100644 index 00000000000..4c96595345a --- /dev/null +++ b/hapi-fhir-okhttp/src/main/java/ca/uhn/fhir/okhttp/utils/UrlStringUtils.java @@ -0,0 +1,44 @@ +package ca.uhn.fhir.okhttp.utils; + +/* + * #%L + * HAPI FHIR - Core Library + * %% + * Copyright (C) 2014 - 2016 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 class UrlStringUtils { + + public static String withTrailingQuestionMarkRemoved(String input) { + return input.replaceAll("\\?$", ""); + } + + public static String everythingAfterFirstQuestionMark(String input) { + return input.substring(input.indexOf('?') + 1); + } + + public static boolean hasQuestionMark(StringBuilder sb) { + return sb.indexOf("?") != -1; + } + + public static void deleteLastCharacter(StringBuilder sb) { + sb.deleteCharAt(sb.length() - 1); + } + + public static boolean endsWith(StringBuilder sb, char c) { + return sb.length() > 0 && sb.charAt(sb.length() - 1) == c; + } + +} \ No newline at end of file diff --git a/hapi-fhir-okhttp/src/test/java/ca/uhn/fhir/okhttp/GenericOkHttpClientDstu2Test.java b/hapi-fhir-okhttp/src/test/java/ca/uhn/fhir/okhttp/GenericOkHttpClientDstu2Test.java new file mode 100644 index 00000000000..4bae2da6552 --- /dev/null +++ b/hapi-fhir-okhttp/src/test/java/ca/uhn/fhir/okhttp/GenericOkHttpClientDstu2Test.java @@ -0,0 +1,1955 @@ +package ca.uhn.fhir.okhttp; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.model.api.Bundle; +import ca.uhn.fhir.model.api.Include; +import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; +import ca.uhn.fhir.model.dstu2.composite.IdentifierDt; +import ca.uhn.fhir.model.dstu2.composite.MetaDt; +import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry; +import ca.uhn.fhir.model.dstu2.resource.Bundle.Link; +import ca.uhn.fhir.model.dstu2.resource.*; +import ca.uhn.fhir.model.primitive.*; +import ca.uhn.fhir.okhttp.client.OkHttpRestfulClientFactory; +import ca.uhn.fhir.parser.IParser; +import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.rest.api.PreferReturnEnum; +import ca.uhn.fhir.rest.api.SummaryEnum; +import ca.uhn.fhir.rest.client.IGenericClient; +import ca.uhn.fhir.rest.client.RestfulClientFactory; +import ca.uhn.fhir.rest.client.ServerValidationModeEnum; +import ca.uhn.fhir.rest.client.api.Header; +import ca.uhn.fhir.rest.client.exceptions.InvalidResponseException; +import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; +import ca.uhn.fhir.rest.method.SearchStyleEnum; +import ca.uhn.fhir.rest.param.DateRangeParam; +import ca.uhn.fhir.rest.server.Constants; +import ca.uhn.fhir.rest.server.EncodingEnum; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import org.apache.commons.io.Charsets; +import org.apache.commons.io.IOUtils; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.AbstractHandler; +import org.hl7.fhir.instance.model.api.IBase; +import org.hl7.fhir.instance.model.api.IBaseBundle; +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.Test; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.*; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +public class GenericOkHttpClientDstu2Test { + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(GenericOkHttpClientDstu2Test.class); + private static FhirContext ourCtx; + private static int ourPort; + private static Server ourServer; + private static int ourResponseCount = 0; + private static String[] ourResponseBodies; + private static String ourResponseBody; + private static String ourResponseContentType; + private static int ourResponseStatus; + private static String ourRequestUri; + private static List ourRequestUriAll; + private static String ourRequestMethod; + private static String ourRequestContentType; + private static byte[] ourRequestBodyBytes; + private static String ourRequestBodyString; + private static ArrayListMultimap ourRequestHeaders; + private static List> ourRequestHeadersAll; + private static Map ourRequestFirstHeaders; + + @BeforeClass + public static void beforeClass() throws Exception { + ourCtx = FhirContext.forDstu2(); + + ourPort = RandomServerPortProvider.findFreePort(); + ourServer = new Server(ourPort); + ourLog.info("Starting server on port {}", ourPort); + ourServer.setHandler(new AbstractHandler() { + + @Override + public void handle(String theArg0, Request theRequest, HttpServletRequest theServletRequest, HttpServletResponse theResp) throws IOException, ServletException { + theRequest.setHandled(true); + ourRequestUri = "http:" + theRequest.getHttpURI().toString(); + ourRequestUriAll.add(ourRequestUri); + ourRequestMethod = theRequest.getMethod(); + ourRequestContentType = theServletRequest.getContentType(); + ourRequestBodyBytes = IOUtils.toByteArray(theServletRequest.getInputStream()); + ourRequestBodyString = new String(ourRequestBodyBytes, Charsets.UTF_8); + + ourRequestHeaders = ArrayListMultimap.create(); + ourRequestHeadersAll.add(ourRequestHeaders); + ourRequestFirstHeaders = Maps.newHashMap(); + + for (Enumeration headerNameEnum = theRequest.getHeaderNames(); headerNameEnum.hasMoreElements(); ) { + String nextName = headerNameEnum.nextElement(); + for (Enumeration headerValueEnum = theRequest.getHeaders(nextName); headerValueEnum.hasMoreElements(); ) { + String nextValue = headerValueEnum.nextElement(); + if (ourRequestFirstHeaders.containsKey(nextName) == false) { + ourRequestFirstHeaders.put(nextName, new Header(nextName, nextValue)); + } + ourRequestHeaders.put(nextName, new Header(nextName, nextValue)); + } + } + + theResp.setStatus(ourResponseStatus); + + if (ourResponseBody != null) { + theResp.setContentType(ourResponseContentType); + theResp.getWriter().write(ourResponseBody); + } else if (ourResponseBodies != null) { + theResp.setContentType(ourResponseContentType); + theResp.getWriter().write(ourResponseBodies[ourResponseCount]); + } + + ourResponseCount++; + } + }); + + ourServer.start(); + } + + @AfterClass + public static void afterClass() throws Exception { + ourServer.stop(); + } + + /** + * This suite of tests can be reconfigured to test a different RestfulClientFactory implementation by + * changing the instance returned here. + * + * @param context FhirContext or null + * @return RestfulClientFactory implementation to run tests against + */ + private RestfulClientFactory createNewClientFactoryForTesting(FhirContext context) { + if (context == null) { + return new OkHttpRestfulClientFactory(); + } else { + return new OkHttpRestfulClientFactory(ourCtx); + } + } + + @Before + public void before() { + RestfulClientFactory clientFactory = createNewClientFactoryForTesting(ourCtx); + clientFactory.setServerValidationMode(ServerValidationModeEnum.NEVER); + + ourCtx.setRestfulClientFactory(clientFactory); + ourResponseCount = 0; + } + + @Test + public void testProviderWhereWeForgotToSetTheContext() throws Exception { + RestfulClientFactory clientFactory = createNewClientFactoryForTesting(null); + clientFactory.setServerValidationMode(ServerValidationModeEnum.NEVER); + + ourCtx.setRestfulClientFactory(clientFactory); + + try { + ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + fail(); + } catch (IllegalStateException e) { + String factoryClassName = clientFactory.getClass().getSimpleName(); + assertEquals(factoryClassName + " does not have FhirContext defined. This must be set via " + factoryClassName + "#setFhirContext(FhirContext)", e.getMessage()); + } + } + + private String getPatientFeedWithOneResult() { + //@formatter:off + String msg = "\n" + + "d039f91a-cc3c-4013-988e-af4d8d0614bd\n" + + "\n" + + "" + + "" + + "
John Cardinal: 444333333
" + + "" + + "" + + "" + + "" + + "
" + + "
" + + "
\n" + + "
\n" + + "
"; + //@formatter:on + return msg; + } + + @Test + public void testAcceptHeaderFetchConformance() throws Exception { + IParser p = ourCtx.newXmlParser(); + + Conformance conf = new Conformance(); + conf.setCopyright("COPY"); + + final String respString = p.encodeResourceToString(conf); + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = respString; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + client.fetchConformance().ofType(Conformance.class).execute(); + assertEquals("http://localhost:" + ourPort + "/fhir/metadata", ourRequestUri); + assertEquals(1, ourRequestHeaders.get("Accept").size()); + assertThat(ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY)); + + client.fetchConformance().ofType(Conformance.class).encodedJson().execute(); + assertEquals("http://localhost:" + ourPort + "/fhir/metadata?_format=json", ourRequestUri); + assertEquals(1, ourRequestHeaders.get("Accept").size()); + assertThat(ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + + client.fetchConformance().ofType(Conformance.class).encodedXml().execute(); + assertEquals("http://localhost:" + ourPort + "/fhir/metadata?_format=xml", ourRequestUri); + assertEquals(1, ourRequestHeaders.get("Accept").size()); + assertThat(ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); + } + + @Test + public void testAcceptHeaderPreflightConformance() throws Exception { + final IParser p = ourCtx.newXmlParser(); + + final Conformance conf = new Conformance(); + conf.setCopyright("COPY"); + + final Patient patient = new Patient(); + patient.addName().addFamily("FAMILY"); + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBodies = new String[]{p.encodeResourceToString(conf), p.encodeResourceToString(patient)}; + + ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE); + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + Patient resp = client.read(Patient.class, new IdDt("123")); + assertEquals("FAMILY", resp.getName().get(0).getFamily().get(0).getValue()); + assertEquals("http://localhost:" + ourPort + "/fhir/metadata", ourRequestUriAll.get(0)); + assertEquals(1, ourRequestHeadersAll.get(0).get("Accept").size()); + assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY)); + assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); + assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUriAll.get(1)); + assertEquals(1, ourRequestHeadersAll.get(1).get("Accept").size()); + assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY)); + assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); + assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + } + + @Test + public void testAcceptHeaderPreflightConformancePreferJson() throws Exception { + final IParser p = ourCtx.newXmlParser(); + + final Conformance conf = new Conformance(); + conf.setCopyright("COPY"); + + final Patient patient = new Patient(); + patient.addName().addFamily("FAMILY"); + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBodies = new String[]{p.encodeResourceToString(conf), p.encodeResourceToString(patient)}; + + ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE); + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + client.setEncoding(EncodingEnum.JSON); + + Patient resp = client.read(Patient.class, new IdDt("123")); + assertEquals("FAMILY", resp.getName().get(0).getFamily().get(0).getValue()); + assertEquals("http://localhost:" + ourPort + "/fhir/metadata?_format=json", ourRequestUriAll.get(0)); + assertEquals(1, ourRequestHeadersAll.get(0).get("Accept").size()); + assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), not(containsString(Constants.CT_FHIR_XML))); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123?_format=json", ourRequestUriAll.get(1)); + assertEquals(1, ourRequestHeadersAll.get(1).get("Accept").size()); + assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), not(containsString(Constants.CT_FHIR_XML))); + } + + @Test + @SuppressWarnings("deprecation") + public void testConformance() throws Exception { + IParser p = ourCtx.newXmlParser(); + + Conformance conf = new Conformance(); + conf.setCopyright("COPY"); + + final String respString = p.encodeResourceToString(conf); + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = respString; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + //@formatter:off + Conformance resp = (Conformance) client.conformance(); + + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/metadata", ourRequestUri); + assertEquals("COPY", resp.getCopyright()); + assertEquals("GET", ourRequestMethod); + } + + @Test + public void testCreate() throws Exception { + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + Patient p = new Patient(); + p.addName().addFamily("FOOFAMILY"); + + client.create().resource(p).execute(); + + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); + assertThat(ourRequestBodyString, containsString("")); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); + assertEquals("POST", ourRequestMethod); + + p.setId("123"); + + client.create().resource(p).execute(); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); + String body = ourRequestBodyString; + assertThat(body, containsString("")); + assertThat(body, not(containsString("123"))); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); + assertEquals("POST", ourRequestMethod); + } + + @Test + public void testCreateConditional() throws Exception { + ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + Patient p = new Patient(); + p.addName().addFamily("FOOFAMILY"); + + client.create().resource(p).conditionalByUrl("Patient?name=foo").execute(); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + String expectedContentTypeHeader = EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8; + assertContentTypeEquals(expectedContentTypeHeader); + assertThat(ourRequestBodyString, containsString("")); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); + assertEquals("POST", ourRequestMethod); + + client.create().resource(p).conditionalByUrl("Patient?name=http://foo|bar").execute(); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertContentTypeEquals(expectedContentTypeHeader); + assertThat(ourRequestBodyString, containsString("")); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=http%3A//foo%7Cbar", ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); + assertEquals("POST", ourRequestMethod); + + client.create().resource(p).conditional().where(Patient.NAME.matches().value("foo")).execute(); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertContentTypeEquals(expectedContentTypeHeader); + assertThat(ourRequestBodyString, containsString("")); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); + assertEquals("POST", ourRequestMethod); + } + + private void assertContentTypeEquals(String expectedContentTypeHeader) { + // charsets are case-insensitive according to the HTTP spec (e.g. utf-8 == UTF-8): + // https://tools.ietf.org/html/rfc2616#section-3.4 + assertThat(getActualContentTypeHeader(), equalToIgnoringCase(expectedContentTypeHeader)); + } + + @SuppressWarnings("deprecation") + @Test + public void testCreateNonFluent() throws Exception { + ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + Patient p = new Patient(); + p.addName().addFamily("FOOFAMILY"); + + client.create(p); + + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); + assertThat(ourRequestBodyString, containsString("")); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); + assertEquals("POST", ourRequestMethod); + } + + @Test + public void testCreatePrefer() throws Exception { + ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + Patient p = new Patient(); + p.addName().addFamily("FOOFAMILY"); + + client.create().resource(p).prefer(PreferReturnEnum.MINIMAL).execute(); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_PREFER).size()); + assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_MINIMAL, ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); + + client.create().resource(p).prefer(PreferReturnEnum.REPRESENTATION).execute(); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_PREFER).size()); + assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_REPRESENTATION, ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); + } + + @Test + public void testCreateReturningResourceBody() throws Exception { + Patient p = new Patient(); + p.setId("123"); + final String formatted = ourCtx.newXmlParser().encodeResourceToString(p); + + ourResponseStatus = Constants.STATUS_HTTP_200_OK; + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = formatted; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + p = new Patient(); + p.setId(new IdDt("1")); + p.addName().addFamily("FOOFAMILY"); + + MethodOutcome output = client.create().resource(p).execute(); + assertNotNull(output.getResource()); + assertEquals("Patient/123", output.getResource().getIdElement().toUnqualifiedVersionless().getValue()); + } + + @Test + public void testDeleteConditional() throws Exception { + ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + client.delete().resourceById(new IdDt("Patient/123")).execute(); + assertEquals("DELETE", ourRequestMethod); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUri); + + client.delete().resourceConditionalByUrl("Patient?name=foo").execute(); + assertEquals("DELETE", ourRequestMethod); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestUri); + + client.delete().resourceConditionalByType("Patient").where(Patient.NAME.matches().value("foo")).execute(); + assertEquals("DELETE", ourRequestMethod); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestUri); + } + + @SuppressWarnings("deprecation") + @Test + public void testDeleteNonFluent() throws Exception { + ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + client.delete(Patient.class, new IdDt("Patient/123")); + assertEquals("DELETE", ourRequestMethod); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUri); + + client.delete(Patient.class, "123"); + assertEquals("DELETE", ourRequestMethod); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUri); + } + + @Test + public void testHistory() throws Exception { + final String msg = getPatientFeedWithOneResult(); + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = msg; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + ca.uhn.fhir.model.dstu2.resource.Bundle response; + + //@formatter:off + response = client + .history() + .onServer() + .andReturnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/_history", ourRequestUri); + assertEquals(1, response.getEntry().size()); + + //@formatter:off + response = client + .history() + .onServer() + .andReturnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) + .since((Date) null) + .count(null) + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/_history", ourRequestUri); + assertEquals(1, response.getEntry().size()); + + //@formatter:off + response = client + .history() + .onServer() + .andReturnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) + .since(new InstantDt()) + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/_history", ourRequestUri); + assertEquals(1, response.getEntry().size()); + + //@formatter:off + response = client + .history() + .onType(Patient.class) + .andReturnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/_history", ourRequestUri); + assertEquals(1, response.getEntry().size()); + + //@formatter:off + response = client + .history() + .onInstance(new IdDt("Patient", "123")) + .andReturnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/_history", ourRequestUri); + assertEquals(1, response.getEntry().size()); + + //@formatter:off + response = client + .history() + .onInstance(new IdDt("Patient", "123")) + .andReturnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) + .count(123) + .since(new InstantDt("2001-01-02T11:22:33Z")) + .execute(); + //@formatter:on + assertThat(ourRequestUri, either(equalTo("http://localhost:" + ourPort + "/fhir/Patient/123/_history?_since=2001-01-02T11:22:33Z&_count=123")).or(equalTo("http://localhost:" + ourPort + "/fhir/Patient/123/_history?_count=123&_since=2001-01-02T11:22:33Z"))); + assertEquals(1, response.getEntry().size()); + + //@formatter:off + response = client + .history() + .onInstance(new IdDt("Patient", "123")) + .andReturnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) + .since(new InstantDt("2001-01-02T11:22:33Z").getValue()) + .execute(); + //@formatter:on + assertThat(ourRequestUri, containsString("_since=2001-01")); + assertEquals(1, response.getEntry().size()); + } + + @Test + public void testMetaAdd() throws Exception { + IParser p = ourCtx.newXmlParser(); + + MetaDt inMeta = new MetaDt().addProfile("urn:profile:in"); + + Parameters outParams = new Parameters(); + outParams.addParameter().setName("meta").setValue(new MetaDt().addProfile("urn:profile:out")); + final String respString = p.encodeResourceToString(outParams); + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = respString; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + //@formatter:off + MetaDt resp = client + .meta() + .add() + .onResource(new IdDt("Patient/123")) + .meta(inMeta) + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$meta-add", ourRequestUri); + assertEquals("urn:profile:out", resp.getProfile().get(0).getValue()); + assertEquals("POST", ourRequestMethod); + assertEquals("", ourRequestBodyString); + } + + @Test + public void testMetaGet() throws Exception { + IParser p = ourCtx.newXmlParser(); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("meta").setValue(new MetaDt().addProfile("urn:profile:in")); + + Parameters outParams = new Parameters(); + outParams.addParameter().setName("meta").setValue(new MetaDt().addProfile("urn:profile:out")); + final String respString = p.encodeResourceToString(outParams); + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = respString; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + //@formatter:off + MetaDt resp = client + .meta() + .get(MetaDt.class) + .fromServer() + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/$meta", ourRequestUri); + assertEquals("urn:profile:out", resp.getProfile().get(0).getValue()); + assertEquals("GET", ourRequestMethod); + + //@formatter:off + resp = client + .meta() + .get(MetaDt.class) + .fromType("Patient") + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$meta", ourRequestUri); + assertEquals("urn:profile:out", resp.getProfile().get(0).getValue()); + assertEquals("GET", ourRequestMethod); + + //@formatter:off + resp = client + .meta() + .get(MetaDt.class) + .fromResource(new IdDt("Patient/123")) + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$meta", ourRequestUri); + assertEquals("urn:profile:out", resp.getProfile().get(0).getValue()); + assertEquals("GET", ourRequestMethod); + } + + @Test + public void testOperationAsGetWithInParameters() throws Exception { + IParser p = ourCtx.newXmlParser(); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("param1").setValue(new StringDt("STRINGVALIN1")); + inParams.addParameter().setName("param1").setValue(new StringDt("STRINGVALIN1b")); + inParams.addParameter().setName("param2").setValue(new StringDt("STRINGVALIN2")); + + Parameters outParams = new Parameters(); + outParams.addParameter().setValue(new StringDt("STRINGVALOUT1")); + outParams.addParameter().setValue(new StringDt("STRINGVALOUT2")); + final String respString = p.encodeResourceToString(outParams); + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = respString; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + //@formatter:off + Parameters resp = client + .operation() + .onServer() + .named("$SOMEOPERATION") + .withParameters(inParams) + .useHttpGet() + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", ourRequestUri); + assertEquals(respString, p.encodeResourceToString(resp)); + assertEquals("GET", ourRequestMethod); + + //@formatter:off + resp = client + .operation() + .onType(Patient.class) + .named("$SOMEOPERATION") + .withParameters(inParams) + .useHttpGet() + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", ourRequestUri); + assertEquals(respString, p.encodeResourceToString(resp)); + assertEquals("GET", ourRequestMethod); + + //@formatter:off + resp = client + .operation() + .onInstance(new IdDt("Patient", "123")) + .named("$SOMEOPERATION") + .withParameters(inParams) + .useHttpGet() + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", ourRequestUri); + assertEquals(respString, p.encodeResourceToString(resp)); + assertEquals("GET", ourRequestMethod); + + // @formatter:off + resp = client + .operation() + .onInstance(new IdDt("http://foo.com/bar/baz/Patient/123/_history/22")) + .named("$SOMEOPERATION") + .withParameters(inParams) + .useHttpGet() + .execute(); + // @formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", ourRequestUri); + } + + @Test + public void testOperationAsGetWithNoInParameters() throws Exception { + IParser p = ourCtx.newXmlParser(); + + Parameters outParams = new Parameters(); + outParams.addParameter().setValue(new StringDt("STRINGVALOUT1")); + outParams.addParameter().setValue(new StringDt("STRINGVALOUT2")); + final String respString = p.encodeResourceToString(outParams); + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = respString; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + //@formatter:off + Parameters resp = client + .operation() + .onServer() + .named("$SOMEOPERATION") + .withNoParameters(Parameters.class) + .useHttpGet() + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(respString, p.encodeResourceToString(resp)); + assertEquals("GET", ourRequestMethod); + + //@formatter:off + resp = client + .operation() + .onType(Patient.class) + .named("$SOMEOPERATION") + .withNoParameters(Parameters.class) + .useHttpGet() + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$SOMEOPERATION", ourRequestUri); + assertEquals(respString, p.encodeResourceToString(resp)); + assertEquals("GET", ourRequestMethod); + + //@formatter:off + resp = client + .operation() + .onInstance(new IdDt("Patient", "123")) + .named("$SOMEOPERATION") + .withNoParameters(Parameters.class) + .useHttpGet() + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(respString, p.encodeResourceToString(resp)); + assertEquals("GET", ourRequestMethod); + + // @formatter:off + resp = client + .operation() + .onInstance(new IdDt("http://foo.com/bar/baz/Patient/123/_history/22")) + .named("$SOMEOPERATION") + .withNoParameters(Parameters.class) + .useHttpGet() + .execute(); + // @formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + } + + @Test + public void testOperationWithBundleResponseJson() throws Exception { + ourResponseContentType = Constants.CT_FHIR_JSON; + final String respString = "{\n" + " \"resourceType\":\"Bundle\",\n" + " \"id\":\"8cef5f2a-0ba9-43a5-be26-c8dde9ff0e19\",\n" + " \"base\":\"http://localhost:" + ourPort + "/fhir\"\n" + "}"; + ourResponseBody = respString; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + client.registerInterceptor(new LoggingInterceptor(true)); + + // 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")); + + // Invoke $everything on "Patient/1" + Parameters outParams = client.operation().onInstance(new IdDt("Patient", "18066")).named("$everything").withParameters(inParams).execute(); + + /* + * Note that the $everything operation returns a Bundle instead of a Parameters resource. The client operation + * methods return a Parameters instance however, so HAPI creates a Parameters object + * with a single parameter containing the value. + */ + ca.uhn.fhir.model.dstu2.resource.Bundle responseBundle = (ca.uhn.fhir.model.dstu2.resource.Bundle) outParams.getParameter().get(0).getResource(); + + // Print the response bundle + assertEquals("8cef5f2a-0ba9-43a5-be26-c8dde9ff0e19", responseBundle.getId().getIdPart()); + } + + @Test + public void testOperationWithBundleResponseXml() throws Exception { + IParser p = ourCtx.newXmlParser(); + + Parameters inParams = new Parameters(); + inParams.addParameter().setValue(new StringDt("STRINGVALIN1")); + inParams.addParameter().setValue(new StringDt("STRINGVALIN2")); + String reqString = p.encodeResourceToString(inParams); + + ca.uhn.fhir.model.dstu2.resource.Bundle outParams = new ca.uhn.fhir.model.dstu2.resource.Bundle(); + outParams.setTotal(123); + final String respString = p.encodeResourceToString(outParams); + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = respString; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + //@formatter:off + Parameters resp = client + .operation() + .onServer() + .named("$SOMEOPERATION") + .withParameters(inParams).execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); + assertEquals(ourRequestBodyString, reqString); + assertEquals("POST", ourRequestMethod); + assertEquals(1, resp.getParameter().size()); + assertEquals(ca.uhn.fhir.model.dstu2.resource.Bundle.class, resp.getParameter().get(0).getResource().getClass()); + } + + @Test + public void testOperationWithInlineParams() throws Exception { + IParser p = ourCtx.newXmlParser(); + + Parameters outParams = new Parameters(); + outParams.addParameter().setValue(new StringDt("STRINGVALOUT1")); + outParams.addParameter().setValue(new StringDt("STRINGVALOUT2")); + final String respString = p.encodeResourceToString(outParams); + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = respString; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + //@formatter:off + Parameters resp = client + .operation() + .onServer() + .named("$SOMEOPERATION") + .withParameter(Parameters.class, "name1", new StringDt("value1")) + .andParameter("name2", new StringDt("value1")) + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(respString, p.encodeResourceToString(resp)); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); + assertEquals("POST", ourRequestMethod); + assertEquals("", (ourRequestBodyString)); + + /* + * Composite type + */ + + //@formatter:off + resp = client + .operation() + .onServer() + .named("$SOMEOPERATION") + .withParameter(Parameters.class, "name1", new IdentifierDt("system1", "value1")) + .andParameter("name2", new StringDt("value1")) + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(respString, p.encodeResourceToString(resp)); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); + assertEquals("POST", ourRequestMethod); + assertEquals("", + (ourRequestBodyString)); + + /* + * Resource + */ + + //@formatter:off + resp = client + .operation() + .onServer() + .named("$SOMEOPERATION") + .withParameter(Parameters.class, "name1", new IdentifierDt("system1", "value1")) + .andParameter("name2", new Patient().setActive(true)) + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(respString, p.encodeResourceToString(resp)); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); + assertEquals("POST", ourRequestMethod); + assertEquals( + "", + (ourRequestBodyString)); + } + + @Test(expected = IllegalArgumentException.class) + public void testOperationWithInvalidParam() { + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + // Who knows what the heck this is! + IBase weirdBase = new IBase() { + private static final long serialVersionUID = 1L; + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public boolean hasFormatComment() { + return false; + } + + @Override + public List getFormatCommentsPre() { + return null; + } + + @Override + public List getFormatCommentsPost() { + return null; + } + }; + + //@formatter:off + client + .operation() + .onServer() + .named("$SOMEOPERATION") + .withParameter(Parameters.class, "name1", weirdBase) + .execute(); + //@formatter:on + } + + @Test + public void testOperationWithProfiledDatatypeParam() throws IOException, Exception { + IParser p = ourCtx.newXmlParser(); + + Parameters outParams = new Parameters(); + outParams.addParameter().setValue(new StringDt("STRINGVALOUT1")); + outParams.addParameter().setValue(new StringDt("STRINGVALOUT2")); + final String respString = p.encodeResourceToString(outParams); + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = respString; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + //@formatter:off + client + .operation() + .onInstance(new IdDt("http://foo/Patient/1")) + .named("validate-code") + .withParameter(Parameters.class, "code", new CodeDt("8495-4")) + .andParameter("system", new UriDt("http://loinc.org")) + .useHttpGet() + .execute(); + //@formatter:off + + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/1/$validate-code?code=8495-4&system=http%3A%2F%2Floinc.org", ourRequestUri); + + //@formatter:off + + client + .operation() + .onInstance(new IdDt("http://foo/Patient/1")) + .named("validate-code") + .withParameter(Parameters.class, "code", new CodeDt("8495-4")) + .andParameter("system", new UriDt("http://loinc.org")) + .execute(); + //@formatter:off + + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/1/$validate-code", ourRequestUri); + ourLog.info(ourRequestBodyString); + assertEquals("", ourRequestBodyString); + } + + @Test + public void testOperationWithListOfParameterResponse() throws Exception { + IParser p = ourCtx.newXmlParser(); + + Parameters inParams = new Parameters(); + inParams.addParameter().setValue(new StringDt("STRINGVALIN1")); + inParams.addParameter().setValue(new StringDt("STRINGVALIN2")); + String reqString = p.encodeResourceToString(inParams); + + Parameters outParams = new Parameters(); + outParams.addParameter().setValue(new StringDt("STRINGVALOUT1")); + outParams.addParameter().setValue(new StringDt("STRINGVALOUT2")); + final String respString = p.encodeResourceToString(outParams); + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = respString; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + //@formatter:off + Parameters resp = client + .operation() + .onServer() + .named("$SOMEOPERATION") + .withParameters(inParams).execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(respString, p.encodeResourceToString(resp)); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); + assertEquals(ourRequestBodyString, reqString); + assertEquals("POST", ourRequestMethod); + + //@formatter:off + resp = client + .operation() + .onType(Patient.class) + .named("$SOMEOPERATION") + .withParameters(inParams).execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$SOMEOPERATION", ourRequestUri); + assertEquals(respString, p.encodeResourceToString(resp)); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); + assertEquals(ourRequestBodyString, reqString); + assertEquals("POST", ourRequestMethod); + + //@formatter:off + resp = client + .operation() + .onInstance(new IdDt("Patient", "123")) + .named("$SOMEOPERATION") + .withParameters(inParams).execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(respString, p.encodeResourceToString(resp)); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); + assertEquals(ourRequestBodyString, reqString); + assertEquals("POST", ourRequestMethod); + + resp = client.operation().onInstance(new IdDt("http://foo.com/bar/baz/Patient/123/_history/22")).named("$SOMEOPERATION").withParameters(inParams).execute(); + // @formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + } + + @Test + public void testOperationWithNoInParameters() throws Exception { + IParser p = ourCtx.newXmlParser(); + + Parameters inParams = new Parameters(); + final String reqString = p.encodeResourceToString(inParams); + + Parameters outParams = new Parameters(); + outParams.addParameter().setValue(new StringDt("STRINGVALOUT1")); + outParams.addParameter().setValue(new StringDt("STRINGVALOUT2")); + final String respString = p.encodeResourceToString(outParams); + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = respString; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + //@formatter:off + Parameters resp = client + .operation() + .onServer() + .named("$SOMEOPERATION") + .withNoParameters(Parameters.class).execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(respString, p.encodeResourceToString(resp)); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); + assertEquals(ourRequestBodyString, reqString); + assertEquals("POST", ourRequestMethod); + + //@formatter:off + resp = client + .operation() + .onType(Patient.class) + .named("$SOMEOPERATION") + .withNoParameters(Parameters.class).execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$SOMEOPERATION", ourRequestUri); + assertEquals(respString, p.encodeResourceToString(resp)); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); + assertEquals(ourRequestBodyString, reqString); + assertEquals("POST", ourRequestMethod); + + //@formatter:off + resp = client + .operation() + .onInstance(new IdDt("Patient", "123")) + .named("$SOMEOPERATION") + .withNoParameters(Parameters.class).execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(respString, p.encodeResourceToString(resp)); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); + assertEquals(ourRequestBodyString, reqString); + assertEquals("POST", ourRequestMethod); + + // @formatter:off + resp = client + .operation() + .onInstance(new IdDt("http://foo.com/bar/baz/Patient/123/_history/22")) + .named("$SOMEOPERATION") + .withNoParameters(Parameters.class) + .execute(); + // @formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + } + + @Test + public void testPageNext() throws Exception { + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = getPatientFeedWithOneResult(); + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + ca.uhn.fhir.model.dstu2.resource.Bundle sourceBundle = new ca.uhn.fhir.model.dstu2.resource.Bundle(); + sourceBundle.getLinkOrCreate(IBaseBundle.LINK_PREV).setUrl("http://localhost:" + ourPort + "/fhir/prev"); + sourceBundle.getLinkOrCreate(IBaseBundle.LINK_NEXT).setUrl("http://localhost:" + ourPort + "/fhir/next"); + + //@formatter:off + ca.uhn.fhir.model.dstu2.resource.Bundle resp = client + .loadPage() + .next(sourceBundle) + .execute(); + //@formatter:on + + assertEquals(1, resp.getEntry().size()); + assertEquals("http://localhost:" + ourPort + "/fhir/next", ourRequestUri); + } + + @Test + public void testPageNextNoLink() throws Exception { + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + ca.uhn.fhir.model.dstu2.resource.Bundle sourceBundle = new ca.uhn.fhir.model.dstu2.resource.Bundle(); + try { + client.loadPage().next(sourceBundle).execute(); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage(), containsString("Can not perform paging operation because no link was found in Bundle with relation \"next\"")); + } + } + + @Test + public void testPagePrev() throws Exception { + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = getPatientFeedWithOneResult(); + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + ca.uhn.fhir.model.dstu2.resource.Bundle sourceBundle = new ca.uhn.fhir.model.dstu2.resource.Bundle(); + sourceBundle.getLinkOrCreate("previous").setUrl("http://localhost:" + ourPort + "/fhir/prev"); + + //@formatter:off + ca.uhn.fhir.model.dstu2.resource.Bundle resp = client + .loadPage() + .previous(sourceBundle) + .execute(); + //@formatter:on + + assertEquals(1, resp.getEntry().size()); + assertEquals("http://localhost:" + ourPort + "/fhir/prev", ourRequestUri); + + + /* + * Try with "prev" instead of "previous" + */ + + sourceBundle = new ca.uhn.fhir.model.dstu2.resource.Bundle(); + sourceBundle.getLinkOrCreate("prev").setUrl("http://localhost:" + ourPort + "/fhir/prev"); + + //@formatter:off + resp = client + .loadPage() + .previous(sourceBundle) + .execute(); + //@formatter:on + + assertEquals(1, resp.getEntry().size()); + assertEquals("http://localhost:" + ourPort + "/fhir/prev", ourRequestUri); + } + + @Test + public void testReadByUri() throws Exception { + Patient patient = new Patient(); + patient.addName().addFamily("FAM"); + final String respString = ourCtx.newXmlParser().encodeResourceToString(patient); + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = respString; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + Patient response; + + response = (Patient) client.read(new UriDt("http://localhost:" + ourPort + "/fhir/Patient/123")); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUri); + assertEquals("FAM", response.getName().get(0).getFamily().get(0).getValue()); + } + + @Test + public void testReadFluentByUri() throws Exception { + Patient patient = new Patient(); + patient.addName().addFamily("FAM"); + final String respString = ourCtx.newXmlParser().encodeResourceToString(patient); + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = respString; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + Patient response; + + response = (Patient) client.read().resource(Patient.class).withUrl(new IdDt("http://localhost:" + ourPort + "/AAA/Patient/123")).execute(); + assertEquals("http://localhost:" + ourPort + "/AAA/Patient/123", ourRequestUri); + assertEquals("FAM", response.getName().get(0).getFamily().get(0).getValue()); + } + + @Test + public void testReadUpdatedHeaderDoesntOverwriteResourceValue() throws Exception { + //@formatter:off + final String input = "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; + //@formatter:on + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = input; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + ca.uhn.fhir.model.dstu2.resource.Bundle response; + + //@formatter:off + response = client + .search() + .forResource(Patient.class) + .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) + .execute(); + //@formatter:on + + assertEquals("2015-06-22T15:48:57.554-04:00", ResourceMetadataKeyEnum.UPDATED.get(response).getValueAsString()); + } + + @Test + public void testReadWithElementsParam() throws Exception { + String msg = "{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}"; + + ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + ourResponseBody = msg; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + //@formatter:off + IBaseResource response = client.read() + .resource("Patient") + .withId("123") + .elementsSubset("name", "identifier") + .execute(); + //@formatter:on + + assertThat(ourRequestUri, either(equalTo("http://localhost:" + ourPort + "/fhir/Patient/123?_elements=name%2Cidentifier")).or(equalTo("http://localhost:" + ourPort + "/fhir/Patient/123?_elements=identifier%2Cname"))); + assertEquals(Patient.class, response.getClass()); + } + + @Test + public void testReadWithSummaryInvalid() throws Exception { + String msg = "<>>>><<<<>"; + + ourResponseContentType = Constants.CT_HTML + "; charset=UTF-8"; + ourResponseBody = msg; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + //@formatter:off + try { + client.read() + .resource(Patient.class) + .withId("123") + .summaryMode(SummaryEnum.TEXT) + .execute(); + fail(); + } catch (InvalidResponseException e) { + assertThat(e.getMessage(), containsString("String does not appear to be valid")); + } + //@formatter:on + } + + @Test + public void testReadWithSummaryParamHtml() throws Exception { + String msg = "
HELP IM A DIV
"; + + ourResponseContentType = Constants.CT_HTML + "; charset=UTF-8"; + ourResponseBody = msg; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + //@formatter:off + Patient response = client.read() + .resource(Patient.class) + .withId("123") + .summaryMode(SummaryEnum.TEXT) + .execute(); + //@formatter:on + + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123?_summary=text", ourRequestUri); + assertEquals(Patient.class, response.getClass()); + assertEquals("
HELP IM A DIV
", response.getText().getDiv().getValueAsString()); + } + + @Test + public void testSearchByString() throws Exception { + String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; + + ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + ourResponseBody = msg; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + //@formatter:off + Bundle response = client.search() + .forResource("Patient") + .where(Patient.NAME.matches().value("james")) + .execute(); + //@formatter:on + + assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=james", ourRequestUri); + assertEquals(Patient.class, response.getEntries().get(0).getResource().getClass()); + } + + @Test + public void testSearchByUrl() throws Exception { + final String msg = getPatientFeedWithOneResult(); + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = msg; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + //@formatter:off + ca.uhn.fhir.model.dstu2.resource.Bundle response = client.search() + .byUrl("http://localhost:" + ourPort + "/AAA?name=http://foo|bar") + .encodedJson() + .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/AAA?name=http%3A//foo%7Cbar&_format=json", ourRequestUri); + assertNotNull(response); + + //@formatter:off + response = client.search() + .byUrl("Patient?name=http://foo|bar") + .encodedJson() + .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=http%3A//foo%7Cbar&_format=json", ourRequestUri); + assertNotNull(response); + + //@formatter:off + response = client.search() + .byUrl("/Patient?name=http://foo|bar") + .encodedJson() + .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=http%3A//foo%7Cbar&_format=json", ourRequestUri); + assertNotNull(response); + + //@formatter:off + response = client.search() + .byUrl("Patient") + .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); + assertNotNull(response); + + //@formatter:off + response = client.search() + .byUrl("Patient?") + .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); + assertNotNull(response); + + try { + client.search().byUrl("foo/bar?test=1"); + } catch (IllegalArgumentException e) { + assertEquals("Search URL must be either a complete URL starting with http: or https:, or a relative FHIR URL in the form [ResourceType]?[Params]", e.getMessage()); + } + } + + /** + * See #191 + */ + @Test + public void testSearchReturningDstu2Bundle() throws Exception { + String msg = IOUtils.toString(GenericOkHttpClientDstu2Test.class.getResourceAsStream("/bundle_orion.xml")); + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = msg; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + //@formatter:off + ca.uhn.fhir.model.dstu2.resource.Bundle response = client.search() + .forResource("Observation") + .where(Patient.NAME.matches().value("FOO")) + .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) + .execute(); + //@formatter:on + + Link link = response.getLink().get(0); + assertEquals("just trying add link", link.getRelation()); + assertEquals("blarion", link.getUrl()); + + Entry entry = response.getEntry().get(0); + link = entry.getLink().get(0); + assertEquals("orionhealth.edit", link.getRelation()); + assertEquals("Observation", link.getUrl()); + } + + @Test + public void testSearchWithElementsParam() throws Exception { + String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; + + ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + ourResponseBody = msg; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + //@formatter:off + Bundle response = client.search() + .forResource("Patient") + .where(Patient.NAME.matches().value("james")) + .elementsSubset("name", "identifier") + .execute(); + //@formatter:on + + assertThat(ourRequestUri, either(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=identifier%2Cname"))); + assertEquals(Patient.class, response.getEntries().get(0).getResource().getClass()); + } + + @Test + public void testSearchByPost() throws Exception { + String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; + + ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + ourResponseBody = msg; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + //@formatter:off + Bundle response = client.search() + .forResource("Patient") + .where(Patient.NAME.matches().value("james")) + .elementsSubset("name", "identifier") + .usingStyle(SearchStyleEnum.POST) + .execute(); + //@formatter:on + + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/_search?_elements=identifier%2Cname", ourRequestUri); + + // assertThat(ourRequestUri, + // either(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=identifier%2Cname"))); + + assertEquals(Patient.class, response.getEntries().get(0).getResource().getClass()); + + assertEquals("name=james", ourRequestBodyString); + + assertEquals("application/x-www-form-urlencoded", ourRequestContentType.replace(";char", "; char").toLowerCase()); + assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY, ourRequestFirstHeaders.get("Accept").getValue()); + assertThat(ourRequestFirstHeaders.get("User-Agent").getValue(), not(emptyString())); + } + + @Test + public void testSearchByPostUseJson() throws Exception { + String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; + + ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + ourResponseBody = msg; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + //@formatter:off + Bundle response = client.search() + .forResource("Patient") + .where(Patient.NAME.matches().value("james")) + .elementsSubset("name", "identifier") + .usingStyle(SearchStyleEnum.POST) + .encodedJson() + .execute(); + //@formatter:on + + assertThat(ourRequestUri, containsString("http://localhost:" + ourPort + "/fhir/Patient/_search?")); + assertThat(ourRequestUri, containsString("_elements=identifier%2Cname")); + assertThat(ourRequestUri, not(containsString("_format=json"))); + + // assertThat(ourRequestUri, + // either(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=identifier%2Cname"))); + + assertEquals(Patient.class, response.getEntries().get(0).getResource().getClass()); + + assertEquals("name=james", ourRequestBodyString); + + assertEquals("application/x-www-form-urlencoded", ourRequestContentType); + assertEquals(Constants.CT_FHIR_JSON, ourRequestFirstHeaders.get("Accept").getValue()); + } + + @Test + public void testSearchWithLastUpdated() throws Exception { + String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; + + ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + ourResponseBody = msg; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + //@formatter:off + Bundle response = client.search() + .forResource("Patient") + .where(Patient.NAME.matches().value("james")) + .lastUpdated(new DateRangeParam("2011-01-01", "2012-01-01")) + .execute(); + //@formatter:on + + assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=james&_lastUpdated=ge2011-01-01&_lastUpdated=le2012-01-01", ourRequestUri); + assertEquals(Patient.class, response.getEntries().get(0).getResource().getClass()); + } + + @Test + public void testSearchWithProfileAndSecurity() throws Exception { + String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; + + ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + ourResponseBody = msg; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + //@formatter:off + Bundle response = client.search() + .forResource("Patient") + .withProfile("http://foo1") + .withProfile("http://foo2") + .withSecurity("system1", "code1") + .withSecurity("system2", "code2") + .execute(); + //@formatter:on + + assertEquals("http://localhost:" + ourPort + "/fhir/Patient?_security=system1%7Ccode1&_security=system2%7Ccode2&_profile=http%3A%2F%2Ffoo1&_profile=http%3A%2F%2Ffoo2", ourRequestUri); + assertEquals(Patient.class, response.getEntries().get(0).getResource().getClass()); + } + + @SuppressWarnings("unused") + @Test + public void testSearchWithReverseInclude() throws Exception { + String msg = getPatientFeedWithOneResult(); + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = msg; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + //@formatter:off + Bundle response = client.search() + .forResource(Patient.class) + .encodedJson() + .revInclude(new Include("Provenance:target")) + .execute(); + //@formatter:on + + assertEquals("http://localhost:" + ourPort + "/fhir/Patient?_revinclude=Provenance%3Atarget&_format=json", ourRequestUri); + } + + @Test + public void testSearchWithSummaryParam() throws Exception { + String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; + + ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + ourResponseBody = msg; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + //@formatter:off + Bundle response = client.search() + .forResource("Patient") + .where(Patient.NAME.matches().value("james")) + .summaryMode(SummaryEnum.FALSE) + .execute(); + //@formatter:on + + assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=james&_summary=false", ourRequestUri); + assertEquals(Patient.class, response.getEntries().get(0).getResource().getClass()); + } + + @Test + public void testTransactionWithListOfResources() throws Exception { + ca.uhn.fhir.model.dstu2.resource.Bundle resp = new ca.uhn.fhir.model.dstu2.resource.Bundle(); + resp.addEntry().getResponse().setLocation("Patient/1/_history/1"); + resp.addEntry().getResponse().setLocation("Patient/2/_history/2"); + String respString = ourCtx.newJsonParser().encodeResourceToString(resp); + + ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + ourResponseBody = respString; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + List input = new ArrayList(); + + Patient p1 = new Patient(); // No ID + p1.addName().addFamily("PATIENT1"); + input.add(p1); + + Patient p2 = new Patient(); // Yes ID + p2.addName().addFamily("PATIENT2"); + p2.setId("Patient/2"); + input.add(p2); + + //@formatter:off + List response = client.transaction() + .withResources(input) + .encodedJson() + .execute(); + //@formatter:on + + assertEquals("http://localhost:" + ourPort + "/fhir", ourRequestUri); + assertEquals(2, response.size()); + + String requestString = ourRequestBodyString; + ca.uhn.fhir.model.dstu2.resource.Bundle requestBundle = ourCtx.newJsonParser().parseResource(ca.uhn.fhir.model.dstu2.resource.Bundle.class, requestString); + assertEquals(2, requestBundle.getEntry().size()); + assertEquals("POST", requestBundle.getEntry().get(0).getRequest().getMethod()); + assertEquals("PUT", requestBundle.getEntry().get(1).getRequest().getMethod()); + assertEquals("Patient/2", requestBundle.getEntry().get(1).getRequest().getUrl()); + + p1 = (Patient) response.get(0); + assertEquals(new IdDt("Patient/1/_history/1"), p1.getId().toUnqualified()); + // assertEquals("PATIENT1", p1.getName().get(0).getFamily().get(0).getValue()); + + p2 = (Patient) response.get(1); + assertEquals(new IdDt("Patient/2/_history/2"), p2.getId().toUnqualified()); + // assertEquals("PATIENT2", p2.getName().get(0).getFamily().get(0).getValue()); + } + + @Test + public void testTransactionWithString() throws Exception { + ca.uhn.fhir.model.dstu2.resource.Bundle req = new ca.uhn.fhir.model.dstu2.resource.Bundle(); + Patient patient = new Patient(); + patient.addName().addFamily("PAT_FAMILY"); + req.addEntry().setResource(patient); + Observation observation = new Observation(); + observation.getCode().setText("OBS_TEXT"); + req.addEntry().setResource(observation); + String reqString = ourCtx.newJsonParser().encodeResourceToString(req); + + ca.uhn.fhir.model.dstu2.resource.Bundle resp = new ca.uhn.fhir.model.dstu2.resource.Bundle(); + resp.addEntry().getResponse().setLocation("Patient/1/_history/1"); + resp.addEntry().getResponse().setLocation("Patient/2/_history/2"); + + ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + ourResponseBody = reqString; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + //@formatter:off + String response = client.transaction() + .withBundle(reqString) + .execute(); + //@formatter:on + + assertEquals("http://localhost:" + ourPort + "/fhir/", ourRequestUri); + assertThat(response, containsString("\"Bundle\"")); + assertContentTypeEquals("application/json+fhir; charset=UTF-8"); + + //@formatter:off + response = client.transaction() + .withBundle(reqString) + .encodedXml() + .execute(); + //@formatter:on + + assertEquals("http://localhost:" + ourPort + "/fhir/", ourRequestUri); + assertContentTypeEquals("application/xml+fhir; charset=UTF-8"); + } + + @Test + public void testTransactionWithTransactionResource() throws Exception { + ca.uhn.fhir.model.dstu2.resource.Bundle resp = new ca.uhn.fhir.model.dstu2.resource.Bundle(); + resp.addEntry().getResponse().setLocation("Patient/1/_history/1"); + resp.addEntry().getResponse().setLocation("Patient/2/_history/2"); + String respString = ourCtx.newJsonParser().encodeResourceToString(resp); + + ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + ourResponseBody = respString; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + ca.uhn.fhir.model.dstu2.resource.Bundle input = new ca.uhn.fhir.model.dstu2.resource.Bundle(); + + Patient p1 = new Patient(); // No ID + p1.addName().addFamily("PATIENT1"); + input.addEntry().setResource(p1); + + Patient p2 = new Patient(); // Yes ID + p2.addName().addFamily("PATIENT2"); + p2.setId("Patient/2"); + input.addEntry().setResource(p2); + + //@formatter:off + ca.uhn.fhir.model.dstu2.resource.Bundle response = client.transaction() + .withBundle(input) + .encodedJson() + .execute(); + //@formatter:on + + assertEquals("http://localhost:" + ourPort + "/fhir", ourRequestUri); + assertEquals(2, response.getEntry().size()); + + assertEquals("Patient/1/_history/1", response.getEntry().get(0).getResponse().getLocation()); + assertEquals("Patient/2/_history/2", response.getEntry().get(1).getResponse().getLocation()); + } + + @Test + public void testUpdateConditional() throws Exception { + ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + Patient p = new Patient(); + p.addName().addFamily("FOOFAMILY"); + + client.update().resource(p).conditionalByUrl("Patient?name=foo").execute(); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); + assertThat(ourRequestBodyString, containsString("")); + assertEquals("PUT", ourRequestMethod); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestUri); + + client.update().resource(p).conditionalByUrl("Patient?name=http://foo|bar").execute(); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); + assertThat(ourRequestBodyString, containsString("")); + assertEquals("PUT", ourRequestMethod); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=http%3A//foo%7Cbar", ourRequestUri); + + client.update().resource(ourCtx.newXmlParser().encodeResourceToString(p)).conditionalByUrl("Patient?name=foo").execute(); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); + assertThat(ourRequestBodyString, containsString("")); + assertEquals("PUT", ourRequestMethod); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestUri); + + client.update().resource(p).conditional().where(Patient.NAME.matches().value("foo")).and(Patient.ADDRESS.matches().value("AAA|BBB")).execute(); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); + assertThat(ourRequestBodyString, containsString("")); + assertEquals("PUT", ourRequestMethod); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo&address=AAA%5C%7CBBB", ourRequestUri); + + client.update().resource(ourCtx.newXmlParser().encodeResourceToString(p)).conditional().where(Patient.NAME.matches().value("foo")).and(Patient.ADDRESS.matches().value("AAA|BBB")).execute(); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); + assertThat(ourRequestBodyString, containsString("")); + assertEquals("PUT", ourRequestMethod); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo&address=AAA%5C%7CBBB", ourRequestUri); + } + + private String getActualContentTypeHeader() { + return ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"); + } + + @Test + public void testUpdateNonFluent() throws Exception { + ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + Patient p = new Patient(); + p.addName().addFamily("FOOFAMILY"); + + client.update(new IdDt("Patient/123"), p); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + + String expectedContentTypeHeader = EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8; + assertThat(getActualContentTypeHeader(), equalToIgnoringCase(expectedContentTypeHeader)); + assertThat(ourRequestBodyString, containsString("")); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUri); + assertEquals("PUT", ourRequestMethod); + + client.update("123", p); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertThat(getActualContentTypeHeader(), equalToIgnoringCase(expectedContentTypeHeader)); + assertThat(ourRequestBodyString, containsString("")); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUri); + assertEquals("PUT", ourRequestMethod); + } + + @Test + public void testUpdatePrefer() throws Exception { + ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + Patient p = new Patient(); + p.setId(new IdDt("1")); + p.addName().addFamily("FOOFAMILY"); + + client.update().resource(p).prefer(PreferReturnEnum.MINIMAL).execute(); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_PREFER).size()); + assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_MINIMAL, ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); + + client.update().resource(p).prefer(PreferReturnEnum.REPRESENTATION).execute(); + assertEquals(1, ourRequestHeaders.get(Constants.HEADER_PREFER).size()); + assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_REPRESENTATION, ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); + } + + @Test + public void testUpdateReturningResourceBody() throws Exception { + Patient p = new Patient(); + p.setId("123"); + final String formatted = ourCtx.newXmlParser().encodeResourceToString(p); + + ourResponseStatus = Constants.STATUS_HTTP_200_OK; + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = formatted; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + p = new Patient(); + p.setId(new IdDt("1")); + p.addName().addFamily("FOOFAMILY"); + + MethodOutcome output = client.update().resource(p).execute(); + assertNotNull(output.getResource()); + assertEquals("Patient/123", output.getResource().getIdElement().toUnqualifiedVersionless().getValue()); + } + + @Test + public void testValidateFluent() throws Exception { + OperationOutcome oo = new OperationOutcome(); + oo.addIssue().setDiagnostics("FOOBAR"); + final String msg = ourCtx.newXmlParser().encodeResourceToString(oo); + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = msg; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + Patient p = new Patient(); + p.addName().addGiven("GIVEN"); + + MethodOutcome response; + + response = client.validate().resource(p).execute(); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate", ourRequestUri); + assertEquals("POST", ourRequestMethod); + assertEquals("", ourRequestBodyString); + assertNotNull(response.getOperationOutcome()); + assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDiagnosticsElement().getValue()); + + response = client.validate().resource(ourCtx.newXmlParser().encodeResourceToString(p)).execute(); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate", ourRequestUri); + assertEquals("POST", ourRequestMethod); + assertEquals("", ourRequestBodyString); + assertNotNull(response.getOperationOutcome()); + assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDiagnosticsElement().getValue()); + + response = client.validate().resource(ourCtx.newJsonParser().encodeResourceToString(p)).execute(); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate", ourRequestUri); + assertEquals("POST", ourRequestMethod); + assertEquals("{\"resourceType\":\"Parameters\",\"parameter\":[{\"name\":\"resource\",\"resource\":{\"resourceType\":\"Patient\",\"name\":[{\"given\":[\"GIVEN\"]}]}}]}", ourRequestBodyString); + assertNotNull(response.getOperationOutcome()); + assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDiagnosticsElement().getValue()); + + response = client.validate().resource(ourCtx.newJsonParser().encodeResourceToString(p)).prettyPrint().execute(); + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate?_pretty=true", ourRequestUri); + assertEquals("POST", ourRequestMethod); + assertThat(ourRequestBodyString, containsString("\"resourceType\": \"Parameters\",\n")); + assertNotNull(response.getOperationOutcome()); + assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDiagnosticsElement().getValue()); + } + + @Test + public void testValidateNonFluent() throws Exception { + OperationOutcome oo = new OperationOutcome(); + oo.addIssue().setDiagnostics("FOOBAR"); + final String msg = ourCtx.newXmlParser().encodeResourceToString(oo); + + ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + ourResponseBody = msg; + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + + Patient p = new Patient(); + p.addName().addGiven("GIVEN"); + + MethodOutcome response; + + //@formatter:off + response = client.validate(p); + //@formatter:on + + assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate", ourRequestUri); + assertEquals("POST", ourRequestMethod); + assertEquals("", ourRequestBodyString); + assertNotNull(response.getOperationOutcome()); + assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDiagnosticsElement().getValue()); + } + + private OperationOutcome toOo(IBaseOperationOutcome theOperationOutcome) { + return (OperationOutcome) theOperationOutcome; + } + + @Before + public void beforeReset() { + ourRequestUri = null; + ourRequestUriAll = Lists.newArrayList(); + ourResponseStatus = 200; + ourResponseBody = null; + ourResponseBodies = null; + ourResponseCount = 0; + + ourResponseContentType = null; + ourRequestContentType = null; + ourRequestBodyBytes = null; + ourRequestBodyString = null; + ourRequestHeaders = null; + ourRequestFirstHeaders = null; + ourRequestMethod = null; + ourRequestHeadersAll = Lists.newArrayList(); + } +} diff --git a/hapi-fhir-okhttp/src/test/java/ca/uhn/fhir/okhttp/OkHttpRestfulClientFactoryTest.java b/hapi-fhir-okhttp/src/test/java/ca/uhn/fhir/okhttp/OkHttpRestfulClientFactoryTest.java new file mode 100644 index 00000000000..fab3ada4d98 --- /dev/null +++ b/hapi-fhir-okhttp/src/test/java/ca/uhn/fhir/okhttp/OkHttpRestfulClientFactoryTest.java @@ -0,0 +1,44 @@ +package ca.uhn.fhir.okhttp; + +import ca.uhn.fhir.okhttp.client.OkHttpRestfulClientFactory; +import okhttp3.OkHttpClient; +import org.junit.Before; +import org.junit.Test; + +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.*; + +public class OkHttpRestfulClientFactoryTest { + + private OkHttpRestfulClientFactory clientFactory; + + @Before + public void setUp() { + clientFactory = new OkHttpRestfulClientFactory(); + } + + @Test + public void testGetNativeClient_noClientSet_returnsADefault() throws Exception { + OkHttpClient actualNativeClient = clientFactory.getNativeClient(); + + assertNotNull(actualNativeClient); + } + + @Test + public void testGetNativeClient_noProxySet_defaultHasNoProxySet() throws Exception { + OkHttpClient actualNativeClient = clientFactory.getNativeClient(); + + assertEquals(null, actualNativeClient.proxy()); + } + + @Test + public void testSetHttpClient() { + OkHttpClient okHttpClient = new OkHttpClient.Builder().writeTimeout(5000, TimeUnit.MILLISECONDS).build(); + + clientFactory.setHttpClient(okHttpClient); + + assertSame(okHttpClient, clientFactory.getNativeClient()); + } + +} \ No newline at end of file diff --git a/hapi-fhir-okhttp/src/test/java/ca/uhn/fhir/okhttp/RandomServerPortProvider.java b/hapi-fhir-okhttp/src/test/java/ca/uhn/fhir/okhttp/RandomServerPortProvider.java new file mode 100644 index 00000000000..f079d1ee396 --- /dev/null +++ b/hapi-fhir-okhttp/src/test/java/ca/uhn/fhir/okhttp/RandomServerPortProvider.java @@ -0,0 +1,36 @@ +package ca.uhn.fhir.okhttp; + +import java.io.IOException; +import java.net.ServerSocket; +import java.util.ArrayList; +import java.util.List; + +/** + * Provides server ports + */ +public class RandomServerPortProvider { + + private static List ourPorts = new ArrayList(); + + public static int findFreePort() { + ServerSocket server; + try { + server = new ServerSocket(0); + int port = server.getLocalPort(); + ourPorts.add(port); + server.close(); + Thread.sleep(500); + return port; + } catch (IOException e) { + throw new Error(e); + } catch (InterruptedException e) { + throw new Error(e); + } + } + + public static List list() { + return ourPorts; + } + +} + \ No newline at end of file diff --git a/hapi-fhir-okhttp/src/test/java/ca/uhn/fhir/okhttp/client/OkHttpRestfulClientTest.java b/hapi-fhir-okhttp/src/test/java/ca/uhn/fhir/okhttp/client/OkHttpRestfulClientTest.java new file mode 100644 index 00000000000..ff147f57e3b --- /dev/null +++ b/hapi-fhir-okhttp/src/test/java/ca/uhn/fhir/okhttp/client/OkHttpRestfulClientTest.java @@ -0,0 +1,24 @@ +package ca.uhn.fhir.okhttp.client; + +import org.junit.Test; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; + +public class OkHttpRestfulClientTest { + + @Test + public void testNewHeaderBuilder_urlHasTrailingSlash_shouldTrim() { + StringBuilder headerBuilder = OkHttpRestfulClient.newHeaderBuilder(new StringBuilder("http://localhost/")); + + assertThat(headerBuilder.toString(), equalTo("http://localhost")); + } + + @Test + public void testNewHeaderBuilder_urlHasNoTrailingSlash_shouldNotTrimLastCharacter() { + StringBuilder headerBuilder = OkHttpRestfulClient.newHeaderBuilder(new StringBuilder("http://example.com")); + + assertThat(headerBuilder.toString(), equalTo("http://example.com")); + } + +} \ No newline at end of file diff --git a/hapi-fhir-okhttp/src/test/resources/bundle_orion.xml b/hapi-fhir-okhttp/src/test/resources/bundle_orion.xml new file mode 100644 index 00000000000..7f2fc0a8275 --- /dev/null +++ b/hapi-fhir-okhttp/src/test/resources/bundle_orion.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 8578e27f223..31a4d406523 100644 --- a/pom.xml +++ b/pom.xml @@ -1576,5 +1576,6 @@ + hapi-fhir-okhttp