Add paging methods to generic client for DSTU2 bundle

This commit is contained in:
jamesagnew 2015-05-25 07:59:06 -04:00
parent ec3c94a823
commit e22f52ca44
14 changed files with 387 additions and 50 deletions

View File

@ -6,9 +6,9 @@ import java.util.List;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome; import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.Conformance; import ca.uhn.fhir.model.dstu2.resource.Conformance;
import ca.uhn.fhir.model.dstu2.resource.Observation; import ca.uhn.fhir.model.dstu2.resource.Observation;
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome; import ca.uhn.fhir.model.dstu2.resource.OperationOutcome;
@ -36,7 +36,7 @@ public class GenericClientExample {
IGenericClient client = ctx.newRestfulGenericClient(serverBase); IGenericClient client = ctx.newRestfulGenericClient(serverBase);
// Perform a search // Perform a search
Bundle results = client ca.uhn.fhir.model.api.Bundle results = client
.search() .search()
.forResource(Patient.class) .forResource(Patient.class)
.where(Patient.FAMILY.matches().value("duck")) .where(Patient.FAMILY.matches().value("duck"))
@ -48,7 +48,7 @@ public class GenericClientExample {
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static void fluentSearch() { public static void fluentSearch() {
FhirContext ctx = new FhirContext(); FhirContext ctx = FhirContext.forDstu2();
IGenericClient client = ctx.newRestfulGenericClient("http://fhir.healthintersections.com.au/open"); IGenericClient client = ctx.newRestfulGenericClient("http://fhir.healthintersections.com.au/open");
{ {
// START SNIPPET: create // START SNIPPET: create
@ -205,6 +205,7 @@ public class GenericClientExample {
.forResource(Patient.class) .forResource(Patient.class)
.where(Patient.BIRTHDATE.beforeOrEquals().day("2011-01-01")) .where(Patient.BIRTHDATE.beforeOrEquals().day("2011-01-01"))
.and(Patient.CAREPROVIDER.hasChainedProperty(Organization.NAME.matches().value("Health"))) .and(Patient.CAREPROVIDER.hasChainedProperty(Organization.NAME.matches().value("Health")))
.returnBundle(Bundle.class)
.execute(); .execute();
// END SNIPPET: search // END SNIPPET: search
@ -212,6 +213,7 @@ public class GenericClientExample {
response = client.search() response = client.search()
.forResource(Patient.class) .forResource(Patient.class)
.where(Patient.FAMILY.matches().values("Smith", "Smyth")) .where(Patient.FAMILY.matches().values("Smith", "Smyth"))
.returnBundle(Bundle.class)
.execute(); .execute();
// END SNIPPET: searchOr // END SNIPPET: searchOr
@ -221,6 +223,7 @@ public class GenericClientExample {
.where(Patient.ADDRESS.matches().values("Toronto")) .where(Patient.ADDRESS.matches().values("Toronto"))
.and(Patient.ADDRESS.matches().values("Ontario")) .and(Patient.ADDRESS.matches().values("Ontario"))
.and(Patient.ADDRESS.matches().values("Canada")) .and(Patient.ADDRESS.matches().values("Canada"))
.returnBundle(Bundle.class)
.execute(); .execute();
// END SNIPPET: searchAnd // END SNIPPET: searchAnd
@ -229,6 +232,7 @@ public class GenericClientExample {
.forResource(Patient.class) .forResource(Patient.class)
.withIdAndCompartment("123", "condition") .withIdAndCompartment("123", "condition")
.where(Patient.ADDRESS.matches().values("Toronto")) .where(Patient.ADDRESS.matches().values("Toronto"))
.returnBundle(Bundle.class)
.execute(); .execute();
// END SNIPPET: searchCompartment // END SNIPPET: searchCompartment
@ -241,6 +245,7 @@ public class GenericClientExample {
.include(Patient.INCLUDE_ORGANIZATION) .include(Patient.INCLUDE_ORGANIZATION)
.sort().ascending(Patient.BIRTHDATE) .sort().ascending(Patient.BIRTHDATE)
.sort().descending(Patient.NAME).limitTo(123) .sort().descending(Patient.NAME).limitTo(123)
.returnBundle(Bundle.class)
.execute(); .execute();
// END SNIPPET: searchAdv // END SNIPPET: searchAdv
@ -249,6 +254,7 @@ public class GenericClientExample {
.forResource("Patient") .forResource("Patient")
.where(Patient.NAME.matches().value("Tester")) .where(Patient.NAME.matches().value("Tester"))
.usingStyle(SearchStyleEnum.POST) .usingStyle(SearchStyleEnum.POST)
.returnBundle(Bundle.class)
.execute(); .execute();
// END SNIPPET: searchPost // END SNIPPET: searchPost
@ -258,16 +264,9 @@ public class GenericClientExample {
.where(Observation.CODE_VALUE_DATE .where(Observation.CODE_VALUE_DATE
.withLeft(Observation.CODE.exactly().code("FOO$BAR")) .withLeft(Observation.CODE.exactly().code("FOO$BAR"))
.withRight(Observation.VALUE_DATE.exactly().day("2001-01-01"))) .withRight(Observation.VALUE_DATE.exactly().day("2001-01-01")))
.returnBundle(Bundle.class)
.execute(); .execute();
// END SNIPPET: searchComposite // END SNIPPET: searchComposite
// START SNIPPET: searchPaging
if (response.getLinkNext().isEmpty() == false) {
// load next page
Bundle nextPage = client.loadPage().next(response).execute();
}
// END SNIPPET: searchPaging
} }
{ {
// START SNIPPET: transaction // START SNIPPET: transaction
@ -330,7 +329,7 @@ public class GenericClientExample {
public static void history() { public static void history() {
IGenericClient client = FhirContext.forDstu2().newRestfulGenericClient(""); IGenericClient client = FhirContext.forDstu2().newRestfulGenericClient("");
{ {
Bundle response; ca.uhn.fhir.model.api.Bundle response;
// START SNIPPET: historyDstu1 // START SNIPPET: historyDstu1
response = client response = client
.history() .history()
@ -364,8 +363,30 @@ public class GenericClientExample {
} }
public static void main(String[] args) { public static void main(String[] args) {
operation(); paging();
} }
private static void paging() {
{
// START SNIPPET: searchPaging
FhirContext ctx = FhirContext.forDstu2();
IGenericClient client = ctx.newRestfulGenericClient("http://fhirtest.uhn.ca/baseDstu2");
// Perform a search
Bundle results = client.search()
.forResource(Patient.class)
.where(Patient.NAME.matches().value("Smith"))
.returnBundle(Bundle.class)
.execute();
if (results.getLink(Bundle.LINK_NEXT) != null) {
// load next page
Bundle nextPage = client.loadPage().next(results).execute();
}
// END SNIPPET: searchPaging
}
}
@SuppressWarnings("unused") @SuppressWarnings("unused")
private static void operationHttpGet() { private static void operationHttpGet() {
// START SNIPPET: operationHttpGet // START SNIPPET: operationHttpGet
@ -410,6 +431,17 @@ public class GenericClientExample {
.named("$everything") .named("$everything")
.withParameters(inParams) .withParameters(inParams)
.execute(); .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.
*/
Bundle responseBundle = (Bundle) outParams.getParameter().get(0).getResource();
// Print the response bundle
System.out.println(ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(responseBundle));
// END SNIPPET: operation // END SNIPPET: operation
} }

View File

@ -43,6 +43,7 @@ import org.hl7.fhir.instance.model.api.IBaseConformance;
import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import ca.uhn.fhir.context.BaseRuntimeChildDefinition; import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition; import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
@ -79,6 +80,7 @@ import ca.uhn.fhir.rest.gclient.IFetchConformanceTyped;
import ca.uhn.fhir.rest.gclient.IFetchConformanceUntyped; import ca.uhn.fhir.rest.gclient.IFetchConformanceUntyped;
import ca.uhn.fhir.rest.gclient.IGetPage; import ca.uhn.fhir.rest.gclient.IGetPage;
import ca.uhn.fhir.rest.gclient.IGetPageTyped; import ca.uhn.fhir.rest.gclient.IGetPageTyped;
import ca.uhn.fhir.rest.gclient.IGetPageUntyped;
import ca.uhn.fhir.rest.gclient.IGetTags; import ca.uhn.fhir.rest.gclient.IGetTags;
import ca.uhn.fhir.rest.gclient.IHistory; import ca.uhn.fhir.rest.gclient.IHistory;
import ca.uhn.fhir.rest.gclient.IHistoryTyped; import ca.uhn.fhir.rest.gclient.IHistoryTyped;
@ -402,7 +404,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
if (def == null) { if (def == null) {
throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_CANNOT_DETEMINE_RESOURCE_TYPE, theUrl.getValueAsString())); throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_CANNOT_DETEMINE_RESOURCE_TYPE, theUrl.getValueAsString()));
} }
return (IBaseResource) read(def.getImplementingClass(), id); return read(def.getImplementingClass(), id);
} }
@Override @Override
@ -841,23 +843,33 @@ public class GenericClient extends BaseClient implements IGenericClient {
} }
} }
private class GetPageInternal extends BaseClientExecutable<IGetPageTyped, Bundle> implements IGetPageTyped { @SuppressWarnings({ "unchecked", "rawtypes" })
private class GetPageInternal extends BaseClientExecutable<IGetPageTyped<Object>, Object> implements IGetPageTyped<Object> {
private String myUrl; private String myUrl;
private Class<? extends IBaseBundle> myBundleType;
public GetPageInternal(String theUrl) { public GetPageInternal(String theUrl) {
myUrl = theUrl; myUrl = theUrl;
} }
@Override public GetPageInternal(String theUrl, Class<? extends IBaseBundle> theBundleType) {
public Bundle execute() { myUrl = theUrl;
myBundleType = theBundleType;
}
BundleResponseHandler binding = new BundleResponseHandler(null); @Override
public Object execute() {
IClientResponseHandler binding;
if (myBundleType == null) {
binding = new BundleResponseHandler(null);
} else {
binding = new ResourceResponseHandler(myBundleType, null);
}
HttpSimpleGetClientInvocation invocation = new HttpSimpleGetClientInvocation(myUrl); HttpSimpleGetClientInvocation invocation = new HttpSimpleGetClientInvocation(myUrl);
Map<String, List<String>> params = null; Map<String, List<String>> params = null;
return invoke(params, binding, invocation); return invoke(params, binding, invocation);
} }
} }
@ -1022,7 +1034,12 @@ public class GenericClient extends BaseClient implements IGenericClient {
} }
private final class LoadPageInternal implements IGetPage { @SuppressWarnings({"unchecked", "rawtypes"})
private final class LoadPageInternal implements IGetPage, IGetPageUntyped {
private static final String PREVIOUS = "previous";
private static final String PREV = "prev";
private String myPageUrl;
@Override @Override
public IGetPageTyped next(Bundle theBundle) { public IGetPageTyped next(Bundle theBundle) {
@ -1039,6 +1056,64 @@ public class GenericClient extends BaseClient implements IGenericClient {
return new GetPageInternal(thePageUrl); return new GetPageInternal(thePageUrl);
} }
@Override
public <T extends IBaseBundle> IGetPageTyped<T> next(T theBundle) {
return nextOrPrevious("next", theBundle);
}
private <T extends IBaseBundle> IGetPageTyped<T> nextOrPrevious(String theWantRel, T theBundle) {
RuntimeResourceDefinition def = myContext.getResourceDefinition(theBundle);
List<IBase> links = def.getChildByName("link").getAccessor().getValues(theBundle);
if (links == null || links.isEmpty()) {
throw new IllegalArgumentException(myContext.getLocalizer().getMessage(GenericClient.class, "noPagingLinkFoundInBundle", theWantRel));
}
for (IBase nextLink : links) {
BaseRuntimeElementCompositeDefinition linkDef = (BaseRuntimeElementCompositeDefinition) myContext.getElementDefinition(nextLink.getClass());
List<IBase> rel = linkDef.getChildByName("relation").getAccessor().getValues(nextLink);
if (rel == null || rel.isEmpty()) {
continue;
}
String relation = ((IPrimitiveType<?>)rel.get(0)).getValueAsString();
if (theWantRel.equals(relation) || (theWantRel == PREVIOUS && PREV.equals(relation))) {
List<IBase> urls = linkDef.getChildByName("url").getAccessor().getValues(nextLink);
if (urls == null || urls.isEmpty()) {
continue;
}
String url = ((IPrimitiveType<?>)urls.get(0)).getValueAsString();
if (isBlank(url)) {
continue;
}
return (IGetPageTyped<T>) byUrl(url).andReturnBundle(theBundle.getClass());
}
}
throw new IllegalArgumentException(myContext.getLocalizer().getMessage(GenericClient.class, "noPagingLinkFoundInBundle", theWantRel));
}
@Override
public <T extends IBaseBundle> IGetPageTyped<T> previous(T theBundle) {
return nextOrPrevious(PREVIOUS, theBundle);
}
@Override
public IGetPageUntyped byUrl(String thePageUrl) {
if (isBlank(thePageUrl)) {
throw new IllegalArgumentException("thePagingUrl must not be blank or null");
}
myPageUrl = thePageUrl;
return this;
}
@Override
public IGetPageTyped andReturnDstu1Bundle() {
return new GetPageInternal(myPageUrl);
}
@Override
public <T extends IBaseBundle> IGetPageTyped andReturnBundle(Class<T> theBundleType) {
Validate.notNull(theBundleType, "theBundleType must not be null");
return new GetPageInternal(myPageUrl, theBundleType);
}
} }
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
@ -1326,7 +1401,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
ResourceResponseHandler<IBaseResource> handler = new ResourceResponseHandler<IBaseResource>((Class<IBaseResource>) bundleType, null); ResourceResponseHandler<IBaseResource> handler = new ResourceResponseHandler<IBaseResource>((Class<IBaseResource>) bundleType, null);
IBaseResource response = handler.invokeClient(theResponseMimeType, theResponseReader, theResponseStatusCode, theHeaders); IBaseResource response = handler.invokeClient(theResponseMimeType, theResponseReader, theResponseStatusCode, theHeaders);
IVersionSpecificBundleFactory bundleFactory = myContext.newBundleFactory(); IVersionSpecificBundleFactory bundleFactory = myContext.newBundleFactory();
bundleFactory.initializeWithBundleResource((IBaseResource) response); bundleFactory.initializeWithBundleResource(response);
return bundleFactory.toListOfResources(); return bundleFactory.toListOfResources();
} else { } else {
return new ArrayList<IBaseResource>(new BundleResponseHandler(myType).invokeClient(theResponseMimeType, theResponseReader, theResponseStatusCode, theHeaders).toListOfResources()); return new ArrayList<IBaseResource>(new BundleResponseHandler(myType).invokeClient(theResponseMimeType, theResponseReader, theResponseStatusCode, theHeaders).toListOfResources());

View File

@ -20,14 +20,51 @@ package ca.uhn.fhir.rest.gclient;
* #L% * #L%
*/ */
import org.hl7.fhir.instance.model.api.IBaseBundle;
import ca.uhn.fhir.model.api.Bundle; import ca.uhn.fhir.model.api.Bundle;
public interface IGetPage { public interface IGetPage {
IGetPageTyped previous(Bundle theBundle); /**
* Load the next page of results using the link with relation "next" in the bundle. This
* method accepts a DSTU1 Atom Bundle
*/
IGetPageTyped<Bundle> next(Bundle theBundle);
IGetPageTyped next(Bundle theBundle); /**
* Load the next page of results using the link with relation "next" in the bundle. This
* method accepts a DSTU2 Bundle resource
*
* @since 1.1
*/
<T extends IBaseBundle> IGetPageTyped<T> next(T theBundle);
IGetPageTyped url(String thePageUrl); /**
* Load the previous page of results using the link with relation "previous" in the bundle. This
* method accepts a DSTU1 Atom Bundle
*/
IGetPageTyped<Bundle> previous(Bundle theBundle);
/**
* Load the previous page of results using the link with relation "prev" in the bundle. This
* method accepts a DSTU2+ Bundle resource
*
* @since 1.1
*/
<T extends IBaseBundle> IGetPageTyped<T> previous(T theBundle);
/**
* Load a page of results using the a given URL and return a DSTU1 Atom bundle
*
* @deprecated Use {@link #byUrl(String)} instead
*/
@Deprecated
IGetPageTyped<Bundle> url(String thePageUrl);
/**
* Load a page of results using the a given URL and return a DSTU1 Atom bundle
*/
IGetPageUntyped byUrl(String thePageUrl);
} }

View File

@ -20,9 +20,8 @@ package ca.uhn.fhir.rest.gclient;
* #L% * #L%
*/ */
import ca.uhn.fhir.model.api.Bundle;
public interface IGetPageTyped extends IClientExecutable<IGetPageTyped, Bundle> { public interface IGetPageTyped<T> extends IClientExecutable<IGetPageTyped<T>, T> {
// nothing for now // nothing for now

View File

@ -0,0 +1,40 @@
package ca.uhn.fhir.rest.gclient;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 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 org.hl7.fhir.instance.model.api.IBaseBundle;
import ca.uhn.fhir.model.api.Bundle;
public interface IGetPageUntyped {
/**
* Return a DSTU1 Atom feed
*/
IGetPageTyped<Bundle> andReturnDstu1Bundle();
/**
* Return a Bundle resource of the given type
*/
<T extends IBaseBundle> IGetPageTyped<T> andReturnBundle(Class<T> theBundleType);
}

View File

@ -35,7 +35,7 @@ public interface IBaseBundle extends IBaseResource {
* link.type field to indicate that the given link is for * link.type field to indicate that the given link is for
* the previous page of results. * the previous page of results.
*/ */
public static final String LINK_PREV = "prev"; public static final String LINK_PREV = "previous";
/** /**
* Constant for links provided in the bundle. This constant is used in the * Constant for links provided in the bundle. This constant is used in the

View File

@ -6,6 +6,7 @@ ca.uhn.fhir.context.FhirContext.noStructuresForSpecifiedVersion=Could not find t
ca.uhn.fhir.context.RuntimeResourceDefinition.nonInstantiableType=Resource type "{0}" can not be instantiated. Check that this class has a no-argument constructor, and that it is static if it is a nested type. Error is: {1} ca.uhn.fhir.context.RuntimeResourceDefinition.nonInstantiableType=Resource type "{0}" can not be instantiated. Check that this class has a no-argument constructor, and that it is static if it is a nested type. Error is: {1}
ca.uhn.fhir.rest.client.GenericClient.noPagingLinkFoundInBundle=Can not perform paging operation because no link was found in Bundle with relation "{0}"
ca.uhn.fhir.rest.client.GenericClient.noVersionIdForVread=No version specified in URL for 'vread' operation: {0} ca.uhn.fhir.rest.client.GenericClient.noVersionIdForVread=No version specified in URL for 'vread' operation: {0}
ca.uhn.fhir.rest.client.GenericClient.incompleteUriForRead=The given URI is not an absolute URL and is not usable for this operation: {0} ca.uhn.fhir.rest.client.GenericClient.incompleteUriForRead=The given URI is not an absolute URL and is not usable for this operation: {0}
ca.uhn.fhir.rest.client.GenericClient.cannotDetermineResourceTypeFromUri=Unable to determine the resource type from the given URI: {0} ca.uhn.fhir.rest.client.GenericClient.cannotDetermineResourceTypeFromUri=Unable to determine the resource type from the given URI: {0}

View File

@ -21,6 +21,7 @@ import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.message.BasicHeader; import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicStatusLine; import org.apache.http.message.BasicStatusLine;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
@ -32,7 +33,6 @@ import org.mockito.stubbing.Answer;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle; import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.dstu2.resource.Observation; import ca.uhn.fhir.model.dstu2.resource.Observation;
import ca.uhn.fhir.model.dstu2.resource.Parameters; import ca.uhn.fhir.model.dstu2.resource.Parameters;
@ -57,7 +57,7 @@ public class GenericClientDstu2Test {
public void before() { public void before() {
myHttpClient = mock(HttpClient.class, new ReturnsDeepStubs()); myHttpClient = mock(HttpClient.class, new ReturnsDeepStubs());
ourCtx.getRestfulClientFactory().setHttpClient(myHttpClient); ourCtx.getRestfulClientFactory().setHttpClient(myHttpClient);
ourCtx.getRestfulClientFactory().setServerValidationModeEnum(ServerValidationModeEnum.NEVER); ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
myHttpResponse = mock(HttpResponse.class, new ReturnsDeepStubs()); myHttpResponse = mock(HttpResponse.class, new ReturnsDeepStubs());
} }
@ -83,13 +83,10 @@ public class GenericClientDstu2Test {
.execute(); .execute();
//@formatter:on //@formatter:on
assertEquals( assertEquals("http://example.com/fhir/Patient?_revinclude=Provenance%3Atarget&_format=json", capt.getValue().getURI().toString());
"http://example.com/fhir/Patient?_revinclude=Provenance%3Atarget&_format=json",
capt.getValue().getURI().toString());
} }
private String getPatientFeedWithOneResult() { private String getPatientFeedWithOneResult() {
//@formatter:off //@formatter:off
String msg = "<Bundle xmlns=\"http://hl7.org/fhir\">\n" + String msg = "<Bundle xmlns=\"http://hl7.org/fhir\">\n" +
@ -615,7 +612,6 @@ public class GenericClientDstu2Test {
// assertEquals("PATIENT2", p2.getName().get(0).getFamily().get(0).getValue()); // assertEquals("PATIENT2", p2.getName().get(0).getFamily().get(0).getValue());
} }
@Test @Test
public void testTransactionWithString() throws Exception { public void testTransactionWithString() throws Exception {
@ -638,7 +634,8 @@ public class GenericClientDstu2Test {
@Override @Override
public InputStream answer(InvocationOnMock theInvocation) throws Throwable { public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(respStringJson), Charset.forName("UTF-8")); return new ReaderInputStream(new StringReader(respStringJson), Charset.forName("UTF-8"));
}}); }
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
@ -671,8 +668,6 @@ public class GenericClientDstu2Test {
} }
@Test @Test
public void testTransactionWithTransactionResource() throws Exception { public void testTransactionWithTransactionResource() throws Exception {
@ -888,4 +883,101 @@ public class GenericClientDstu2Test {
return body; return body;
} }
@Test
public void testPageNext() throws Exception {
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(getPatientFeedWithOneResult()), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
int idx = 0;
ca.uhn.fhir.model.dstu2.resource.Bundle sourceBundle = new ca.uhn.fhir.model.dstu2.resource.Bundle();
sourceBundle.getLinkOrCreate(IBaseBundle.LINK_PREV).setUrl("http://foo.bar/prev");
sourceBundle.getLinkOrCreate(IBaseBundle.LINK_NEXT).setUrl("http://foo.bar/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://foo.bar/next", capt.getAllValues().get(idx).getURI().toASCIIString());
idx++;
}
@Test
public void testPagePrev() throws Exception {
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(getPatientFeedWithOneResult()), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
int idx = 0;
ca.uhn.fhir.model.dstu2.resource.Bundle sourceBundle = new ca.uhn.fhir.model.dstu2.resource.Bundle();
sourceBundle.getLinkOrCreate("previous").setUrl("http://foo.bar/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://foo.bar/prev", capt.getAllValues().get(idx).getURI().toASCIIString());
idx++;
/*
* Try with "prev" instead of "previous"
*/
sourceBundle = new ca.uhn.fhir.model.dstu2.resource.Bundle();
sourceBundle.getLinkOrCreate("prev").setUrl("http://foo.bar/prev");
//@formatter:off
resp = client
.loadPage()
.previous(sourceBundle)
.execute();
//@formatter:on
assertEquals(1, resp.getEntry().size());
assertEquals("http://foo.bar/prev", capt.getAllValues().get(idx).getURI().toASCIIString());
idx++;
}
@Test
public void testPageNextNoLink() throws Exception {
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/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\""));
}
}
} }

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<mxfile userAgent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:37.0) Gecko/20100101 Firefox/37.0" type="google"><diagram>5Vnfb6M4EP5r8rgoQAnJY5M2e5XupNX2pLt7dMGAVceOjNk299fvGI8DAdpL24RudC+R/fn3N994PGQSrjbPXxXZFn/IlPJJME2fJ+HNJAhmgQ+/BthZ4CqMLZArlloIexjgnv1LEZwiWrGUlgcdtZRcs+0hmEghaKIPsExyXAIn25LcTd8A9wnhffQvlurCovNg1uC/UZYXbhl/trAtpd65OVKakYrrLzUEbaZ5Q9xc9UbCWyBMSQnTmNLmeUW5Ic0REtIwC2dRtoiiZO7HyRe7/fWx3ffHUFTgVj865dxO+YPwCs95tyQl/U5LWamE9lh4Kpim91sCTeHNEyhjEi4LveFQ86GYSaHvsXddZ5yvJJeqHh3eXN366whwXJUqTVFQx5yt7oMH+0rlhmq1gzE4QzBDNaAiI6w+NRb3rxArWtZ2IiIovHw/c0MkFJDLI3lF/bR4lSr3Ch57WcGUx0SpiUioZ0RkXMsjW+Z9nHrrZr6pl9CTifx3mplzAm8OWUqt5eYQ+450tKA/JSxQA2MYKz7SWA47qbHcndT2gkv0AN+xiKT6Qy4QjuUCoMIuqwnxqkJYF7CyN4sa5b+X7lFoHBLnEI3nESfGmrY4r8XuEvUZdpx+UJ+jXdF++K47+gPcj8Lp596kVz1OaApPMaxKpQuZS0H4bYMulaxESs2U00O+6DPTfxvYM4o0tX+wE7CldrYpqNtM3TTWw0R6rZR8guoDl8mjhdYg9P1o9yCtI6XdstnnG40C57Q6eD2saKJy+tJE9UOhb11FOdHsx+GOTmuqaHRTxb+uqfCS/UVNhXlK66b6BkuaI5/29l8s1uvFYqSwOvhAH+91gunqf71O0lJXgafwxvde4r3F8ZAJRuHzc58p/UzyAjXajaafm0T6b84i30n5KFR+6sPE2ef80e5tAa2btXPyQPlSqpQqp3khhdnM2UIfBpdXQ5/N5T4Q+3DoN8mMOvdfAdz15DTinkxuCrspHNWYHygmZnnXbWs6lD2B7Dd6lGYiSrL5LIzjOIh8EmWOvZb39TRUFmRrikml+G6pSPJoKBwMAS35NLo6WwrrnMh9ne37nvOItuv58xP4Xo/HfgLbJFAzbvh6UFDKTelOaKoyIM5as8010GN6tHgstZKPtOMn7XCBEOEsF1BV9qRLwzWDr8PXiG9YmtYOP2Q3Cb0zXnt0Af0oDDiLybp5nHOFlsmcdU59W/Ys1s+ML1b5x0r/LMrHbHhQ+dM7jN5QXHFSlkbyXXf4vzlB13jOKdpvhulYXoAZwgtPBqR1hDfC2SI/Ptlfj/z1XXD6rBeqzT9lNlo3/zOGtz8B</diagram></mxfile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -47,7 +47,7 @@
--> -->
<!-- <!--
--> -->
<link rel="shortcut icon" href="https://github.com/jamesagnew/hapi-fhir/images/favicon.png" /> <link rel="shortcut icon" href="http://jamesagnew.github.io/hapi-fhir/images/favicon.png" />
<link rel="stylesheet" type="text/css" href="hapi.css" /> <link rel="stylesheet" type="text/css" href="hapi.css" />
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" /> <link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" />

View File

@ -16,9 +16,42 @@
different one. These dependencies can cause conflicts and be very irritating to solve. different one. These dependencies can cause conflicts and be very irritating to solve.
</p> </p>
<subsection name="Quick Start: Using Logback">
<p> <p>
Unfortunately HAPI is not immune to this issue. If you don't want to spend much time worrying about logging, it's probably
easiest to just include the <a href="http://logback.qos.ch/">Logback</a>
JAR along with your application.
</p> </p>
<p>
Logback is a powerful and flexible framework. To configure it, simply
include a "logback.xml" file on your classpath. The following contents
may be placed in this file to simply log at a suitable level
to the console:
</p>
<source><![CDATA[<configuration scan="true" scanPeriod="30 seconds">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%file:%line] %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>]]></source>
<p>
For more detail on how logging can be configured, see the
following section.
</p>
</subsection>
<subsection name="Configuring HAPI's Logging - SLF4j"> <subsection name="Configuring HAPI's Logging - SLF4j">

View File

@ -93,8 +93,24 @@
<p> <p>
Searching for resources is probably the most common initial scenario for Searching for resources is probably the most common initial scenario for
client applications, so we'll start the demonstration there. client applications, so we'll start the demonstration there. The FHIR search
operation generally uses a URL with a set of predefined search parameters,
and returns a Bundle containing zero-or-more resources which matched the
given search criteria.
</p> </p>
<p>
Search is a very powerful mechanism, with advanced features such as paging,
including linked resources, etc. See the FHIR
<a href="http://hl7.org/fhir/search.html">search specification</a>
for more information.
</p>
<p>
<notclosed ! colour this section differently>
<b>Note on Bundle types: </b> As of DSTU2, FHIR defines Bundle as a resource
instead of an Atom feed as it was in DSTU1.
</p>
<p> <p>
The following example shows how to query using the generic client: The following example shows how to query using the generic client:
</p> </p>

View File

@ -8,8 +8,18 @@
<body> <body>
<section name="Upgrading to HAPI FHIR 0.8"> <section name="Upgrading to HAPI 1.1">
<p>
HAPI 1.1 introduces support for the "reference i"
</p>
<img src="./images/hapi-1.1-structs-resource.png" alt="Structures"/>
</section>
<!-- <!--
<section name="Upgrading to HAPI FHIR 0.8">
<p> <p>
<b>This section is still incomplete: </b> Note that HAPI 0.8 has not <b>This section is still incomplete: </b> Note that HAPI 0.8 has not
@ -123,8 +133,8 @@
</subsection> </subsection>
-->
</section> </section>
-->
</body> </body>