ResourceReferenceDt#loadResource(IRestfulClient) did not
use the client's read functionality, so it did not handle JSON responses or use interceptors. Thanks to JT for reporting!
This commit is contained in:
parent
e93f7542a5
commit
eea406e10e
|
@ -1,48 +1,23 @@
|
|||
package ca.uhn.fhir.model.base.composite;
|
||||
|
||||
/*
|
||||
* #%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 static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.hl7.fhir.instance.model.api.IBaseDatatype;
|
||||
import org.hl7.fhir.instance.model.api.IBaseReference;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.model.api.BaseIdentifiableElement;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.client.BaseClient;
|
||||
import ca.uhn.fhir.rest.client.api.IRestfulClient;
|
||||
|
||||
public abstract class BaseResourceReferenceDt extends BaseIdentifiableElement implements IBaseDatatype, IBaseReference {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseResourceReferenceDt.class);
|
||||
private IBaseResource myResource;
|
||||
|
||||
|
@ -95,42 +70,27 @@ public abstract class BaseResourceReferenceDt extends BaseIdentifiableElement im
|
|||
* HTTP client to retrieve the resource unless it has already been loaded, or was a contained resource in which case
|
||||
* it is simply returned.
|
||||
*/
|
||||
public IBaseResource loadResource(IRestfulClient theClient) throws IOException {
|
||||
public IBaseResource loadResource(IRestfulClient theClient) {
|
||||
if (myResource != null) {
|
||||
return myResource;
|
||||
}
|
||||
|
||||
IdDt resourceId = getReference();
|
||||
if (resourceId == null) {
|
||||
if (resourceId == null || isBlank(resourceId.getValue())) {
|
||||
throw new IllegalStateException("Reference has no resource ID defined");
|
||||
}
|
||||
if (isBlank(resourceId.getBaseUrl()) || isBlank(resourceId.getResourceType())) {
|
||||
throw new IllegalStateException("Reference is not complete (must be in the form [baseUrl]/[resource type]/[resource ID]) - Reference is: " + resourceId.getValue());
|
||||
}
|
||||
|
||||
String resourceUrl = resourceId.getValue();
|
||||
|
||||
ourLog.debug("Loading resource at URL: {}", resourceUrl);
|
||||
|
||||
HttpClient httpClient = theClient.getHttpClient();
|
||||
FhirContext context = theClient.getFhirContext();
|
||||
|
||||
if (!resourceUrl.startsWith("http")) {
|
||||
resourceUrl = theClient.getServerBase() + resourceUrl;
|
||||
}
|
||||
|
||||
HttpGet get = new HttpGet(resourceUrl);
|
||||
HttpResponse response = httpClient.execute(get);
|
||||
try {
|
||||
// TODO: choose appropriate parser based on response CT
|
||||
IParser parser = context.newXmlParser();
|
||||
|
||||
Reader responseReader = BaseClient.createReaderFromResponse(response);
|
||||
myResource = parser.parseResource(responseReader);
|
||||
|
||||
} finally {
|
||||
if (response instanceof CloseableHttpResponse) {
|
||||
((CloseableHttpResponse) response).close();
|
||||
}
|
||||
}
|
||||
|
||||
RuntimeResourceDefinition definition = theClient.getFhirContext().getResourceDefinition(resourceId.getResourceType());
|
||||
Class<? extends IBaseResource> resourceType = definition.getImplementingClass();
|
||||
myResource = theClient.fetchResourceFromUrl(resourceType, resourceUrl);
|
||||
myResource.setId(resourceUrl);
|
||||
return myResource;
|
||||
}
|
||||
|
||||
|
|
|
@ -46,21 +46,34 @@ import org.apache.http.client.HttpClient;
|
|||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpRequestBase;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
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.BaseRuntimeElementCompositeDefinition;
|
||||
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.api.SummaryEnum;
|
||||
import ca.uhn.fhir.rest.client.api.IRestfulClient;
|
||||
import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException;
|
||||
import ca.uhn.fhir.rest.client.exceptions.InvalidResponseException;
|
||||
import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException;
|
||||
import ca.uhn.fhir.rest.method.HttpGetClientInvocation;
|
||||
import ca.uhn.fhir.rest.method.IClientResponseHandler;
|
||||
import ca.uhn.fhir.rest.method.IClientResponseHandlerHandlesBinary;
|
||||
import ca.uhn.fhir.rest.method.MethodUtil;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
|
||||
public abstract class BaseClient implements IRestfulClient {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseClient.class);
|
||||
|
||||
private final HttpClient myClient;
|
||||
|
@ -74,7 +87,7 @@ public abstract class BaseClient implements IRestfulClient {
|
|||
private Boolean myPrettyPrint = false;
|
||||
private SummaryEnum mySummary;
|
||||
private final String myUrlBase;
|
||||
|
||||
|
||||
BaseClient(HttpClient theClient, String theUrlBase, RestfulClientFactory theFactory) {
|
||||
super();
|
||||
myClient = theClient;
|
||||
|
@ -103,8 +116,8 @@ public abstract class BaseClient implements IRestfulClient {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the encoding that will be used on requests. Default is <code>null</code>, which means the client will not explicitly request an encoding. (This is standard behaviour according to the
|
||||
* FHIR specification)
|
||||
* Returns the encoding that will be used on requests. Default is <code>null</code>, which means the client will not
|
||||
* explicitly request an encoding. (This is standard behaviour according to the FHIR specification)
|
||||
*/
|
||||
public EncodingEnum getEncoding() {
|
||||
return myEncoding;
|
||||
|
@ -137,8 +150,9 @@ public abstract class BaseClient implements IRestfulClient {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the pretty print flag, which is a request to the server for it to return "pretty printed" responses. Note that this is currently a non-standard flag (_pretty) which is supported only by
|
||||
* HAPI based servers (and any other servers which might implement it).
|
||||
* Returns the pretty print flag, which is a request to the server for it to return "pretty printed" responses. Note
|
||||
* that this is currently a non-standard flag (_pretty) which is supported only by HAPI based servers (and any other
|
||||
* servers which might implement it).
|
||||
*/
|
||||
public Boolean getPrettyPrint() {
|
||||
return myPrettyPrint;
|
||||
|
@ -168,8 +182,7 @@ public abstract class BaseClient implements IRestfulClient {
|
|||
return invokeClient(theContext, binding, clientInvocation, null, null, theLogRequestAndResponse, null, null);
|
||||
}
|
||||
|
||||
<T> T invokeClient(FhirContext theContext, IClientResponseHandler<T> binding, BaseHttpClientInvocation clientInvocation, EncodingEnum theEncoding, Boolean thePrettyPrint,
|
||||
boolean theLogRequestAndResponse, SummaryEnum theSummaryMode, Set<String> theSubsetElements) {
|
||||
<T> T invokeClient(FhirContext theContext, IClientResponseHandler<T> binding, BaseHttpClientInvocation clientInvocation, EncodingEnum theEncoding, Boolean thePrettyPrint, boolean theLogRequestAndResponse, SummaryEnum theSummaryMode, Set<String> theSubsetElements) {
|
||||
|
||||
if (!myDontValidateConformance) {
|
||||
myFactory.validateServerBaseIfConfiguredToDoSo(myUrlBase, myClient, this);
|
||||
|
@ -187,7 +200,7 @@ public abstract class BaseClient implements IRestfulClient {
|
|||
} else if (theEncoding == EncodingEnum.JSON) {
|
||||
params.put(Constants.PARAM_FORMAT, Collections.singletonList("json"));
|
||||
}
|
||||
|
||||
|
||||
if (theSummaryMode != null) {
|
||||
params.put(Constants.PARAM_SUMMARY, Collections.singletonList(theSummaryMode.getCode()));
|
||||
} else if (mySummary != null) {
|
||||
|
@ -197,8 +210,8 @@ public abstract class BaseClient implements IRestfulClient {
|
|||
if (thePrettyPrint == Boolean.TRUE) {
|
||||
params.put(Constants.PARAM_PRETTY, Collections.singletonList(Constants.PARAM_PRETTY_VALUE_TRUE));
|
||||
}
|
||||
|
||||
if (theSubsetElements != null && theSubsetElements.isEmpty()== false) {
|
||||
|
||||
if (theSubsetElements != null && theSubsetElements.isEmpty() == false) {
|
||||
params.put(Constants.PARAM_ELEMENTS, Collections.singletonList(StringUtils.join(theSubsetElements, ',')));
|
||||
}
|
||||
|
||||
|
@ -366,8 +379,9 @@ public abstract class BaseClient implements IRestfulClient {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the pretty print flag, which is a request to the server for it to return "pretty printed" responses. Note that this is currently a non-standard flag (_pretty) which is supported only by
|
||||
* HAPI based servers (and any other servers which might implement it).
|
||||
* Returns the pretty print flag, which is a request to the server for it to return "pretty printed" responses. Note
|
||||
* that this is currently a non-standard flag (_pretty) which is supported only by HAPI based servers (and any other
|
||||
* servers which might implement it).
|
||||
*/
|
||||
public boolean isPrettyPrint() {
|
||||
return Boolean.TRUE.equals(myPrettyPrint);
|
||||
|
@ -397,7 +411,8 @@ public abstract class BaseClient implements IRestfulClient {
|
|||
}
|
||||
|
||||
/**
|
||||
* This method is an internal part of the HAPI API and may change, use with caution. If you want to disable the loading of conformance statements, use
|
||||
* This method is an internal part of the HAPI API and may change, use with caution. If you want to disable the
|
||||
* loading of conformance statements, use
|
||||
* {@link IRestfulClientFactory#setServerValidationModeEnum(ServerValidationModeEnum)}
|
||||
*/
|
||||
public void setDontValidateConformance(boolean theDontValidateConformance) {
|
||||
|
@ -405,8 +420,9 @@ public abstract class BaseClient implements IRestfulClient {
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets the encoding that will be used on requests. Default is <code>null</code>, which means the client will not explicitly request an encoding. (This is perfectly acceptable behaviour according
|
||||
* to the FHIR specification. In this case, the server will choose which encoding to return, and the client can handle either XML or JSON)
|
||||
* Sets the encoding that will be used on requests. Default is <code>null</code>, which means the client will not
|
||||
* explicitly request an encoding. (This is perfectly acceptable behaviour according to the FHIR specification. In
|
||||
* this case, the server will choose which encoding to return, and the client can handle either XML or JSON)
|
||||
*/
|
||||
@Override
|
||||
public void setEncoding(EncodingEnum theEncoding) {
|
||||
|
@ -436,8 +452,9 @@ public abstract class BaseClient implements IRestfulClient {
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets the pretty print flag, which is a request to the server for it to return "pretty printed" responses. Note that this is currently a non-standard flag (_pretty) which is supported only by
|
||||
* HAPI based servers (and any other servers which might implement it).
|
||||
* Sets the pretty print flag, which is a request to the server for it to return "pretty printed" responses. Note
|
||||
* that this is currently a non-standard flag (_pretty) which is supported only by HAPI based servers (and any other
|
||||
* servers which might implement it).
|
||||
*/
|
||||
@Override
|
||||
public void setPrettyPrint(Boolean thePrettyPrint) {
|
||||
|
@ -477,4 +494,67 @@ public abstract class BaseClient implements IRestfulClient {
|
|||
return reader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends IBaseResource> T fetchResourceFromUrl(Class<T> theResourceType, String theUrl) {
|
||||
BaseHttpClientInvocation clientInvocation = new HttpGetClientInvocation(theUrl);
|
||||
ResourceResponseHandler<T> binding = new ResourceResponseHandler<T>(theResourceType, null, false);
|
||||
return invokeClient(getFhirContext(), binding, clientInvocation, null, false, false, null, null);
|
||||
}
|
||||
|
||||
protected final class ResourceResponseHandler<T extends IBaseResource> implements IClientResponseHandler<T> {
|
||||
|
||||
private boolean myAllowHtmlResponse;
|
||||
private IIdType myId;
|
||||
private Class<T> myType;
|
||||
|
||||
public ResourceResponseHandler(Class<T> theType, IIdType theId) {
|
||||
myType = theType;
|
||||
myId = theId;
|
||||
}
|
||||
|
||||
public ResourceResponseHandler(Class<T> theType, IIdType theId, boolean theAllowHtmlResponse) {
|
||||
myType = theType;
|
||||
myId = theId;
|
||||
myAllowHtmlResponse = theAllowHtmlResponse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws BaseServerResponseException {
|
||||
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
|
||||
if (respType == null) {
|
||||
if (myAllowHtmlResponse && theResponseMimeType.toLowerCase().contains(Constants.CT_HTML) && myType != null) {
|
||||
return readHtmlResponse(theResponseReader);
|
||||
}
|
||||
throw NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader);
|
||||
}
|
||||
IParser parser = respType.newParser(getFhirContext());
|
||||
T retVal = parser.parseResource(myType, theResponseReader);
|
||||
|
||||
MethodUtil.parseClientRequestResourceHeaders(myId, theHeaders, retVal);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private T readHtmlResponse(Reader theResponseReader) {
|
||||
RuntimeResourceDefinition resDef = getFhirContext().getResourceDefinition(myType);
|
||||
IBaseResource instance = resDef.newInstance();
|
||||
BaseRuntimeChildDefinition textChild = resDef.getChildByName("text");
|
||||
BaseRuntimeElementCompositeDefinition<?> textElement = (BaseRuntimeElementCompositeDefinition<?>) textChild.getChildByName("text");
|
||||
IBase textInstance = textElement.newInstance();
|
||||
textChild.getMutator().addValue(instance, textInstance);
|
||||
|
||||
BaseRuntimeChildDefinition divChild = textElement.getChildByName("div");
|
||||
BaseRuntimeElementDefinition<?> divElement = divChild.getChildByName("div");
|
||||
IPrimitiveType<?> divInstance = (IPrimitiveType<?>) divElement.newInstance();
|
||||
try {
|
||||
divInstance.setValueAsString(IOUtils.toString(theResponseReader));
|
||||
} catch (Exception e) {
|
||||
throw new InvalidResponseException(400, "Failed to process HTML response from server: " + e.getMessage(), e);
|
||||
}
|
||||
divChild.getMutator().addValue(textInstance, divInstance);
|
||||
return (T) instance;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import java.util.HashMap;
|
|||
import java.util.Map;
|
||||
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
|
@ -57,6 +58,7 @@ class ClientInvocationHandlerFactory {
|
|||
myMethodToLambda.put(theClientType.getMethod("registerInterceptor", IClientInterceptor.class), new RegisterInterceptorLambda());
|
||||
myMethodToLambda.put(theClientType.getMethod("unregisterInterceptor", IClientInterceptor.class), new UnregisterInterceptorLambda());
|
||||
myMethodToLambda.put(theClientType.getMethod("setSummary", SummaryEnum.class), new SetSummaryLambda());
|
||||
myMethodToLambda.put(theClientType.getMethod("fetchResourceFromUrl", Class.class, String.class), new FetchResourceFromUrlLambda());
|
||||
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new ConfigurationException("Failed to find methods on client. This is a HAPI bug!", e);
|
||||
|
@ -85,7 +87,18 @@ class ClientInvocationHandlerFactory {
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class FetchResourceFromUrlLambda implements ILambda {
|
||||
@Override
|
||||
public Object handle(ClientInvocationHandler theTarget, Object[] theArgs) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<? extends IBaseResource> type = (Class<? extends IBaseResource>) theArgs[0];
|
||||
String url = (String) theArgs[1];
|
||||
|
||||
return theTarget.fetchResourceFromUrl(type, url);
|
||||
}
|
||||
}
|
||||
|
||||
class SetEncodingLambda implements ILambda {
|
||||
@Override
|
||||
public Object handle(ClientInvocationHandler theTarget, Object[] theArgs) {
|
||||
|
|
|
@ -50,7 +50,6 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
|
|||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
|
||||
import ca.uhn.fhir.context.BaseRuntimeChildDatatypeDefinition;
|
||||
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
||||
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
|
||||
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
|
||||
|
@ -74,7 +73,6 @@ 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.exceptions.InvalidResponseException;
|
||||
import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException;
|
||||
import ca.uhn.fhir.rest.gclient.IClientExecutable;
|
||||
import ca.uhn.fhir.rest.gclient.ICreate;
|
||||
|
@ -1719,62 +1717,6 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
}
|
||||
}
|
||||
|
||||
private final class ResourceResponseHandler<T extends IBaseResource> implements IClientResponseHandler<T> {
|
||||
|
||||
private boolean myAllowHtmlResponse;
|
||||
private IIdType myId;
|
||||
private Class<T> myType;
|
||||
|
||||
public ResourceResponseHandler(Class<T> theType, IIdType theId) {
|
||||
myType = theType;
|
||||
myId = theId;
|
||||
}
|
||||
|
||||
public ResourceResponseHandler(Class<T> theType, IIdType theId, boolean theAllowHtmlResponse) {
|
||||
myType = theType;
|
||||
myId = theId;
|
||||
myAllowHtmlResponse = theAllowHtmlResponse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws BaseServerResponseException {
|
||||
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
|
||||
if (respType == null) {
|
||||
if (myAllowHtmlResponse && theResponseMimeType.toLowerCase().contains(Constants.CT_HTML) && myType != null) {
|
||||
return readHtmlResponse(theResponseReader);
|
||||
}
|
||||
throw NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader);
|
||||
}
|
||||
IParser parser = respType.newParser(myContext);
|
||||
T retVal = parser.parseResource(myType, theResponseReader);
|
||||
|
||||
MethodUtil.parseClientRequestResourceHeaders(myId, theHeaders, retVal);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private T readHtmlResponse(Reader theResponseReader) {
|
||||
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(myType);
|
||||
IBaseResource instance = resDef.newInstance();
|
||||
BaseRuntimeChildDefinition textChild = resDef.getChildByName("text");
|
||||
BaseRuntimeElementCompositeDefinition<?> textElement = (BaseRuntimeElementCompositeDefinition<?>) textChild.getChildByName("text");
|
||||
IBase textInstance = textElement.newInstance();
|
||||
textChild.getMutator().addValue(instance, textInstance);
|
||||
|
||||
BaseRuntimeChildDefinition divChild = textElement.getChildByName("div");
|
||||
BaseRuntimeElementDefinition<?> divElement = divChild.getChildByName("div");
|
||||
IPrimitiveType<?> divInstance = (IPrimitiveType<?>) divElement.newInstance();
|
||||
try {
|
||||
divInstance.setValueAsString(IOUtils.toString(theResponseReader));
|
||||
} catch (Exception e) {
|
||||
throw new InvalidResponseException(400, "Failed to process HTML response from server: " + e.getMessage(), e);
|
||||
}
|
||||
divChild.getMutator().addValue(textInstance, divInstance);
|
||||
return (T) instance;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private class SearchInternal extends BaseClientExecutable<IQuery<Object>, Object>implements IQuery<Object>, IUntypedQuery {
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ package ca.uhn.fhir.rest.client.api;
|
|||
|
||||
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.api.SummaryEnum;
|
||||
|
@ -30,6 +31,17 @@ import ca.uhn.fhir.rest.server.EncodingEnum;
|
|||
|
||||
public interface IRestfulClient {
|
||||
|
||||
/**
|
||||
* Retrieve the contents at the given URL and parse them as a resource. This
|
||||
* method could be used as a low level implementation of a read/vread/search
|
||||
* operation.
|
||||
*
|
||||
* @param theResourceType The resource type to parse
|
||||
* @param theUrl The URL to load
|
||||
* @return The parsed resource
|
||||
*/
|
||||
<T extends IBaseResource> T fetchResourceFromUrl(Class<T> theResourceType, String theUrl);
|
||||
|
||||
/**
|
||||
* Returns the FHIR context associated with this client
|
||||
*/
|
||||
|
@ -51,7 +63,7 @@ public interface IRestfulClient {
|
|||
* logging, or add security headers, or pre-process responses, etc.
|
||||
*/
|
||||
void registerInterceptor(IClientInterceptor theInterceptor);
|
||||
|
||||
|
||||
/**
|
||||
* Specifies that the client should use the given encoding to do its
|
||||
* queries. This means that the client will append the "_format" param
|
||||
|
@ -62,7 +74,7 @@ public interface IRestfulClient {
|
|||
* an encoding (which generally implies the use of XML). The default is <code>null</code>.
|
||||
*/
|
||||
void setEncoding(EncodingEnum theEncoding);
|
||||
|
||||
|
||||
/**
|
||||
* Specifies that the client should request that the server respond with "pretty printing"
|
||||
* enabled. Note that this is a non-standard parameter, not all servers will
|
||||
|
@ -71,7 +83,7 @@ public interface IRestfulClient {
|
|||
* @param thePrettyPrint The pretty print flag to use in the request (default is <code>false</code>)
|
||||
*/
|
||||
void setPrettyPrint(Boolean thePrettyPrint);
|
||||
|
||||
|
||||
/**
|
||||
* If not set to <code>null</code>, specifies a value for the <code>_summary</code> parameter
|
||||
* to be applied globally on this client.
|
||||
|
@ -82,5 +94,5 @@ public interface IRestfulClient {
|
|||
* Remove an intercaptor that was previously registered using {@link IRestfulClient#registerInterceptor(IClientInterceptor)}
|
||||
*/
|
||||
void unregisterInterceptor(IClientInterceptor theInterceptor);
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,210 @@
|
|||
package ca.uhn.fhir.model.primitive;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.StringReader;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import org.apache.commons.io.input.ReaderInputStream;
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.ProtocolVersion;
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
import org.apache.http.message.BasicHeader;
|
||||
import org.apache.http.message.BasicStatusLine;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||
import ca.uhn.fhir.rest.client.ServerValidationModeEnum;
|
||||
import ca.uhn.fhir.rest.client.api.IRestfulClient;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
|
||||
public class BaseResourceReferenceDtTest {
|
||||
|
||||
private static FhirContext ourCtx;
|
||||
|
||||
private HttpClient myHttpClient;
|
||||
private HttpResponse myHttpResponse;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
ourCtx = FhirContext.forDstu2();
|
||||
myHttpClient = mock(HttpClient.class, new ReturnsDeepStubs());
|
||||
ourCtx.getRestfulClientFactory().setHttpClient(myHttpClient);
|
||||
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
|
||||
myHttpResponse = mock(HttpResponse.class, new ReturnsDeepStubs());
|
||||
}
|
||||
|
||||
private ArgumentCaptor<HttpUriRequest> fixtureJson() throws IOException, ClientProtocolException {
|
||||
Patient patient = new Patient();
|
||||
patient.addName().addFamily("FAM");
|
||||
final String input = ourCtx.newJsonParser().encodeResourceToString(patient);
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getAllHeaders()).thenReturn(new Header[] { new BasicHeader(Constants.HEADER_LAST_MODIFIED, "Sat, 20 Jun 2015 19:32:17 GMT") });
|
||||
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<InputStream>() {
|
||||
@Override
|
||||
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(input), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
return capt;
|
||||
}
|
||||
|
||||
private ArgumentCaptor<HttpUriRequest> fixtureXml() throws IOException, ClientProtocolException {
|
||||
Patient patient = new Patient();
|
||||
patient.addName().addFamily("FAM");
|
||||
final String input = ourCtx.newXmlParser().encodeResourceToString(patient);
|
||||
|
||||
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.getAllHeaders()).thenReturn(new Header[] { new BasicHeader(Constants.HEADER_LAST_MODIFIED, "Sat, 20 Jun 2015 19:32:17 GMT") });
|
||||
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<InputStream>() {
|
||||
@Override
|
||||
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(input), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
return capt;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadResourceFromAnnotationClientJson() throws Exception {
|
||||
ArgumentCaptor<HttpUriRequest> capt = fixtureJson();
|
||||
|
||||
IClientType client = ourCtx.newRestfulClient(IClientType.class, "http://example.com/fhir");
|
||||
|
||||
ResourceReferenceDt ref = new ResourceReferenceDt();
|
||||
ref.setReference("http://domain2.example.com/base/Patient/123");
|
||||
Patient response = (Patient) ref.loadResource(client);
|
||||
|
||||
assertEquals("http://domain2.example.com/base/Patient/123", capt.getAllValues().get(0).getURI().toASCIIString());
|
||||
assertEquals("FAM", response.getName().get(0).getFamily().get(0).getValue());
|
||||
assertEquals("http://domain2.example.com/base/Patient/123", response.getId().getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testErrors() {
|
||||
IClientType client = ourCtx.newRestfulClient(IClientType.class, "http://example.com/fhir");
|
||||
|
||||
try {
|
||||
new ResourceReferenceDt().loadResource(client);
|
||||
fail();
|
||||
} catch (IllegalStateException e) {
|
||||
assertEquals("Reference has no resource ID defined", e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
new ResourceReferenceDt("123").loadResource(client);
|
||||
fail();
|
||||
} catch (IllegalStateException e) {
|
||||
assertEquals("Reference is not complete (must be in the form [baseUrl]/[resource type]/[resource ID]) - Reference is: 123", e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
new ResourceReferenceDt("Patient/123").loadResource(client);
|
||||
fail();
|
||||
} catch (IllegalStateException e) {
|
||||
assertEquals("Reference is not complete (must be in the form [baseUrl]/[resource type]/[resource ID]) - Reference is: Patient/123", e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
new ResourceReferenceDt("http://foo/123123").loadResource(client);
|
||||
fail();
|
||||
} catch (DataFormatException e) {
|
||||
assertEquals("Unknown resource name \"Foo\" (this name is not known in FHIR version \"DSTU2\")", e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
new ResourceReferenceDt("http://foo/Sometype/123123").loadResource(client);
|
||||
fail();
|
||||
} catch (DataFormatException e) {
|
||||
assertEquals("Unknown resource name \"Sometype\" (this name is not known in FHIR version \"DSTU2\")", e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReturnAlreadyLoadedInstance() throws ClientProtocolException, IOException {
|
||||
ArgumentCaptor<HttpUriRequest> capt = fixtureJson();
|
||||
IClientType client = ourCtx.newRestfulClient(IClientType.class, "http://example4.com/fhir");
|
||||
|
||||
Patient pat = new Patient();
|
||||
|
||||
ResourceReferenceDt ref = new ResourceReferenceDt();
|
||||
ref.setReference("http://domain2.example.com/base/Patient/123");
|
||||
ref.setResource(pat);
|
||||
Patient response = (Patient) ref.loadResource(client);
|
||||
|
||||
assertEquals(0, capt.getAllValues().size());
|
||||
assertSame(pat, response);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadResourceFromAnnotationClientXml() throws Exception {
|
||||
ArgumentCaptor<HttpUriRequest> capt = fixtureXml();
|
||||
|
||||
IClientType client = ourCtx.newRestfulClient(IClientType.class, "http://example3.com/fhir");
|
||||
|
||||
ResourceReferenceDt ref = new ResourceReferenceDt();
|
||||
ref.setReference("http://domain2.example.com/base/Patient/123");
|
||||
Patient response = (Patient) ref.loadResource(client);
|
||||
|
||||
assertEquals("FAM", response.getName().get(0).getFamily().get(0).getValue());
|
||||
assertEquals("http://domain2.example.com/base/Patient/123", capt.getAllValues().get(0).getURI().toASCIIString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadResourceFromGenericClientJson() throws Exception {
|
||||
ArgumentCaptor<HttpUriRequest> capt = fixtureJson();
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example1.com/fhir");
|
||||
|
||||
ResourceReferenceDt ref = new ResourceReferenceDt();
|
||||
ref.setReference("http://domain2.example.com/base/Patient/123");
|
||||
Patient response = (Patient) ref.loadResource(client);
|
||||
|
||||
assertEquals("http://domain2.example.com/base/Patient/123", capt.getAllValues().get(0).getURI().toASCIIString());
|
||||
assertEquals("FAM", response.getName().get(0).getFamily().get(0).getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadResourceFromGenericClientXml() throws Exception {
|
||||
ArgumentCaptor<HttpUriRequest> capt = fixtureXml();
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example2.com/fhir");
|
||||
|
||||
ResourceReferenceDt ref = new ResourceReferenceDt();
|
||||
ref.setReference("http://domain2.example.com/base/Patient/123");
|
||||
Patient response = (Patient) ref.loadResource(client);
|
||||
|
||||
assertEquals("http://domain2.example.com/base/Patient/123", capt.getAllValues().get(0).getURI().toASCIIString());
|
||||
assertEquals("FAM", response.getName().get(0).getFamily().get(0).getValue());
|
||||
}
|
||||
|
||||
public interface IClientType extends IRestfulClient {
|
||||
|
||||
}
|
||||
}
|
|
@ -188,6 +188,12 @@
|
|||
by calling the actual implementing method in the server (previously
|
||||
the call was simulated, which meant that many features did not work)
|
||||
</action>
|
||||
<action type="fix">
|
||||
ResourceReferenceDt#loadResource(IRestfulClient) did not
|
||||
use the client's read functionality, so it did not
|
||||
handle JSON responses or use interceptors. Thanks to
|
||||
JT for reporting!
|
||||
</action>
|
||||
</release>
|
||||
<release version="1.2" date="2015-09-18">
|
||||
<action type="add">
|
||||
|
|
Loading…
Reference in New Issue