OkHttp client (#416)

* Created stub factory, client and test using jaxrs as a template

* OkHttp client, request and response partially implemented

* OkHttp client now adds sensible default headers to requests

* Creating an OkHttp byte request is now implemented

* OkHttp query parameter requests now supported

* Refactored tests, no longer comparing case for charsets

* Added proxy support to okhttpclientfactory
Re-enabled no-fhir-context test

* Fixed no-context test - expected exception message is now generic

* fixed some content type comparisons

* Removing trailing question marks from URLs as OkHttp doesn't do this automatically (like JaxRs)

* Fixed NPE when content-type header doesn't exist

* Correct HTTP method is now set on OkHttp requests (PUT, POST, UPDATE, etc.)

* Removed support for getRequestBodyFromStream - wasn't implemented correctly

* Added response buffering

* Updated test in light of changes pulled in from master

* Extract method refactors in okhttprestfulclient

* Implemented binary request with the OkHttp client

* Extracted out a generic string utils class

* Added relevant file and class headers

* Reformatted code

* Implemented getHttpClient(serverBase) in OkHttpRestfulClientFactory

* Renamed fields and parameters to be consistent with the rest of the project

* Reformat OkHttpClientDstu2Test (removes extraneous blank lines etc.)

* Removed additional blank lines in test

* Copied missing bundle_orion.xml to OkHttp test resources
This commit is contained in:
Matt Clarke 2016-08-03 12:19:37 +12:00 committed by James Agnew
parent 681f4e153d
commit 06b9059a87
12 changed files with 2770 additions and 0 deletions

95
hapi-fhir-okhttp/pom.xml Normal file
View File

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>hapi-fhir</artifactId>
<groupId>ca.uhn.hapi.fhir</groupId>
<version>2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>hapi-fhir-okhttp</artifactId>
<packaging>jar</packaging>
<name>HAPI FHIR OkHttp Client</name>
<dependencies>
<!-- HAPI DEPENDENCIES -->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
<version>2.0-SNAPSHOT</version>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
<exclusion>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- conformance profile -->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2</artifactId>
<version>2.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu3</artifactId>
<version>2.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.4.1</version>
</dependency>
<!-- Unit test dependencies -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-server</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-jetty-http</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-moxy</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -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<String, List<String>> myIfNoneExistParams;
private String myIfNoneExistString;
private RequestTypeEnum myRequestType;
private List<Header> myHeaders;
private OkHttpRestfulRequest myRequest;
public OkHttpRestfulClient(OkHttpClient theClient,
StringBuilder theUrl,
Map<String, List<String>> theIfNoneExistParams,
String theIfNoneExistString,
RequestTypeEnum theRequestType,
List<Header> 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<String, List<String>> theParams, EncodingEnum theEncoding) {
initBaseRequest(theContext, theEncoding, getFormBodyFromParams(theParams));
return myRequest;
}
private RequestBody getFormBodyFromParams(Map<String, List<String>> queryParams) {
FormBody.Builder formBuilder = new FormBody.Builder();
for (Map.Entry<String, List<String>> 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<String, List<String>> 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;
}
}

View File

@ -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<String, List<String>> theIfNoneExistParams,
String theIfNoneExistString,
RequestTypeEnum theRequestType,
List<Header> 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());
}
}

View File

@ -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<String, List<String>> 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();
}
}

View File

@ -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<String, List<String>> 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);
}
}
}
}

View File

@ -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;
}
}

View File

@ -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());
}
}

View File

@ -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<Integer> ourPorts = new ArrayList<Integer>();
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<Integer> list() {
return ourPorts;
}
}

View File

@ -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"));
}
}

View File

@ -0,0 +1,69 @@
<Bundle xmlns="http://hl7.org/fhir">
<id value="487d3669-0e1c-4867-b124-400d1849548d"></id>
<type value="searchset"></type>
<base value="http://localhost:19080/fhir/dstu1"></base>
<link>
<relation value="just trying add link"></relation>
<url value="blarion"></url>
</link>
<link>
<relation value="self"></relation>
<url value="http://localhost:19080/fhir/dstu1/Observation?subject.identifier=puppet|CLONE-AA102"></url>
</link>
<entry>
<link>
<relation value="orionhealth.edit"></relation>
<url value="Observation"></url>
</link>
<resource>
<Observation xmlns="http://hl7.org/fhir">
<id value="0d87f02c-da2c-4551-9ead-2956f0165a4f"></id>
<extension url="http://orionhealth.com/fhir/extensions#created-by"></extension>
<extension url="http://orionhealth.com/fhir/extensions#last-modified-by"></extension>
<extension url="http://orionhealth.com/fhir/extensions#received-instant">
<valueInstant value="2015-06-25T17:08:47.190+12:00"></valueInstant>
</extension>
<code>
<coding>
<system value="http://loinc.org"></system>
<code value="8867-4"></code>
</coding>
</code>
<valueString value="observationValue"></valueString>
<status value="final"></status>
<reliability value="ok"></reliability>
<subject>
<reference value="Patient/INGE6TSFFVAUCMJQGJAHA5LQOBSXI"></reference>
</subject>
</Observation>
</resource>
</entry>
<entry>
<link>
<relation value="orionhealth.edit"></relation>
<url value="Observation"></url>
</link>
<resource>
<Observation xmlns="http://hl7.org/fhir">
<id value="c54ac0cc-a99f-40aa-9541-c5aa853a2e88"></id>
<extension url="http://orionhealth.com/fhir/extensions#created-by"></extension>
<extension url="http://orionhealth.com/fhir/extensions#last-modified-by"></extension>
<extension url="http://orionhealth.com/fhir/extensions#received-instant">
<valueInstant value="2015-06-25T17:08:47.190+12:00"></valueInstant>
</extension>
<code>
<coding>
<system value="http://loinc.org"></system>
<code value="3141-9"></code>
</coding>
</code>
<valueString value="observationValue"></valueString>
<status value="final"></status>
<reliability value="ok"></reliability>
<subject>
<reference value="Patient/INGE6TSFFVAUCMJQGJAHA5LQOBSXI"></reference>
</subject>
</Observation>
</resource>
</entry>
</Bundle>

View File

@ -1576,5 +1576,6 @@
</profiles>
<modules>
<module>hapi-fhir-okhttp</module>
</modules>
</project>