Compare commits

...

27 Commits

Author SHA1 Message Date
TipzCM 7166deb41e
Merge 012469da5b into fb7571185a 2024-09-26 15:37:57 -04:00
volodymyr-korzh fb7571185a
Composite unique search parameter with dateTime component does not reject resource duplication (#6318)
* Composite Unique Search Parameter with dateTime component does not reject resource duplication - failing tests

* Composite Unique Search Parameter with dateTime component does not reject resource duplication - implementation

* Composite Unique Search Parameter with dateTime component does not reject resource duplication - changelog and test fixes
2024-09-26 09:18:35 -06:00
Tadgh 919e2d2405
Attribution for javamail and API bump (#6319) 2024-09-26 14:56:49 +00:00
leif stawnyczy 012469da5b updating changelog 2024-08-20 13:53:29 -04:00
leif stawnyczy c2418163d6 adding docs 2024-08-20 10:47:55 -04:00
leif stawnyczy 72dd5c9ab8 adding a test 2024-08-20 10:21:47 -04:00
leif stawnyczy 57754b9e8e minor tweaks 2024-08-13 09:34:26 -04:00
leif stawnyczy de0e2313d1 Merge branch 'master' into 6180-fix-client 2024-08-12 14:53:24 -04:00
leif stawnyczy 30ec4256c3 fixing tests 2024-08-12 09:48:40 -04:00
leif stawnyczy 0110242f6f spotless 2024-08-09 17:17:55 -04:00
leif stawnyczy 534de22b04 test fixes 2024-08-09 17:17:39 -04:00
leif stawnyczy 3392d3ee61 fixing tests 2024-08-09 16:47:32 -04:00
leif stawnyczy abb3d40576 fixing tests 2024-08-09 14:53:46 -04:00
leif stawnyczy 6eb8d3e236 fixing tests 2024-08-09 14:12:44 -04:00
leif stawnyczy a668a1b08b Merge branch 'master' into 6180-fix-client 2024-08-09 13:17:42 -04:00
leif stawnyczy 0ac978ca76 minor tweaks 2024-08-09 12:01:20 -04:00
leif stawnyczy 901a78f534 refacting changelog 2024-08-07 10:40:30 -04:00
leif stawnyczy fe715bc681 fixing build 2024-08-07 10:35:32 -04:00
leif stawnyczy 796147e623 cleanup 2024-08-07 09:44:48 -04:00
leif stawnyczy 8d54801370 added changelog 2024-08-06 16:39:52 -04:00
leif stawnyczy e8a5193c19 cleaning up 2024-08-06 16:28:23 -04:00
leif stawnyczy afde9df9a6 cleaning up 2024-08-06 16:23:59 -04:00
leif stawnyczy acab578bc5 fixing the client 2024-08-06 15:05:24 -04:00
leif stawnyczy cf2f55afbb fixing the client 2024-08-01 16:20:26 -04:00
leif stawnyczy a4b731d809 cleaning up client 2024-08-01 08:55:51 -04:00
leif stawnyczy 5671c8c09b implementing most 2024-07-30 17:56:26 -04:00
leif stawnyczy baf3933fbf client changes 2024-07-30 13:50:31 -04:00
33 changed files with 1505 additions and 196 deletions

View File

@ -21,6 +21,7 @@ package ca.uhn.fhir.rest.client.api;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.param.HttpClientRequestParameters;
import org.hl7.fhir.instance.model.api.IBaseBinary;
import java.util.List;
@ -60,11 +61,27 @@ public interface IHttpClient {
*/
IHttpRequest createBinaryRequest(FhirContext theContext, IBaseBinary theBinary);
@Deprecated
IHttpRequest createGetRequest(FhirContext theContext, EncodingEnum theEncoding);
/**
* Create a normal http get request
* @param theContext TODO
* @param theEncoding the request encoding
* Create a normal http request. The RequestType in the parameters will determine the type.
* @return the http request to be executed
*/
IHttpRequest createGetRequest(FhirContext theContext, EncodingEnum theEncoding);
IHttpRequest createRequest(HttpClientRequestParameters theParameters);
void addHeadersToRequest(IHttpRequest theRequest, EncodingEnum theEncodingEnum, FhirContext theContext);
/**
* Updates the client's url, as well as the conditional create/update strings/params
* (ie, the "if None Exists" stuff)
*
* This is used when we reuse a client for multiple different requests
* (ex, searches, or fetching the /metadata endpoint followed by whatever
* the actual endpoint is, etc).
*
* Deprecated / Legacy clients do not use this (they create new clients for
* every request)
*/
void setNewUrl(StringBuilder theUrl, String theIfNoneExistsString, Map<String, List<String>> theIfNoneExistsParams);
}

View File

@ -0,0 +1,178 @@
/*-
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2024 Smile CDR, Inc.
* %%
* 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%
*/
package ca.uhn.fhir.rest.param;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import jakarta.annotation.Nonnull;
import org.hl7.fhir.instance.model.api.IBaseBinary;
import java.util.List;
import java.util.Map;
/**
* This is a parameters object used for creating HttpClientRequests.
* This will allow extensibility without constant signature changes.
*/
public class HttpClientRequestParameters {
/**
* The fhir context used.
*/
private FhirContext myFhirContext;
/**
* The encoding type (JSON, XML, etc) to use for the request.
*/
private EncodingEnum myEncodingEnum;
/**
* The request type (GET, POST, PUT, etc)
* Generally a required field.
*/
private RequestTypeEnum myRequestTypeEnum;
/**
* Parameters
*/
private Map<String, List<String>> myParams;
/**
* The content type to use (application/json, etc)
*/
private String myContentType;
/**
* If the payload is a String, this is the content to attach.
*
* Only one of String/byte[]/form encoded url parameters can be used.
* String contents will be used before byte contents which will be used
* before FormUrlEncoded parameters.
*/
private String myContents;
/**
* If the payload is a binary, this is the binary to attach
*/
private IBaseBinary myBaseBinary;
/**
* The URL where the request is to be made.
*/
private final String myUrl;
/**
* If the payload is a byte[], this is the content to attach.
*
* Only one of String/byte[]/form encoded url parameters can be used.
* String contents will be used before byte contents which will be used
* before FormUrlEncoded parameters.
*/
private byte[] myByteContents;
/**
* If the payload is a set of form encoded url parameters, these are the
* parameters to use.
*
* Only one of String/byte[]/form encoded url parameters can be used.
* String contents will be used before byte contents which will be used
* before FormUrlEncoded parameters.
*/
private Map<String, List<String>> myFormParams;
public HttpClientRequestParameters(String theUrl, @Nonnull RequestTypeEnum theRequestTypeEnum) {
myUrl = theUrl;
myRequestTypeEnum = theRequestTypeEnum;
}
public FhirContext getFhirContext() {
return myFhirContext;
}
public void setFhirContext(FhirContext theFhirContext) {
myFhirContext = theFhirContext;
}
public EncodingEnum getEncodingEnum() {
return myEncodingEnum;
}
public void setEncodingEnum(EncodingEnum theEncodingEnum) {
myEncodingEnum = theEncodingEnum;
}
public RequestTypeEnum getRequestTypeEnum() {
return myRequestTypeEnum;
}
public void setRequestTypeEnum(RequestTypeEnum theRequestTypeEnum) {
myRequestTypeEnum = theRequestTypeEnum;
}
public Map<String, List<String>> getParams() {
return myParams;
}
public void setParams(Map<String, List<String>> theParams) {
myParams = theParams;
}
public String getContentType() {
return myContentType;
}
public void setContentType(String theContentType) {
myContentType = theContentType;
}
public String getContents() {
return myContents;
}
public void setContents(String theContents) {
myContents = theContents;
}
public IBaseBinary getBaseBinary() {
return myBaseBinary;
}
public void setBaseBinary(IBaseBinary theBaseBinary) {
myBaseBinary = theBaseBinary;
}
public String getUrl() {
return myUrl;
}
public byte[] getByteContents() {
return myByteContents;
}
public void setByteContents(byte[] theByteContents) {
myByteContents = theByteContents;
}
public Map<String, List<String>> getFormParams() {
return myFormParams;
}
public void setFormParams(Map<String, List<String>> theFormParams) {
myFormParams = theFormParams;
}
}

View File

@ -29,6 +29,7 @@ import ca.uhn.fhir.rest.client.api.IHttpClient;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.impl.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.client.method.MethodUtil;
import ca.uhn.fhir.rest.param.HttpClientRequestParameters;
import okhttp3.Call;
import okhttp3.FormBody;
import okhttp3.MediaType;
@ -44,6 +45,7 @@ import static ca.uhn.fhir.okhttp.utils.UrlStringUtils.endsWith;
import static ca.uhn.fhir.okhttp.utils.UrlStringUtils.everythingAfterFirstQuestionMark;
import static ca.uhn.fhir.okhttp.utils.UrlStringUtils.hasQuestionMark;
import static ca.uhn.fhir.okhttp.utils.UrlStringUtils.withTrailingQuestionMarkRemoved;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
/**
* A Http Request based on OkHttp. This is an adapter around the class
@ -52,6 +54,7 @@ import static ca.uhn.fhir.okhttp.utils.UrlStringUtils.withTrailingQuestionMarkRe
* @author Matthew Clarke | matthew.clarke@orionhealth.com | Orion Health
*/
public class OkHttpRestfulClient implements IHttpClient {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OkHttpRestfulClient.class);
private Call.Factory myClient;
private StringBuilder myUrl;
@ -79,13 +82,15 @@ public class OkHttpRestfulClient implements IHttpClient {
@Override
public IHttpRequest createByteRequest(
FhirContext theContext, String theContents, String theContentType, EncodingEnum theEncoding) {
initBaseRequest(theContext, theEncoding, createPostBody(theContents, theContentType));
initBaseRequest(theContext, theEncoding, createPostBody(theContents, theContentType), RequestTypeEnum.POST);
return myRequest;
}
private void initBaseRequest(FhirContext theContext, EncodingEnum theEncoding, RequestBody body) {
private void initBaseRequest(
FhirContext theContext, EncodingEnum theEncoding, RequestBody body, RequestTypeEnum theRequestType) {
RequestTypeEnum requestType = theRequestType != null ? theRequestType : myRequestType;
String sanitisedUrl = withTrailingQuestionMarkRemoved(myUrl.toString());
myRequest = new OkHttpRestfulRequest(myClient, sanitisedUrl, myRequestType, body);
myRequest = new OkHttpRestfulRequest(myClient, sanitisedUrl, requestType, body);
addHeadersToRequest(myRequest, theEncoding, theContext);
}
@ -96,7 +101,7 @@ public class OkHttpRestfulClient implements IHttpClient {
@Override
public IHttpRequest createParamRequest(
FhirContext theContext, Map<String, List<String>> theParams, EncodingEnum theEncoding) {
initBaseRequest(theContext, theEncoding, getFormBodyFromParams(theParams));
initBaseRequest(theContext, theEncoding, getFormBodyFromParams(theParams), RequestTypeEnum.POST);
return myRequest;
}
@ -113,7 +118,11 @@ public class OkHttpRestfulClient implements IHttpClient {
@Override
public IHttpRequest createBinaryRequest(FhirContext theContext, IBaseBinary theBinary) {
initBaseRequest(theContext, null, createPostBody(theBinary.getContent(), theBinary.getContentType()));
initBaseRequest(
theContext,
null,
createPostBody(theBinary.getContent(), theBinary.getContentType()),
RequestTypeEnum.POST);
return myRequest;
}
@ -123,10 +132,55 @@ public class OkHttpRestfulClient implements IHttpClient {
@Override
public IHttpRequest createGetRequest(FhirContext theContext, EncodingEnum theEncoding) {
initBaseRequest(theContext, theEncoding, null);
initBaseRequest(theContext, theEncoding, null, RequestTypeEnum.GET);
return myRequest;
}
@Override
public IHttpRequest createRequest(HttpClientRequestParameters theParameters) {
RequestBody requestBody = null;
switch (theParameters.getRequestTypeEnum()) {
case POST:
case PUT:
if (theParameters.getFormParams() != null
&& !theParameters.getFormParams().isEmpty()) {
requestBody = getFormBodyFromParams(theParameters.getFormParams());
} else if (theParameters.getByteContents() != null) {
requestBody = createPostBody(theParameters.getByteContents(), theParameters.getContentType());
} else if (isNotBlank(theParameters.getContents())) {
requestBody = createPostBody(theParameters.getContents(), theParameters.getContentType());
} else if (theParameters.getBaseBinary() != null) {
requestBody =
createPostBody(theParameters.getBaseBinary().getContent(), theParameters.getContentType());
} else {
ourLog.debug(
"No body contents found for HTTP-{}",
theParameters.getRequestTypeEnum().name());
}
break;
}
initBaseRequest(
theParameters.getFhirContext(),
theParameters.getEncodingEnum(),
requestBody,
theParameters.getRequestTypeEnum());
return myRequest;
}
@Override
public void addHeadersToRequest(IHttpRequest theRequest, EncodingEnum theEncodingEnum, FhirContext theContext) {
// not needed for this client
}
@Override
public void setNewUrl(
StringBuilder theUrl, String theIfNoneExistString, Map<String, List<String>> theIfNoneExistParams) {
myUrl = theUrl;
myIfNoneExistString = theIfNoneExistString;
myIfNoneExistParams = theIfNoneExistParams;
}
private void addHeadersToRequest(
OkHttpRestfulRequest theHttpRequest, EncodingEnum theEncoding, FhirContext theContext) {
if (myHeaders != null) {

View File

@ -25,6 +25,7 @@ import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.api.Header;
import ca.uhn.fhir.rest.client.api.IHttpClient;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.param.HttpClientRequestParameters;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
@ -46,6 +47,8 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
/**
* A Http Client based on Apache. This is an adapter around the class
* {@link org.apache.http.client.HttpClient HttpClient}
@ -67,9 +70,35 @@ public class ApacheHttpClient extends BaseHttpClient implements IHttpClient {
this.myClient = theClient;
}
private HttpRequestBase constructRequestBase(HttpEntity theEntity) {
String url = myUrl.toString();
switch (myRequestType) {
private HttpEntity getEntityFromParameters(HttpClientRequestParameters theParameters) {
if (isNotBlank(theParameters.getContents())) {
return new ByteArrayEntity(theParameters.getContents().getBytes(Constants.CHARSET_UTF8));
} else if (theParameters.getByteContents() != null) {
return new ByteArrayEntity(theParameters.getByteContents());
} else if (theParameters.getFormParams() != null
&& !theParameters.getFormParams().isEmpty()) {
return entityFromFormParams(theParameters.getFormParams());
}
/*
* Could be a get request.
* This fallthrough is for legacy purposes; ideally we would use the request type
* but legacy clients don't have it defined always.
*/
return null;
}
private HttpRequestBase constructRequestBase(HttpClientRequestParameters theParameters, HttpEntity theEntity) {
// we default to the parameters request type;
// but if that's not provided (as in legacy clients case)
// we'll use the client's request type instead.
// one of these will definitely be provided though.
RequestTypeEnum requestTypeEnum = theParameters.getRequestTypeEnum();
if (requestTypeEnum == null) {
requestTypeEnum = myRequestType;
}
assert requestTypeEnum != null : "Request type required";
String url = theParameters.getUrl();
switch (requestTypeEnum) {
case DELETE:
return new HttpDelete(url);
case PATCH:
@ -92,6 +121,13 @@ public class ApacheHttpClient extends BaseHttpClient implements IHttpClient {
}
}
private HttpRequestBase constructRequestBase(HttpEntity theEntity) {
// request type for requests with bodies is POST
RequestTypeEnum requestType = myRequestType == null ? RequestTypeEnum.POST : myRequestType;
HttpClientRequestParameters parameters = new HttpClientRequestParameters(myUrl.toString(), requestType);
return constructRequestBase(parameters, theEntity);
}
private UrlEncodedFormEntity createFormEntity(List<NameValuePair> parameters) {
try {
return new UrlEncodedFormEntity(parameters, "UTF-8");
@ -100,6 +136,13 @@ public class ApacheHttpClient extends BaseHttpClient implements IHttpClient {
}
}
@Override
public IHttpRequest createRequest(HttpClientRequestParameters theParameters) {
myUrl = new StringBuilder(theParameters.getUrl());
HttpRequestBase request = constructRequestBase(theParameters, getEntityFromParameters(theParameters));
return new ApacheHttpRequest(myClient, request);
}
@Override
protected IHttpRequest createHttpRequest() {
return createHttpRequest((HttpEntity) null);
@ -123,6 +166,11 @@ public class ApacheHttpClient extends BaseHttpClient implements IHttpClient {
@Override
protected IHttpRequest createHttpRequest(Map<String, List<String>> theParams) {
UrlEncodedFormEntity entity = entityFromFormParams(theParams);
return createHttpRequest(entity);
}
private UrlEncodedFormEntity entityFromFormParams(Map<String, List<String>> theParams) {
List<NameValuePair> parameters = new ArrayList<>();
for (Entry<String, List<String>> nextParam : theParams.entrySet()) {
List<String> value = nextParam.getValue();
@ -132,7 +180,7 @@ public class ApacheHttpClient extends BaseHttpClient implements IHttpClient {
}
UrlEncodedFormEntity entity = createFormEntity(parameters);
return createHttpRequest(entity);
return entity;
}
@Override

View File

@ -50,8 +50,8 @@ import java.util.Map;
*/
public class ApacheHttpRequest extends BaseHttpRequest implements IHttpRequest {
private HttpClient myClient;
private HttpRequestBase myRequest;
private final HttpClient myClient;
private final HttpRequestBase myRequest;
public ApacheHttpRequest(HttpClient theClient, HttpRequestBase theApacheRequest) {
this.myClient = theClient;

View File

@ -87,13 +87,10 @@ public class ApacheRestfulClientFactory extends RestfulClientFactory {
public HttpClient getNativeHttpClient() {
if (myHttpClient == null) {
// TODO: Use of a deprecated method should be resolved.
RequestConfig defaultRequestConfig = RequestConfig.custom()
.setSocketTimeout(getSocketTimeout())
.setConnectTimeout(getConnectTimeout())
.setConnectionRequestTimeout(getConnectionRequestTimeout())
.setStaleConnectionCheckEnabled(true)
.setProxy(myProxy)
.build();
@ -106,6 +103,10 @@ public class ApacheRestfulClientFactory extends RestfulClientFactory {
new PoolingHttpClientConnectionManager(getConnectionTimeToLive(), TimeUnit.MILLISECONDS);
connectionManager.setMaxTotal(getPoolMaxTotal());
connectionManager.setDefaultMaxPerRoute(getPoolMaxPerRoute());
// default value for stale connection check
// this can be disabled (with a -ve value) if performance is bad
// but currently we are using the default for whatever reason
connectionManager.setValidateAfterInactivity(2000);
builder.setConnectionManager(connectionManager);
if (myProxy != null && isNotBlank(getProxyUsername()) && isNotBlank(getProxyPassword())) {

View File

@ -29,6 +29,7 @@ import ca.uhn.fhir.rest.client.api.IHttpClient;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.impl.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.client.method.MethodUtil;
import ca.uhn.fhir.rest.param.HttpClientRequestParameters;
import org.hl7.fhir.instance.model.api.IBaseBinary;
import java.util.List;
@ -37,10 +38,10 @@ import java.util.Map;
public abstract class BaseHttpClient implements IHttpClient {
private final List<Header> myHeaders;
private final Map<String, List<String>> myIfNoneExistParams;
private final String myIfNoneExistString;
protected final RequestTypeEnum myRequestType;
protected final StringBuilder myUrl;
private Map<String, List<String>> myIfNoneExistParams;
private String myIfNoneExistString;
protected RequestTypeEnum myRequestType;
protected StringBuilder myUrl;
/**
* Constructor
@ -58,6 +59,14 @@ public abstract class BaseHttpClient implements IHttpClient {
this.myHeaders = theHeaders;
}
@Override
public void setNewUrl(
StringBuilder theUrl, String theIfNoneExistString, Map<String, List<String>> theIfNoneExistParams) {
myUrl = theUrl;
myIfNoneExistString = theIfNoneExistString;
myIfNoneExistParams = theIfNoneExistParams;
}
private void addHeaderIfNoneExist(IHttpRequest result) {
if (myIfNoneExistParams != null) {
StringBuilder b = newHeaderBuilder(myUrl);
@ -108,11 +117,12 @@ public abstract class BaseHttpClient implements IHttpClient {
@Override
public IHttpRequest createGetRequest(FhirContext theContext, EncodingEnum theEncoding) {
IHttpRequest retVal = createHttpRequest();
IHttpRequest retVal = createRequest(new HttpClientRequestParameters(myUrl.toString(), RequestTypeEnum.GET));
addHeadersToRequest(retVal, theEncoding, theContext);
return retVal;
}
@Deprecated
protected abstract IHttpRequest createHttpRequest();
protected abstract IHttpRequest createHttpRequest(byte[] theContent);

View File

@ -51,6 +51,8 @@ import ca.uhn.fhir.rest.client.method.HttpGetClientInvocation;
import ca.uhn.fhir.rest.client.method.IClientResponseHandler;
import ca.uhn.fhir.rest.client.method.IClientResponseHandlerHandlesBinary;
import ca.uhn.fhir.rest.client.method.MethodUtil;
import ca.uhn.fhir.rest.client.model.AsHttpRequestParams;
import ca.uhn.fhir.rest.client.model.InvokeClientParameters;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.system.HapiSystemProperties;
@ -88,7 +90,7 @@ public abstract class BaseClient implements IRestfulClient {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseClient.class);
private final IHttpClient myClient;
protected IHttpClient myClient;
private final RestfulClientFactory myFactory;
private final String myUrlBase;
private boolean myDontValidateConformance;
@ -267,6 +269,32 @@ public abstract class BaseClient implements IRestfulClient {
CacheControlDirective theCacheControlDirective,
String theCustomAcceptHeader,
Map<String, List<String>> theCustomHeaders) {
return this.invokeClient(new InvokeClientParameters<T>()
.setContext(theContext)
.setBinding(binding)
.setClientInvocation(clientInvocation)
.setEncoding(theEncoding)
.setPrettyPrint(thePrettyPrint)
.setTheLogRequestAndResponse(theLogRequestAndResponse)
.setSummaryMode(theSummaryMode)
.setSubsetElements(theSubsetElements)
.setCacheControlDirective(theCacheControlDirective)
.setCustomAcceptHeader(theCustomAcceptHeader)
.setCustomHeaders(theCustomHeaders));
}
protected <T> T invokeClient(InvokeClientParameters<T> theParameters) {
FhirContext theContext = theParameters.getContext();
IClientResponseHandler<T> binding = theParameters.getBinding();
BaseHttpClientInvocation clientInvocation = theParameters.getClientInvocation();
EncodingEnum theEncoding = theParameters.getEncoding();
Boolean thePrettyPrint = theParameters.getPrettyPrint();
boolean theLogRequestAndResponse = theParameters.isTheLogRequestAndResponse();
SummaryEnum theSummaryMode = theParameters.getSummaryMode();
Set<String> theSubsetElements = theParameters.getSubsetElements();
CacheControlDirective theCacheControlDirective = theParameters.getCacheControlDirective();
String theCustomAcceptHeader = theParameters.getCustomAcceptHeader();
Map<String, List<String>> theCustomHeaders = theParameters.getCustomHeaders();
if (!myDontValidateConformance) {
myFactory.validateServerBaseIfConfiguredToDoSo(myUrlBase, myClient, this);
@ -299,7 +327,7 @@ public abstract class BaseClient implements IRestfulClient {
params.put(Constants.PARAM_PRETTY, Collections.singletonList(Constants.PARAM_PRETTY_VALUE_TRUE));
}
if (theSubsetElements != null && theSubsetElements.isEmpty() == false) {
if (theSubsetElements != null && !theSubsetElements.isEmpty()) {
params.put(
Constants.PARAM_ELEMENTS, Collections.singletonList(StringUtils.join(theSubsetElements, ',')));
}
@ -309,7 +337,13 @@ public abstract class BaseClient implements IRestfulClient {
encoding = theEncoding;
}
httpRequest = clientInvocation.asHttpRequest(myUrlBase, params, encoding, thePrettyPrint);
AsHttpRequestParams asHttpRequestParams = new AsHttpRequestParams()
.setExtraParams(params)
.setUrlBase(myUrlBase)
.setPrettyPrint(thePrettyPrint)
.setEncodingEnum(encoding)
.setClient(myClient);
httpRequest = clientInvocation.asHttpRequest(asHttpRequestParams);
if (isNotBlank(theCustomAcceptHeader)) {
httpRequest.removeHeaders(Constants.HEADER_ACCEPT);

View File

@ -26,6 +26,9 @@ import ca.uhn.fhir.rest.client.api.Header;
import ca.uhn.fhir.rest.client.api.IHttpClient;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.api.IRestfulClientFactory;
import ca.uhn.fhir.rest.client.model.AsHttpRequestParams;
import ca.uhn.fhir.rest.client.model.CreateRequestParameters;
import ca.uhn.fhir.rest.param.HttpClientRequestParameters;
import ca.uhn.fhir.util.UrlUtil;
import java.util.ArrayList;
@ -48,7 +51,7 @@ public abstract class BaseHttpClientInvocation {
}
/**
* Create an HTTP request out of this client request
* Create an HTTP request out of this client request.
*
* @param theUrlBase
* The FHIR server base url (with a trailing "/")
@ -57,28 +60,67 @@ public abstract class BaseHttpClientInvocation {
* @param theEncoding
* The encoding to use for any serialized content sent to the
* server
*
* @deprecated Use/Override {@link #asHttpRequest(AsHttpRequestParams)} instead.
*/
@Deprecated(since = "7.5.0")
public abstract IHttpRequest asHttpRequest(
String theUrlBase,
Map<String, List<String>> theExtraParams,
EncodingEnum theEncoding,
Boolean thePrettyPrint);
public IHttpRequest asHttpRequest(AsHttpRequestParams theParams) {
// default passes back to deprecated;
// this is to allow existing clients not to break
return asHttpRequest(
theParams.getUrlBase(),
theParams.getExtraParams(),
theParams.getEncodingEnum(),
theParams.getPrettyPrint());
}
/**
* Use {@link #createHttpRequest(CreateRequestParameters)}
*/
@Deprecated
protected IHttpRequest createHttpRequest(String theUrl, EncodingEnum theEncoding, RequestTypeEnum theRequestType) {
return createHttpRequest(new CreateRequestParameters()
.setUrl(theUrl)
.setEncodingEnum(theEncoding)
.setRequestTypeEnum(theRequestType));
}
/**
* Create an HTTP request for the given url, encoding and request-type
*
* @param theUrl
* The complete FHIR url to which the http request will be sent
* @param theEncoding
* The encoding to use for any serialized content sent to the
* server
* @param theRequestType
* the type of HTTP request (GET, DELETE, ..)
*/
protected IHttpRequest createHttpRequest(String theUrl, EncodingEnum theEncoding, RequestTypeEnum theRequestType) {
IHttpClient httpClient = getRestfulClientFactory()
.getHttpClient(new StringBuilder(theUrl), null, null, theRequestType, myHeaders);
return httpClient.createGetRequest(getContext(), theEncoding);
protected IHttpRequest createHttpRequest(CreateRequestParameters theParameters) {
IHttpClient httpClient;
if (theParameters.getClient() != null) {
// reuse existing client
httpClient = theParameters.getClient();
httpClient.setNewUrl(new StringBuilder(theParameters.getUrl()), null, null);
} else {
// make a new client
httpClient = getRestfulClientFactory()
.getHttpClient(
new StringBuilder(theParameters.getUrl()),
null,
null,
theParameters.getRequestTypeEnum(),
myHeaders);
}
HttpClientRequestParameters clientRequestParameters =
new HttpClientRequestParameters(theParameters.getUrl(), theParameters.getRequestTypeEnum());
clientRequestParameters.setEncodingEnum(theParameters.getEncodingEnum());
clientRequestParameters.setFhirContext(getContext());
IHttpRequest request = httpClient.createRequest(clientRequestParameters);
for (Header h : getHeaders()) {
request.addHeader(h.getName(), h.getValue());
}
httpClient.addHeadersToRequest(request, theParameters.getEncodingEnum(), getContext());
return request;
}
/**

View File

@ -69,6 +69,8 @@ import ca.uhn.fhir.rest.client.method.SearchMethodBinding;
import ca.uhn.fhir.rest.client.method.SortParameter;
import ca.uhn.fhir.rest.client.method.TransactionMethodBinding;
import ca.uhn.fhir.rest.client.method.ValidateMethodBindingDstu2Plus;
import ca.uhn.fhir.rest.client.model.AsHttpRequestParams;
import ca.uhn.fhir.rest.client.model.InvokeClientParameters;
import ca.uhn.fhir.rest.gclient.IBaseQuery;
import ca.uhn.fhir.rest.gclient.IClientExecutable;
import ca.uhn.fhir.rest.gclient.ICreate;
@ -179,7 +181,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
private static final String I18N_INCOMPLETE_URI_FOR_READ = GenericClient.class.getName() + ".incompleteUriForRead";
private static final String I18N_NO_VERSION_ID_FOR_VREAD = GenericClient.class.getName() + ".noVersionIdForVread";
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(GenericClient.class);
private FhirContext myContext;
private final FhirContext myContext;
private IHttpRequest myLastRequest;
private boolean myLogRequestAndResponse;
@ -194,17 +196,17 @@ public class GenericClient extends BaseClient implements IGenericClient {
@Override
public IFetchConformanceUntyped capabilities() {
return new FetchConformanceInternal();
return new FetchConformanceInternal(myClient);
}
@Override
public ICreate create() {
return new CreateInternal();
return new CreateInternal(myClient);
}
@Override
public IDelete delete() {
return new DeleteInternal();
return new DeleteInternal(myClient);
}
private <T extends IBaseResource> T doReadOrVRead(
@ -240,8 +242,13 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
}
if (isKeepResponses()) {
myLastRequest = invocation.asHttpRequest(
getServerBase(), createExtraParams(theCustomAcceptHeaderValue), getEncoding(), isPrettyPrint());
AsHttpRequestParams params = new AsHttpRequestParams()
.setExtraParams(createExtraParams(theCustomAcceptHeaderValue))
.setUrlBase(getServerBase())
.setClient(myClient)
.setEncodingEnum(getEncoding())
.setPrettyPrint(isPrettyPrint());
myLastRequest = invocation.asHttpRequest(params);
}
if (theIfVersionMatches != null) {
@ -253,18 +260,17 @@ public class GenericClient extends BaseClient implements IGenericClient {
new ResourceResponseHandler<>(theType, (Class<? extends IBaseResource>) null, id, allowHtmlResponse);
if (theNotModifiedHandler == null) {
return invokeClient(
myContext,
binding,
invocation,
theEncoding,
thePrettyPrint,
myLogRequestAndResponse,
theSummary,
theSubsetElements,
null,
theCustomAcceptHeaderValue,
theCustomHeaders);
return invokeClient(new InvokeClientParameters<T>()
.setContext(myContext)
.setBinding(binding)
.setClientInvocation(invocation)
.setEncoding(theEncoding)
.setPrettyPrint(thePrettyPrint)
.setTheLogRequestAndResponse(myLogRequestAndResponse)
.setSummaryMode(theSummary)
.setSubsetElements(theSubsetElements)
.setCustomAcceptHeader(theCustomAcceptHeaderValue)
.setCustomHeaders(theCustomHeaders));
}
try {
return invokeClient(
@ -286,7 +292,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
@Override
public IFetchConformanceUntyped fetchConformance() {
return new FetchConformanceInternal();
return new FetchConformanceInternal(myClient);
}
@Override
@ -319,7 +325,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
@Override
public IHistory history() {
return new HistoryInternal();
return new HistoryInternal(myClient);
}
/**
@ -340,27 +346,27 @@ public class GenericClient extends BaseClient implements IGenericClient {
@Override
public IGetPage loadPage() {
return new LoadPageInternal();
return new LoadPageInternal(myClient);
}
@Override
public IMeta meta() {
return new MetaInternal();
return new MetaInternal(myClient);
}
@Override
public IOperation operation() {
return new OperationInternal();
return new OperationInternal(myClient);
}
@Override
public IPatch patch() {
return new PatchInternal();
return new PatchInternal(myClient);
}
@Override
public IRead read() {
return new ReadInternal();
return new ReadInternal(myClient);
}
@Override
@ -395,7 +401,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
@SuppressWarnings({"rawtypes", "unchecked"})
@Override
public IUntypedQuery search() {
return new SearchInternal();
return new SearchInternal<>(myClient);
}
private String toResourceName(Class<? extends IBaseResource> theType) {
@ -404,12 +410,12 @@ public class GenericClient extends BaseClient implements IGenericClient {
@Override
public ITransaction transaction() {
return new TransactionInternal();
return new TransactionInternal(myClient);
}
@Override
public IUpdate update() {
return new UpdateInternal();
return new UpdateInternal(myClient);
}
@Override
@ -432,7 +438,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
@Override
public IValidate validate() {
return new ValidateInternal();
return new ValidateInternal(myClient);
}
@Override
@ -484,6 +490,12 @@ public class GenericClient extends BaseClient implements IGenericClient {
private boolean myQueryLogRequestAndResponse;
private Set<String> mySubsetElements;
private final IHttpClient myPreservedClient;
public BaseClientExecutable(IHttpClient theClient) {
myPreservedClient = theClient;
}
public String getCustomAcceptHeaderValue() {
return myCustomAcceptHeaderValue;
}
@ -579,21 +591,27 @@ public class GenericClient extends BaseClient implements IGenericClient {
IClientResponseHandler<Z> theHandler,
BaseHttpClientInvocation theInvocation) {
if (isKeepResponses()) {
myLastRequest = theInvocation.asHttpRequest(getServerBase(), theParams, getEncoding(), myPrettyPrint);
myLastRequest = theInvocation.asHttpRequest(new AsHttpRequestParams()
.setUrlBase(getServerBase())
.setClient(myClient)
.setExtraParams(theParams)
.setEncodingEnum(getParamEncoding())
.setPrettyPrint(myPrettyPrint));
}
Z resp = invokeClient(
myContext,
theHandler,
theInvocation,
myParamEncoding,
myPrettyPrint,
myQueryLogRequestAndResponse || myLogRequestAndResponse,
mySummaryMode,
mySubsetElements,
myCacheControlDirective,
myCustomAcceptHeaderValue,
myCustomHeaderValues);
InvokeClientParameters<Z> params = new InvokeClientParameters<Z>()
.setContext(myContext)
.setBinding(theHandler)
.setClientInvocation(theInvocation)
.setEncoding(myParamEncoding)
.setPrettyPrint(myPrettyPrint)
.setTheLogRequestAndResponse(myQueryLogRequestAndResponse || myLogRequestAndResponse)
.setSummaryMode(mySummaryMode)
.setSubsetElements(mySubsetElements)
.setCacheControlDirective(myCacheControlDirective)
.setCustomAcceptHeader(myCustomAcceptHeaderValue)
.setCustomHeaders(myCustomHeaderValues);
Z resp = invokeClient(params);
return resp;
}
@ -643,7 +661,11 @@ public class GenericClient extends BaseClient implements IGenericClient {
EXEC extends IClientExecutable<?, OUTPUT>, QUERY extends IBaseQuery<QUERY>, OUTPUT>
extends BaseClientExecutable<EXEC, OUTPUT> implements IBaseQuery<QUERY> {
private Map<String, List<String>> myParams = new LinkedHashMap<>();
private final Map<String, List<String>> myParams = new LinkedHashMap<>();
public BaseSearch(IHttpClient theClient) {
super(theClient);
}
@Override
public QUERY and(ICriterion<?> theCriterion) {
@ -710,6 +732,10 @@ public class GenericClient extends BaseClient implements IGenericClient {
private String myResourceBody;
private String mySearchUrl;
public CreateInternal(IHttpClient theClient) {
super(theClient);
}
@Override
public ICreateWithQuery conditional() {
myConditional = true;
@ -780,9 +806,12 @@ public class GenericClient extends BaseClient implements IGenericClient {
private String mySearchUrl;
private DeleteCascadeModeEnum myCascadeMode;
public DeleteInternal(IHttpClient theClient) {
super(theClient);
}
@Override
public MethodOutcome execute() {
Map<String, List<String>> additionalParams = new HashMap<>();
if (myCascadeMode != null) {
switch (myCascadeMode) {
@ -888,6 +917,10 @@ public class GenericClient extends BaseClient implements IGenericClient {
implements IFetchConformanceUntyped, IFetchConformanceTyped {
private RuntimeResourceDefinition myType;
public FetchConformanceInternal(IHttpClient theClient) {
super(theClient);
}
@Override
public Object execute() {
ResourceResponseHandler binding = new ResourceResponseHandler(myType.getImplementingClass());
@ -912,12 +945,13 @@ public class GenericClient extends BaseClient implements IGenericClient {
private class GetPageInternal extends BaseClientExecutable<IGetPageTyped<Object>, Object>
implements IGetPageTyped<Object> {
private Class<? extends IBaseBundle> myBundleType;
private String myUrl;
private final Class<? extends IBaseBundle> myBundleType;
private final String myUrl;
private PagingHttpMethodEnum myPagingHttpMethod = PagingHttpMethodEnum.GET;
public GetPageInternal(String theUrl, Class<? extends IBaseBundle> theBundleType) {
public GetPageInternal(IHttpClient theClient, String theUrl, Class<? extends IBaseBundle> theBundleType) {
super(theClient);
myUrl = theUrl;
myBundleType = theBundleType;
}
@ -947,6 +981,10 @@ public class GenericClient extends BaseClient implements IGenericClient {
private Class<? extends IBaseResource> myType;
private DateRangeParam myAt;
public HistoryInternal(IHttpClient theClient) {
super(theClient);
}
@SuppressWarnings("unchecked")
@Override
public IHistoryTyped andReturnBundle(Class theType) {
@ -1056,10 +1094,16 @@ public class GenericClient extends BaseClient implements IGenericClient {
private static final String PREVIOUS = "previous";
private String myPageUrl;
private IHttpClient myClient;
public LoadPageInternal(IHttpClient theClient) {
myClient = theClient;
}
@Override
public <T extends IBaseBundle> IGetPageTyped andReturnBundle(Class<T> theBundleType) {
Validate.notNull(theBundleType, "theBundleType must not be null");
return new GetPageInternal(myPageUrl, theBundleType);
return new GetPageInternal(myClient, myPageUrl, theBundleType);
}
@Override
@ -1129,6 +1173,10 @@ public class GenericClient extends BaseClient implements IGenericClient {
private String myOnType;
private MetaOperation myOperation;
public MetaInternal(IHttpClient theClient) {
super(theClient);
}
@Override
public IMetaAddOrDeleteUnsourced add() {
myOperation = MetaOperation.ADD;
@ -1303,6 +1351,10 @@ public class GenericClient extends BaseClient implements IGenericClient {
private boolean myUseHttpGet;
private boolean myReturnMethodOutcome;
public OperationInternal(IHttpClient theClient) {
super(theClient);
}
@SuppressWarnings("unchecked")
private void addParam(String theName, IBase theValue) {
BaseRuntimeChildDefinition parameterChild = myParametersDef.getChildByName("parameter");
@ -1716,6 +1768,10 @@ public class GenericClient extends BaseClient implements IGenericClient {
private class PatchInternal extends BaseSearch<IPatchExecutable, IPatchWithQueryTyped, MethodOutcome>
implements IPatch, IPatchWithBody, IPatchExecutable, IPatchWithQuery, IPatchWithQueryTyped {
public PatchInternal(IHttpClient theClient) {
super(theClient);
}
private boolean myConditional;
private IIdType myId;
private String myPatchBody;
@ -1845,6 +1901,10 @@ public class GenericClient extends BaseClient implements IGenericClient {
private ICallable myNotModifiedHandler;
private RuntimeResourceDefinition myType;
public ReadInternal(IHttpClient theClient) {
super(theClient);
}
@Override
public Object execute() { // AAA
if (myId.hasVersionIdPart()) {
@ -2038,7 +2098,8 @@ public class GenericClient extends BaseClient implements IGenericClient {
private List<TokenParam> myTags = new ArrayList<>();
private SearchTotalModeEnum myTotalMode;
public SearchInternal() {
public SearchInternal(IHttpClient theClient) {
super(theClient);
myResourceType = null;
myResourceName = null;
mySearchUrl = null;
@ -2097,7 +2158,6 @@ public class GenericClient extends BaseClient implements IGenericClient {
@Override
public OUTPUT execute() {
Map<String, List<String>> params = getParamMap();
for (TokenParam next : myTags) {
@ -2348,22 +2408,39 @@ public class GenericClient extends BaseClient implements IGenericClient {
private EncodingEnum myRawBundleEncoding;
private List<? extends IBaseResource> myResources;
public TransactionExecutable(IBaseBundle theBundle) {
public TransactionExecutable(IHttpClient theClient, IBaseBundle theBundle) {
this(theClient, null, theBundle, null);
}
public TransactionExecutable(IHttpClient theClient, List<? extends IBaseResource> theResources) {
this(theClient, null, null, theResources);
}
public TransactionExecutable(IHttpClient theClient, String theStrBundle) {
this(theClient, theStrBundle, null, null);
}
public TransactionExecutable(
IHttpClient theClient,
String theStrBundle,
IBaseBundle theBundle,
List<? extends IBaseResource> theResources) {
super(theClient);
if (theBundle != null) {
myBaseBundle = theBundle;
}
public TransactionExecutable(List<? extends IBaseResource> theResources) {
myResources = theResources;
}
public TransactionExecutable(String theBundle) {
myRawBundle = theBundle;
if (theStrBundle != null) {
myRawBundle = theStrBundle;
myRawBundleEncoding = EncodingEnum.detectEncodingNoDefault(myRawBundle);
if (myRawBundleEncoding == null) {
throw new IllegalArgumentException(Msg.code(1395)
+ myContext.getLocalizer().getMessage(GenericClient.class, "cantDetermineRequestType"));
}
}
if (theResources != null) {
myResources = theResources;
}
}
@SuppressWarnings({"unchecked", "rawtypes"})
@Override
@ -2402,16 +2479,22 @@ public class GenericClient extends BaseClient implements IGenericClient {
private final class TransactionInternal implements ITransaction {
private IHttpClient myClient;
public TransactionInternal(IHttpClient theClient) {
myClient = theClient;
}
@Override
public ITransactionTyped<String> withBundle(String theBundle) {
Validate.notBlank(theBundle, "theBundle must not be null");
return new TransactionExecutable<String>(theBundle);
return new TransactionExecutable<String>(myClient, theBundle);
}
@Override
public <T extends IBaseBundle> ITransactionTyped<T> withBundle(T theBundle) {
Validate.notNull(theBundle, "theBundle must not be null");
return new TransactionExecutable<T>(theBundle);
return new TransactionExecutable<T>(myClient, theBundle);
}
@Override
@ -2432,7 +2515,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
}
return new TransactionExecutable<>(theResources);
return new TransactionExecutable<>(myClient, theResources);
}
}
@ -2447,6 +2530,10 @@ public class GenericClient extends BaseClient implements IGenericClient {
private String mySearchUrl;
private boolean myIsHistoryRewrite;
public UpdateInternal(IHttpClient theClient) {
super(theClient);
}
@Override
public IUpdateTyped historyRewrite() {
myIsHistoryRewrite = true;
@ -2563,6 +2650,10 @@ public class GenericClient extends BaseClient implements IGenericClient {
implements IValidate, IValidateUntyped {
private IBaseResource myResource;
public ValidateInternal(IHttpClient theClient) {
super(theClient);
}
@Override
public MethodOutcome execute() {
BaseHttpClientInvocation invocation =

View File

@ -24,12 +24,16 @@ import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.api.Header;
import ca.uhn.fhir.rest.client.api.IHttpClient;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.impl.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.client.model.AsHttpRequestParams;
import ca.uhn.fhir.rest.param.HttpClientRequestParameters;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IBaseBinary;
@ -106,6 +110,20 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
EncodingEnum theEncoding,
Boolean thePrettyPrint)
throws DataFormatException {
return asHttpRequest(new AsHttpRequestParams()
.setUrlBase(theUrlBase)
.setExtraParams(theExtraParams)
.setEncodingEnum(theEncoding)
.setPrettyPrint(thePrettyPrint));
}
@Override
public IHttpRequest asHttpRequest(AsHttpRequestParams theParams) {
String theUrlBase = theParams.getUrlBase();
Map<String, List<String>> theExtraParams = theParams.getExtraParams();
EncodingEnum theEncoding = theParams.getEncodingEnum();
Boolean thePrettyPrint = theParams.getPrettyPrint();
StringBuilder url = new StringBuilder();
if (myUrlPath == null) {
@ -121,8 +139,18 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
}
appendExtraParamsWithQuestionMark(theExtraParams, url, url.indexOf("?") == -1);
IHttpClient httpClient = getRestfulClientFactory()
IHttpClient httpClient;
if (theParams.getClient() != null) {
// use the provided one
httpClient = theParams.getClient();
// update the url to the one we want (in case the
// previous client did not have the correct one
httpClient.setNewUrl(url, myIfNoneExistString, myIfNoneExistParams);
} else {
// make a new one
httpClient = getRestfulClientFactory()
.getHttpClient(url, myIfNoneExistParams, myIfNoneExistString, getRequestType(), getHeaders());
}
if (myResource != null && IBaseBinary.class.isAssignableFrom(myResource.getClass())) {
IBaseBinary binary = (IBaseBinary) myResource;
@ -140,12 +168,25 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
}
if (myParams != null) {
return httpClient.createParamRequest(getContext(), myParams, encoding);
IHttpRequest request = httpClient.createParamRequest(getContext(), myParams, encoding);
return request;
}
encoding = ObjectUtils.defaultIfNull(encoding, EncodingEnum.JSON);
String contents = encodeContents(thePrettyPrint, encoding);
String contentType = getContentType(encoding);
return httpClient.createByteRequest(getContext(), contents, contentType, encoding);
HttpClientRequestParameters parameters = new HttpClientRequestParameters(url.toString(), getRequestType());
parameters.setContents(contents);
parameters.setContentType(contentType);
parameters.setFhirContext(getContext());
parameters.setEncodingEnum(encoding);
parameters.setRequestTypeEnum(getRequestType());
IHttpRequest request = httpClient.createRequest(parameters);
for (Header header : getHeaders()) {
request.addHeader(header.getName(), header.getValue());
}
httpClient.addHeadersToRequest(request, encoding, parameters.getFhirContext());
request.addHeader(Constants.HEADER_CONTENT_TYPE, contentType + Constants.HEADER_SUFFIX_CT_UTF_8);
return request;
}
private String getContentType(EncodingEnum encoding) {

View File

@ -24,6 +24,8 @@ import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.impl.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.client.model.AsHttpRequestParams;
import ca.uhn.fhir.rest.client.model.CreateRequestParameters;
import org.hl7.fhir.instance.model.api.IIdType;
import java.util.List;
@ -31,8 +33,8 @@ import java.util.Map;
public class HttpDeleteClientInvocation extends BaseHttpClientInvocation {
private String myUrlPath;
private Map<String, List<String>> myParams;
private final String myUrlPath;
private final Map<String, List<String>> myParams;
public HttpDeleteClientInvocation(
FhirContext theContext, IIdType theId, Map<String, List<String>> theAdditionalParams) {
@ -54,6 +56,20 @@ public class HttpDeleteClientInvocation extends BaseHttpClientInvocation {
Map<String, List<String>> theExtraParams,
EncodingEnum theEncoding,
Boolean thePrettyPrint) {
return asHttpRequest(new AsHttpRequestParams()
.setUrlBase(theUrlBase)
.setExtraParams(theExtraParams)
.setEncodingEnum(theEncoding)
.setPrettyPrint(thePrettyPrint));
}
@Override
public IHttpRequest asHttpRequest(AsHttpRequestParams theParams) {
String theUrlBase = theParams.getUrlBase();
Map<String, List<String>> theExtraParams = theParams.getExtraParams();
EncodingEnum theEncoding = theParams.getEncodingEnum();
Boolean thePrettyPrint = theParams.getPrettyPrint();
StringBuilder b = new StringBuilder();
b.append(theUrlBase);
if (!theUrlBase.endsWith("/")) {
@ -64,6 +80,11 @@ public class HttpDeleteClientInvocation extends BaseHttpClientInvocation {
appendExtraParamsWithQuestionMark(myParams, b, b.indexOf("?") == -1);
appendExtraParamsWithQuestionMark(theExtraParams, b, b.indexOf("?") == -1);
return createHttpRequest(b.toString(), theEncoding, RequestTypeEnum.DELETE);
CreateRequestParameters requestParameters = new CreateRequestParameters();
requestParameters.setClient(theParams.getClient());
requestParameters.setRequestTypeEnum(RequestTypeEnum.DELETE);
requestParameters.setEncodingEnum(theEncoding);
requestParameters.setUrl(b.toString());
return createHttpRequest(requestParameters);
}
}

View File

@ -25,6 +25,8 @@ import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.api.UrlSourceEnum;
import ca.uhn.fhir.rest.client.impl.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.client.model.AsHttpRequestParams;
import ca.uhn.fhir.rest.client.model.CreateRequestParameters;
import ca.uhn.fhir.util.UrlUtil;
import org.apache.commons.lang3.StringUtils;
@ -87,6 +89,18 @@ public class HttpGetClientInvocation extends BaseHttpClientInvocation {
Map<String, List<String>> theExtraParams,
EncodingEnum theEncoding,
Boolean thePrettyPrint) {
return asHttpRequest(new AsHttpRequestParams()
.setUrlBase(theUrlBase)
.setExtraParams(theExtraParams)
.setPrettyPrint(thePrettyPrint)
.setEncodingEnum(theEncoding));
}
@Override
public IHttpRequest asHttpRequest(AsHttpRequestParams theAsHttpRequestParams) {
String theUrlBase = theAsHttpRequestParams.getUrlBase();
Map<String, List<String>> theExtraParams = theAsHttpRequestParams.getExtraParams();
EncodingEnum theEncoding = theAsHttpRequestParams.getEncodingEnum();
StringBuilder b = new StringBuilder();
if (!myUrlPath.contains("://")) {
@ -110,7 +124,12 @@ public class HttpGetClientInvocation extends BaseHttpClientInvocation {
appendExtraParamsWithQuestionMark(theExtraParams, b, first);
IHttpRequest retVal = super.createHttpRequest(b.toString(), theEncoding, RequestTypeEnum.GET);
CreateRequestParameters createRequestParameters = new CreateRequestParameters()
.setRequestTypeEnum(RequestTypeEnum.GET)
.setEncodingEnum(theEncoding)
.setUrl(b.toString())
.setClient(theAsHttpRequestParams.getClient());
IHttpRequest retVal = super.createHttpRequest(createRequestParameters);
retVal.setUrlSource(myUrlSource);
return retVal;

View File

@ -20,11 +20,16 @@
package ca.uhn.fhir.rest.client.method;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.api.Header;
import ca.uhn.fhir.rest.client.api.IHttpClient;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.impl.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.client.model.AsHttpRequestParams;
import ca.uhn.fhir.rest.client.model.CreateRequestParameters;
import ca.uhn.fhir.rest.param.HttpClientRequestParameters;
import org.hl7.fhir.instance.model.api.IIdType;
import java.util.List;
@ -32,10 +37,10 @@ import java.util.Map;
public class HttpPatchClientInvocation extends BaseHttpClientInvocation {
private String myUrlPath;
private final String myUrlPath;
private Map<String, List<String>> myParams;
private String myContents;
private String myContentType;
private final String myContents;
private final String myContentType;
public HttpPatchClientInvocation(FhirContext theContext, IIdType theId, String theContentType, String theContents) {
super(theContext);
@ -58,6 +63,20 @@ public class HttpPatchClientInvocation extends BaseHttpClientInvocation {
Map<String, List<String>> theExtraParams,
EncodingEnum theEncoding,
Boolean thePrettyPrint) {
return asHttpRequest(new AsHttpRequestParams()
.setUrlBase(theUrlBase)
.setExtraParams(theExtraParams)
.setEncodingEnum(theEncoding)
.setPrettyPrint(thePrettyPrint));
}
@Override
public IHttpRequest asHttpRequest(AsHttpRequestParams theParams) {
String theUrlBase = theParams.getUrlBase();
Map<String, List<String>> theExtraParams = theParams.getExtraParams();
EncodingEnum theEncoding = theParams.getEncodingEnum();
Boolean thePrettyPrint = theParams.getPrettyPrint();
StringBuilder b = new StringBuilder();
b.append(theUrlBase);
if (!theUrlBase.endsWith("/")) {
@ -68,7 +87,12 @@ public class HttpPatchClientInvocation extends BaseHttpClientInvocation {
appendExtraParamsWithQuestionMark(myParams, b, b.indexOf("?") == -1);
appendExtraParamsWithQuestionMark(theExtraParams, b, b.indexOf("?") == -1);
return createHttpRequest(b.toString(), theEncoding, RequestTypeEnum.PATCH);
CreateRequestParameters requestParameters = new CreateRequestParameters();
requestParameters.setClient(theParams.getClient());
requestParameters.setUrl(b.toString());
requestParameters.setEncodingEnum(theEncoding);
requestParameters.setRequestTypeEnum(RequestTypeEnum.PATCH);
return createHttpRequest(requestParameters);
}
@Override
@ -77,4 +101,34 @@ public class HttpPatchClientInvocation extends BaseHttpClientInvocation {
.getHttpClient(new StringBuilder(theUrl), null, null, theRequestType, getHeaders());
return httpClient.createByteRequest(getContext(), myContents, myContentType, null);
}
@Override
protected IHttpRequest createHttpRequest(CreateRequestParameters theParameters) {
IHttpClient client;
if (theParameters.getClient() == null) {
client = getRestfulClientFactory()
.getHttpClient(
new StringBuilder(theParameters.getUrl()),
null,
null,
theParameters.getRequestTypeEnum(),
getHeaders());
} else {
client = theParameters.getClient();
client.setNewUrl(new StringBuilder(theParameters.getUrl()), null, null);
}
HttpClientRequestParameters params =
new HttpClientRequestParameters(theParameters.getUrl(), RequestTypeEnum.PATCH);
params.setContents(myContents);
params.setContentType(myContentType);
params.setFhirContext(getContext());
IHttpRequest req = client.createRequest(params);
for (Header h : getHeaders()) {
req.addHeader(h.getName(), h.getValue());
}
client.addHeadersToRequest(req, theParameters.getEncodingEnum(), getContext());
req.addHeader(Constants.HEADER_CONTENT_TYPE, params.getContentType() + Constants.HEADER_SUFFIX_CT_UTF_8);
return req;
}
}

View File

@ -25,6 +25,8 @@ import ca.uhn.fhir.rest.api.PagingHttpMethodEnum;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.api.UrlSourceEnum;
import ca.uhn.fhir.rest.client.impl.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.client.model.AsHttpRequestParams;
import ca.uhn.fhir.rest.client.model.CreateRequestParameters;
import java.util.List;
import java.util.Map;
@ -34,7 +36,7 @@ public class HttpSimpleClientInvocation extends BaseHttpClientInvocation {
private final String myUrl;
private UrlSourceEnum myUrlSource = UrlSourceEnum.EXPLICIT;
private PagingHttpMethodEnum myPagingHttpMethod;
private final PagingHttpMethodEnum myPagingHttpMethod;
public HttpSimpleClientInvocation(
FhirContext theContext, String theUrlPath, PagingHttpMethodEnum thePagingHttpMethod) {
@ -49,9 +51,23 @@ public class HttpSimpleClientInvocation extends BaseHttpClientInvocation {
Map<String, List<String>> theExtraParams,
EncodingEnum theEncoding,
Boolean thePrettyPrint) {
IHttpRequest retVal = createHttpRequest(myUrl, theEncoding, myPagingHttpMethod.getRequestType());
retVal.setUrlSource(myUrlSource);
return retVal;
return asHttpRequest(new AsHttpRequestParams()
.setUrlBase(myUrl)
.setExtraParams(theExtraParams)
.setEncodingEnum(theEncoding)
.setPrettyPrint(thePrettyPrint));
}
@Override
public IHttpRequest asHttpRequest(AsHttpRequestParams theParams) {
CreateRequestParameters parameters = new CreateRequestParameters();
parameters.setUrl(myUrl);
parameters.setEncodingEnum(theParams.getEncodingEnum());
parameters.setRequestTypeEnum(myPagingHttpMethod.getRequestType());
parameters.setClient(theParams.getClient());
IHttpRequest request = createHttpRequest(parameters);
request.setUrlSource(myUrlSource);
return request;
}
public void setUrlSource(UrlSourceEnum theUrlSource) {

View File

@ -0,0 +1,111 @@
/*-
* #%L
* HAPI FHIR - Client Framework
* %%
* Copyright (C) 2014 - 2024 Smile CDR, Inc.
* %%
* 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%
*/
package ca.uhn.fhir.rest.client.model;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.client.api.IHttpClient;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class AsHttpRequestParams {
/**
* The URL for the request
*/
private String myUrlBase;
/**
* Extra parameters
*/
private Map<String, List<String>> myExtraParams;
/**
* Encoding to use (JSON, fhir+json, etc)
*/
private EncodingEnum myEncodingEnum;
/**
* Whether logs should print pretty or not (for request parsing only)
*/
private Boolean myPrettyPrint;
/**
* The client to use for this request
*/
private IHttpClient myClient;
public String getUrlBase() {
return myUrlBase;
}
public AsHttpRequestParams setUrlBase(String theUrlBase) {
myUrlBase = theUrlBase;
return this;
}
public Map<String, List<String>> getExtraParams() {
if (myExtraParams == null) {
myExtraParams = new HashMap<>();
}
return myExtraParams;
}
public void addExtraParam(String theKey, String theValue) {
Map<String, List<String>> extraParams = getExtraParams();
if (!extraParams.containsKey(theKey)) {
extraParams.put(theKey, new ArrayList<>());
}
extraParams.get(theKey).add(theValue);
}
public AsHttpRequestParams setExtraParams(Map<String, List<String>> theExtraParams) {
myExtraParams = theExtraParams;
return this;
}
public EncodingEnum getEncodingEnum() {
return myEncodingEnum;
}
public AsHttpRequestParams setEncodingEnum(EncodingEnum theEncodingEnum) {
myEncodingEnum = theEncodingEnum;
return this;
}
public Boolean getPrettyPrint() {
return myPrettyPrint;
}
public AsHttpRequestParams setPrettyPrint(Boolean thePrettyPrint) {
myPrettyPrint = thePrettyPrint;
return this;
}
public IHttpClient getClient() {
return myClient;
}
public AsHttpRequestParams setClient(IHttpClient theClient) {
myClient = theClient;
return this;
}
}

View File

@ -0,0 +1,79 @@
/*-
* #%L
* HAPI FHIR - Client Framework
* %%
* Copyright (C) 2014 - 2024 Smile CDR, Inc.
* %%
* 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%
*/
package ca.uhn.fhir.rest.client.model;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.api.IHttpClient;
public class CreateRequestParameters {
/**
* The complete FHIR url to which the http request will be sent
*/
private String myUrl;
/**
* The encoding to use for any serialized content sent to the
* server
*/
private EncodingEnum myEncodingEnum;
/**
* the type of HTTP request (GET, DELETE, ..)
*/
private RequestTypeEnum myRequestTypeEnum;
private IHttpClient myClient;
public String getUrl() {
return myUrl;
}
public CreateRequestParameters setUrl(String theUrl) {
myUrl = theUrl;
return this;
}
public EncodingEnum getEncodingEnum() {
return myEncodingEnum;
}
public CreateRequestParameters setEncodingEnum(EncodingEnum theEncodingEnum) {
myEncodingEnum = theEncodingEnum;
return this;
}
public RequestTypeEnum getRequestTypeEnum() {
return myRequestTypeEnum;
}
public CreateRequestParameters setRequestTypeEnum(RequestTypeEnum theRequestTypeEnum) {
myRequestTypeEnum = theRequestTypeEnum;
return this;
}
public IHttpClient getClient() {
return myClient;
}
public CreateRequestParameters setClient(IHttpClient theClient) {
myClient = theClient;
return this;
}
}

View File

@ -0,0 +1,165 @@
/*-
* #%L
* HAPI FHIR - Client Framework
* %%
* Copyright (C) 2014 - 2024 Smile CDR, Inc.
* %%
* 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%
*/
package ca.uhn.fhir.rest.client.model;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.api.CacheControlDirective;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.api.SummaryEnum;
import ca.uhn.fhir.rest.client.impl.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.client.method.IClientResponseHandler;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class InvokeClientParameters<T> {
private FhirContext myContext;
private IClientResponseHandler<T> myBinding;
private BaseHttpClientInvocation myClientInvocation;
private EncodingEnum myEncoding;
private Boolean myPrettyPrint;
private boolean theLogRequestAndResponse;
private SummaryEnum mySummaryMode;
private Set<String> mySubsetElements;
private CacheControlDirective myCacheControlDirective;
private String myCustomAcceptHeader;
private Map<String, List<String>> myCustomHeaders;
public FhirContext getContext() {
return myContext;
}
public InvokeClientParameters<T> setContext(FhirContext theContext) {
myContext = theContext;
return this;
}
public IClientResponseHandler<T> getBinding() {
return myBinding;
}
public InvokeClientParameters<T> setBinding(IClientResponseHandler<T> theBinding) {
myBinding = theBinding;
return this;
}
public BaseHttpClientInvocation getClientInvocation() {
return myClientInvocation;
}
public InvokeClientParameters<T> setClientInvocation(BaseHttpClientInvocation theClientInvocation) {
myClientInvocation = theClientInvocation;
return this;
}
public EncodingEnum getEncoding() {
return myEncoding;
}
public InvokeClientParameters<T> setEncoding(EncodingEnum theEncoding) {
myEncoding = theEncoding;
return this;
}
public Boolean getPrettyPrint() {
return myPrettyPrint;
}
public InvokeClientParameters<T> setPrettyPrint(Boolean thePrettyPrint) {
myPrettyPrint = thePrettyPrint;
return this;
}
public boolean isTheLogRequestAndResponse() {
return theLogRequestAndResponse;
}
public InvokeClientParameters<T> setTheLogRequestAndResponse(boolean theTheLogRequestAndResponse) {
theLogRequestAndResponse = theTheLogRequestAndResponse;
return this;
}
public SummaryEnum getSummaryMode() {
return mySummaryMode;
}
public InvokeClientParameters<T> setSummaryMode(SummaryEnum theSummaryMode) {
mySummaryMode = theSummaryMode;
return this;
}
public Set<String> getSubsetElements() {
if (mySubsetElements == null) {
mySubsetElements = new HashSet<>();
}
return mySubsetElements;
}
public InvokeClientParameters<T> setSubsetElements(Set<String> theSubsetElements) {
mySubsetElements = theSubsetElements;
return this;
}
public void addSubsetElements(String theEl) {
getSubsetElements().add(theEl);
}
public CacheControlDirective getCacheControlDirective() {
return myCacheControlDirective;
}
public InvokeClientParameters<T> setCacheControlDirective(CacheControlDirective theCacheControlDirective) {
myCacheControlDirective = theCacheControlDirective;
return this;
}
public String getCustomAcceptHeader() {
return myCustomAcceptHeader;
}
public InvokeClientParameters<T> setCustomAcceptHeader(String theCustomAcceptHeader) {
myCustomAcceptHeader = theCustomAcceptHeader;
return this;
}
public Map<String, List<String>> getCustomHeaders() {
if (myCustomHeaders == null) {
myCustomHeaders = new HashMap<>();
}
return myCustomHeaders;
}
public InvokeClientParameters<T> setCustomHeaders(Map<String, List<String>> theCustomHeaders) {
myCustomHeaders = theCustomHeaders;
return this;
}
public void addCustomHeader(String theKey, String theValue) {
Map<String, List<String>> headers = getCustomHeaders();
if (!headers.containsKey(theKey)) {
headers.put(theKey, new ArrayList<>());
}
headers.get(theKey).add(theValue);
}
}

View File

@ -0,0 +1,6 @@
---
type: fix
issue: 6180
title: "Fixed GenericClient so that the IHttpClient (and settings used
to create it) are preserved for the lifetime of the client.
"

View File

@ -0,0 +1,5 @@
---
type: change
issue: 6261
title: "Upgrading to Jakarta had caused a problem with the version of java-simple-mail that was in use. This has been updated to be conformant
with the Jakarta Mail APIs. Thanks to Thomas Papke(@thopap) for the contribution!"

View File

@ -0,0 +1,7 @@
---
type: fix
issue: 6317
title: "Previously, defining a unique combo Search Parameter with the DateTime component and submitting multiple
resources with the same dateTime element (e.g. Observation.effectiveDateTime) resulted in duplicate resource creation.
This has been fixed."

View File

@ -29,6 +29,7 @@ import ca.uhn.fhir.rest.client.api.IHttpClient;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.impl.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.client.method.MethodUtil;
import ca.uhn.fhir.rest.param.HttpClientRequestParameters;
import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.Entity;
import jakarta.ws.rs.client.Invocation.Builder;
@ -37,9 +38,12 @@ import jakarta.ws.rs.core.MultivaluedHashMap;
import jakarta.ws.rs.core.MultivaluedMap;
import org.hl7.fhir.instance.model.api.IBaseBinary;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
/**
* A Http Request based on JaxRs. This is an adapter around the class
* {@link jakarta.ws.rs.client.Client Client}
@ -83,7 +87,15 @@ public class JaxRsHttpClient implements IHttpClient {
@Override
public IHttpRequest createParamRequest(
FhirContext theContext, Map<String, List<String>> theParams, EncodingEnum theEncoding) {
MultivaluedMap<String, String> map = new MultivaluedHashMap<String, String>();
Entity<Form> entity = getFormEntity(theParams);
myRequestType = RequestTypeEnum.POST;
JaxRsHttpRequest retVal = createHttpRequest(entity);
addHeadersToRequest(retVal, theEncoding, theContext);
return retVal;
}
private static Entity<Form> getFormEntity(Map<String, List<String>> theParams) {
MultivaluedMap<String, String> map = new MultivaluedHashMap<>();
for (Map.Entry<String, List<String>> nextParam : theParams.entrySet()) {
List<String> value = nextParam.getValue();
for (String s : value) {
@ -91,9 +103,7 @@ public class JaxRsHttpClient implements IHttpClient {
}
}
Entity<Form> entity = Entity.form(map);
JaxRsHttpRequest retVal = createHttpRequest(entity);
addHeadersToRequest(retVal, theEncoding, theContext);
return retVal;
return entity;
}
@Override
@ -111,6 +121,60 @@ public class JaxRsHttpClient implements IHttpClient {
return result;
}
@Override
public IHttpRequest createRequest(HttpClientRequestParameters theParameters) {
Map<String, String> additionalHeaders = new HashMap<>();
Entity<?> entity;
myRequestType = theParameters.getRequestTypeEnum();
switch (theParameters.getRequestTypeEnum()) {
case POST:
case PUT:
if (theParameters.getBaseBinary() != null) {
entity = Entity.entity(
theParameters.getBaseBinary().getContentAsBase64(),
theParameters.getBaseBinary().getContentType());
} else if (theParameters.getByteContents() != null) {
entity = Entity.entity(
theParameters.getByteContents(),
theParameters.getContentType() + Constants.HEADER_SUFFIX_CT_UTF_8);
additionalHeaders.put(
Constants.HEADER_CONTENT_TYPE,
theParameters.getContentType() + Constants.HEADER_SUFFIX_CT_UTF_8);
} else if (isNotBlank(theParameters.getContents())) {
entity = Entity.entity(
theParameters.getContents(),
theParameters.getContentType() + Constants.HEADER_SUFFIX_CT_UTF_8);
additionalHeaders.put(
Constants.HEADER_CONTENT_TYPE,
theParameters.getContentType() + Constants.HEADER_SUFFIX_CT_UTF_8);
} else {
entity = getFormEntity(theParameters.getFormParams());
}
break;
default:
entity = null;
}
JaxRsHttpRequest request = createHttpRequest(entity);
for (Map.Entry<String, String> entrySet : additionalHeaders.entrySet()) {
request.addHeader(entrySet.getKey(), entrySet.getValue());
}
addHeadersToRequest(request, theParameters.getEncodingEnum(), theParameters.getFhirContext());
return request;
}
@Override
public void addHeadersToRequest(IHttpRequest theRequest, EncodingEnum theEncodingEnum, FhirContext theContext) {
// nothing to do for this client
}
@Override
public void setNewUrl(
StringBuilder theUrl, String theIfNoneExistString, Map<String, List<String>> theIfNoneExistParams) {
myUrl = theUrl;
myIfNoneExistString = theIfNoneExistString;
myIfNoneExistParams = theIfNoneExistParams;
}
public void addHeadersToRequest(JaxRsHttpRequest theHttpRequest, EncodingEnum theEncoding, FhirContext theContext) {
if (myHeaders != null) {
for (Header next : myHeaders) {

View File

@ -75,7 +75,6 @@ public class GenericJaxRsClientDstu2Test {
}
private String getPatientFeedWithOneResult() {
String msg = "<Bundle xmlns=\"http://hl7.org/fhir\">\n" +
"<id>d039f91a-cc3c-4013-988e-af4d8d0614bd</id>\n" +
"<entry>\n" +
@ -108,24 +107,20 @@ public class GenericJaxRsClientDstu2Test {
IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir");
client.fetchConformance().ofType(Conformance.class).execute();
assertEquals(ourServer.getBaseUrl() + "/fhir/metadata", CAPTURE_SERVLET.ourRequestUri);
assertThat(CAPTURE_SERVLET.ourRequestHeaders.get("Accept")).hasSize(1);
assertThat(CAPTURE_SERVLET.ourRequestHeaders.get("Accept").get(0).getValue()).contains(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY);
client.fetchConformance().ofType(Conformance.class).encodedJson().execute();
assertEquals(ourServer.getBaseUrl() + "/fhir/metadata?_format=json", CAPTURE_SERVLET.ourRequestUri);
assertThat(CAPTURE_SERVLET.ourRequestHeaders.get("Accept")).hasSize(1);
assertThat(CAPTURE_SERVLET.ourRequestHeaders.get("Accept").get(0).getValue()).contains(Constants.CT_FHIR_JSON);
client.fetchConformance().ofType(Conformance.class).encodedXml().execute();
assertEquals(ourServer.getBaseUrl() + "/fhir/metadata?_format=xml", CAPTURE_SERVLET.ourRequestUri);
assertThat(CAPTURE_SERVLET.ourRequestHeaders.get("Accept")).hasSize(1);
assertThat(CAPTURE_SERVLET.ourRequestHeaders.get("Accept").get(0).getValue()).contains(Constants.CT_FHIR_XML);
}
@Test
@ -888,7 +883,6 @@ public class GenericJaxRsClientDstu2Test {
.withParameter(Parameters.class, "name1", weirdBase)
.execute();
});
}
@Test
@ -900,13 +894,11 @@ public class GenericJaxRsClientDstu2Test {
outParams.addParameter().setValue(new StringDt("STRINGVALOUT2"));
final String respString = p.encodeResourceToString(outParams);
CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8";
CAPTURE_SERVLET.ourResponseBody = respString;
IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir");
client
.operation()
.onInstance(new IdDt("http://foo/Patient/1"))
@ -916,10 +908,8 @@ public class GenericJaxRsClientDstu2Test {
.useHttpGet()
.execute();
assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/1/$validate-code?code=8495-4&system=http%3A%2F%2Floinc.org", CAPTURE_SERVLET.ourRequestUri);
client
.operation()
.onInstance(new IdDt("http://foo/Patient/1"))
@ -927,7 +917,6 @@ public class GenericJaxRsClientDstu2Test {
.withParameter(Parameters.class, "code", new CodeDt("8495-4"))
.andParameter("system", new UriDt("http://loinc.org"))
.encodedXml()
.encodedXml()
.execute();

View File

@ -1972,7 +1972,7 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
* The loop allows us to create multiple combo index joins if there
* are multiple AND expressions for the related parameters.
*/
while (validateParamValuesAreValidForComboParam(theRequest, theParams, comboParamNames)) {
while (validateParamValuesAreValidForComboParam(theRequest, theParams, comboParamNames, comboParam)) {
applyComboSearchParam(theQueryStack, theParams, theRequest, comboParamNames, comboParam);
}
}
@ -2068,7 +2068,10 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
* (e.g. <code>?date=gt2024-02-01</code>), etc.
*/
private boolean validateParamValuesAreValidForComboParam(
RequestDetails theRequest, @Nonnull SearchParameterMap theParams, List<String> theComboParamNames) {
RequestDetails theRequest,
@Nonnull SearchParameterMap theParams,
List<String> theComboParamNames,
RuntimeSearchParam theComboParam) {
boolean paramValuesAreValidForCombo = true;
List<List<IQueryParameterType>> paramOrValues = new ArrayList<>(theComboParamNames.size());
@ -2129,6 +2132,19 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
break;
}
}
// Date params are not eligible for using composite unique index
// as index could contain date with different precision (e.g. DAY, SECOND)
if (nextParamDef.getParamType() == RestSearchParameterTypeEnum.DATE
&& theComboParam.getComboSearchParamType() == ComboSearchParamType.UNIQUE) {
ourLog.debug(
"Search with params {} is not a candidate for combo searching - "
+ "Unique combo search parameter '{}' has DATE type",
theComboParamNames,
nextParamName);
paramValuesAreValidForCombo = false;
break;
}
}
if (CartesianProductUtil.calculateCartesianProductSize(paramOrValues) > 500) {

View File

@ -566,7 +566,8 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
for (BaseResourceIndexedSearchParam nextParam : paramsListForCompositePart) {
IQueryParameterType nextParamAsClientParam = nextParam.toQueryParameterType();
if (nextParamAsClientParam instanceof DateParam) {
if (theParam.getComboSearchParamType() == ComboSearchParamType.NON_UNIQUE
&& nextParamAsClientParam instanceof DateParam) {
DateParam date = (DateParam) nextParamAsClientParam;
if (date.getPrecision() != TemporalPrecisionEnum.DAY) {
continue;

View File

@ -135,10 +135,10 @@ public abstract class BasePartitioningR4Test extends BaseJpaR4SystemTest {
addCreateDefaultPartition();
addReadDefaultPartition(); // one for search param validation
SearchParameter sp = new SearchParameter();
sp.setId("SearchParameter/patient-birthdate");
sp.setType(Enumerations.SearchParamType.DATE);
sp.setCode("birthdate");
sp.setExpression("Patient.birthDate");
sp.setId("SearchParameter/patient-gender");
sp.setType(Enumerations.SearchParamType.TOKEN);
sp.setCode("gender");
sp.setExpression("Patient.gender");
sp.setStatus(Enumerations.PublicationStatus.ACTIVE);
sp.addBase("Patient");
mySearchParameterDao.update(sp, mySrd);
@ -156,13 +156,13 @@ public abstract class BasePartitioningR4Test extends BaseJpaR4SystemTest {
addCreateDefaultPartition();
sp = new SearchParameter();
sp.setId("SearchParameter/patient-birthdate-unique");
sp.setId("SearchParameter/patient-gender-family-unique");
sp.setType(Enumerations.SearchParamType.COMPOSITE);
sp.setStatus(Enumerations.PublicationStatus.ACTIVE);
sp.addBase("Patient");
sp.addComponent()
.setExpression("Patient")
.setDefinition("SearchParameter/patient-birthdate");
.setDefinition("SearchParameter/patient-gender");
sp.addComponent()
.setExpression("Patient")
.setDefinition("SearchParameter/patient-family");

View File

@ -13,10 +13,10 @@ import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.searchparam.util.JpaParamUtil;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.DateAndListParam;
import ca.uhn.fhir.rest.param.DateOrListParam;
import ca.uhn.fhir.rest.param.DateParam;
import ca.uhn.fhir.rest.param.ReferenceParam;
import ca.uhn.fhir.rest.param.StringOrListParam;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.param.TokenAndListParam;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
@ -33,6 +33,7 @@ import org.hl7.fhir.r4.model.DateType;
import org.hl7.fhir.r4.model.Encounter;
import org.hl7.fhir.r4.model.Enumerations;
import org.hl7.fhir.r4.model.Enumerations.PublicationStatus;
import org.hl7.fhir.r4.model.HumanName;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Observation;
import org.hl7.fhir.r4.model.Organization;
@ -115,6 +116,46 @@ public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test
myMessages.clear();
}
private void createUniqueGenderFamilyComboSp() {
SearchParameter sp = new SearchParameter();
sp.setId("SearchParameter/patient-gender");
sp.setType(Enumerations.SearchParamType.TOKEN);
sp.setCode("gender");
sp.setExpression("Patient.gender");
sp.setStatus(PublicationStatus.ACTIVE);
sp.addBase("Patient");
mySearchParameterDao.update(sp, mySrd);
sp = new SearchParameter();
sp.setId("SearchParameter/patient-family");
sp.setType(Enumerations.SearchParamType.STRING);
sp.setCode("family");
sp.setExpression("Patient.name.family");
sp.setStatus(Enumerations.PublicationStatus.ACTIVE);
sp.addBase("Patient");
mySearchParameterDao.update(sp, mySrd);
sp = new SearchParameter();
sp.setId("SearchParameter/patient-gender-family");
sp.setType(Enumerations.SearchParamType.COMPOSITE);
sp.setStatus(PublicationStatus.ACTIVE);
sp.addBase("Patient");
sp.addComponent()
.setExpression("Patient")
.setDefinition("SearchParameter/patient-gender");
sp.addComponent()
.setExpression("Patient")
.setDefinition("SearchParameter/patient-family");
sp.addExtension()
.setUrl(HapiExtensions.EXT_SP_UNIQUE)
.setValue(new BooleanType(true));
mySearchParameterDao.update(sp, mySrd);
mySearchParamRegistry.forceRefresh();
myMessages.clear();
}
private void createUniqueIndexCoverageBeneficiary() {
SearchParameter sp = new SearchParameter();
sp.setId("SearchParameter/coverage-beneficiary");
@ -276,6 +317,45 @@ public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test
mySearchParamRegistry.forceRefresh();
}
private void createUniqueObservationDateCode() {
SearchParameter sp = new SearchParameter();
sp.setId("SearchParameter/obs-effective");
sp.setType(Enumerations.SearchParamType.DATE);
sp.setCode("date");
sp.setExpression("Observation.effective");
sp.setStatus(PublicationStatus.ACTIVE);
sp.addBase("Observation");
mySearchParameterDao.update(sp, mySrd);
sp = new SearchParameter();
sp.setId("SearchParameter/obs-code");
sp.setType(Enumerations.SearchParamType.TOKEN);
sp.setCode("code");
sp.setExpression("Observation.code");
sp.setStatus(PublicationStatus.ACTIVE);
sp.addBase("Observation");
mySearchParameterDao.update(sp, mySrd);
sp = new SearchParameter();
sp.setId("SearchParameter/observation-date-code");
sp.setType(Enumerations.SearchParamType.COMPOSITE);
sp.setStatus(PublicationStatus.ACTIVE);
sp.addBase("Observation");
sp.setExpression("Observation.code");
sp.addComponent()
.setExpression("Observation")
.setDefinition("SearchParameter/obs-effective");
sp.addComponent()
.setExpression("Observation")
.setDefinition("SearchParameter/obs-code");
sp.addExtension()
.setUrl(HapiExtensions.EXT_SP_UNIQUE)
.setValue(new BooleanType(true));
mySearchParameterDao.update(sp, mySrd);
mySearchParamRegistry.forceRefresh();
}
private void createUniqueObservationSubjectDateCode() {
SearchParameter sp = new SearchParameter();
sp.setId("SearchParameter/obs-subject");
@ -471,11 +551,11 @@ public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test
public void testDoubleMatchingOnAnd_Search_TwoAndOrValues() {
myStorageSettings.setUniqueIndexesCheckedBeforeSave(false);
createUniqueBirthdateAndGenderSps();
createUniqueGenderFamilyComboSp();
Patient pt1 = new Patient();
pt1.setGender(Enumerations.AdministrativeGender.MALE);
pt1.setBirthDateElement(new DateType("2011-01-01"));
pt1.getName().add(new HumanName().setFamily("Family1"));
String id1 = myPatientDao.create(pt1, mySrd).getId().toUnqualifiedVersionless().getValue();
// Two OR values on the same resource - Currently composite SPs don't work for this
@ -484,17 +564,22 @@ public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test
sp.setLoadSynchronous(true);
sp.add(Patient.SP_GENDER,
new TokenAndListParam()
.addAnd(new TokenParam("http://hl7.org/fhir/administrative-gender","male"), new TokenParam( "http://hl7.org/fhir/administrative-gender","female"))
.addAnd(new TokenParam("http://hl7.org/fhir/administrative-gender","male"),
new TokenParam( "http://hl7.org/fhir/administrative-gender","female"))
);
sp.add(Patient.SP_BIRTHDATE,
new DateAndListParam()
.addAnd(new DateParam("2011-01-01"), new DateParam( "2011-02-02"))
sp.add(Patient.SP_FAMILY,
new StringOrListParam()
.addOr(new StringParam("Family1")).addOr(new StringParam("Family2"))
);
IBundleProvider outcome = myPatientDao.search(sp, mySrd);
myCaptureQueriesListener.logFirstSelectQueryForCurrentThread();
assertThat(toUnqualifiedVersionlessIdValues(outcome)).containsExactlyInAnyOrder(id1);
String unformattedSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
assertEquals("SELECT t0.RES_ID FROM HFJ_IDX_CMP_STRING_UNIQ t0 WHERE (t0.IDX_STRING IN ('Patient?birthdate=2011-01-01&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cfemale','Patient?birthdate=2011-01-01&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale','Patient?birthdate=2011-02-02&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cfemale','Patient?birthdate=2011-02-02&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale') )", unformattedSql);
assertEquals("SELECT t0.RES_ID FROM HFJ_IDX_CMP_STRING_UNIQ t0 WHERE (t0.IDX_STRING IN (" +
"'Patient?family=Family1&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cfemale'," +
"'Patient?family=Family1&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale'," +
"'Patient?family=Family2&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cfemale'," +
"'Patient?family=Family2&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale') )", unformattedSql);
}
@ -1167,16 +1252,16 @@ public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test
@Test
public void testOrQuery() {
myStorageSettings.setAdvancedHSearchIndexing(false);
createUniqueBirthdateAndGenderSps();
createUniqueGenderFamilyComboSp();
Patient pt1 = new Patient();
pt1.setGender(Enumerations.AdministrativeGender.MALE);
pt1.setBirthDateElement(new DateType("2011-01-01"));
pt1.getName().add(new HumanName().setFamily("Family1"));
IIdType id1 = myPatientDao.create(pt1, mySrd).getId().toUnqualifiedVersionless();
Patient pt2 = new Patient();
pt2.setGender(Enumerations.AdministrativeGender.MALE);
pt2.setBirthDateElement(new DateType("2011-01-02"));
pt2.getName().add(new HumanName().setFamily("Family2"));
IIdType id2 = myPatientDao.create(pt2, mySrd).getId().toUnqualifiedVersionless();
myCaptureQueriesListener.clear();
@ -1184,16 +1269,21 @@ public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test
SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronousUpTo(100);
params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male"));
params.add("birthdate", new DateOrListParam().addOr(new DateParam("2011-01-01")).addOr(new DateParam("2011-01-02")));
params.add("family", new StringOrListParam()
.addOr(new StringParam("Family1")).addOr(new StringParam("Family2")));
myCaptureQueriesListener.clear();
IBundleProvider results = myPatientDao.search(params, mySrd);
myCaptureQueriesListener.logFirstSelectQueryForCurrentThread();
assertThat(toUnqualifiedVersionlessIdValues(results)).containsExactlyInAnyOrder(id1.getValue(), id2.getValue());
assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false))
.contains("SELECT t0.RES_ID FROM HFJ_IDX_CMP_STRING_UNIQ t0 WHERE (t0.IDX_STRING IN ('Patient?birthdate=2011-01-01&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale','Patient?birthdate=2011-01-02&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale') )");
.contains("SELECT t0.RES_ID FROM HFJ_IDX_CMP_STRING_UNIQ t0 WHERE (t0.IDX_STRING IN " +
"('Patient?family=Family1&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale'," +
"'Patient?family=Family2&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale') )");
logCapturedMessages();
assertThat(myMessages.toString()).contains("Using UNIQUE index(es) for query for search: [Patient?birthdate=2011-01-01&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale, Patient?birthdate=2011-01-02&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale]");
assertThat(myMessages.toString()).contains("Using UNIQUE index(es) for query for search: " +
"[Patient?family=Family1&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale, " +
"Patient?family=Family2&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale]");
myMessages.clear();
}
@ -1201,16 +1291,16 @@ public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test
@Test
public void testSearchSynchronousUsingUniqueComposite() {
myStorageSettings.setAdvancedHSearchIndexing(false);
createUniqueBirthdateAndGenderSps();
createUniqueGenderFamilyComboSp();
Patient pt1 = new Patient();
pt1.setGender(Enumerations.AdministrativeGender.MALE);
pt1.setBirthDateElement(new DateType("2011-01-01"));
pt1.getName().add(new HumanName().setFamily("Family1"));
IIdType id1 = myPatientDao.create(pt1, mySrd).getId().toUnqualifiedVersionless();
Patient pt2 = new Patient();
pt2.setGender(Enumerations.AdministrativeGender.MALE);
pt2.setBirthDateElement(new DateType("2011-01-02"));
pt2.getName().add(new HumanName().setFamily("Family2"));
myPatientDao.create(pt2, mySrd).getId().toUnqualifiedVersionless();
myCaptureQueriesListener.clear();
@ -1218,13 +1308,14 @@ public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test
SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronousUpTo(100);
params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male"));
params.add("birthdate", new DateParam("2011-01-01"));
params.add("family", new StringParam("Family1"));
IBundleProvider results = myPatientDao.search(params, mySrd);
myCaptureQueriesListener.logFirstSelectQueryForCurrentThread();
assertThat(toUnqualifiedVersionlessIdValues(results)).containsExactlyInAnyOrder(id1.getValue());
logCapturedMessages();
assertThat(myMessages.toString()).contains("Using UNIQUE index(es) for query for search: Patient?birthdate=2011-01-01&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale");
assertThat(myMessages.toString()).contains("Using UNIQUE index(es) for query for search: " +
"Patient?family=Family1&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale");
myMessages.clear();
}
@ -1232,33 +1323,34 @@ public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test
@Test
public void testSearchUsingUniqueComposite() {
createUniqueBirthdateAndGenderSps();
createUniqueGenderFamilyComboSp();
Patient pt1 = new Patient();
pt1.setGender(Enumerations.AdministrativeGender.MALE);
pt1.setBirthDateElement(new DateType("2011-01-01"));
pt1.getName().add(new HumanName().setFamily("Family1"));
String id1 = myPatientDao.create(pt1, mySrd).getId().toUnqualifiedVersionless().getValue();
Patient pt2 = new Patient();
pt2.setGender(Enumerations.AdministrativeGender.MALE);
pt2.setBirthDateElement(new DateType("2011-01-02"));
pt2.getName().add(new HumanName().setFamily("Family2"));
myPatientDao.create(pt2, mySrd).getId().toUnqualifiedVersionless();
myMessages.clear();
SearchParameterMap params = new SearchParameterMap();
params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male"));
params.add("birthdate", new DateParam("2011-01-01"));
params.add("family", new StringParam("Family1"));
IBundleProvider results = myPatientDao.search(params, mySrd);
String searchId = results.getUuid();
assertThat(toUnqualifiedVersionlessIdValues(results)).containsExactlyInAnyOrder(id1);
logCapturedMessages();
assertThat(myMessages.toString()).contains("Using UNIQUE index(es) for query for search: Patient?birthdate=2011-01-01&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale");
assertThat(myMessages.toString()).contains("Using UNIQUE index(es) for query for search: " +
"Patient?family=Family1&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale");
myMessages.clear();
// Other order
myMessages.clear();
params = new SearchParameterMap();
params.add("birthdate", new DateParam("2011-01-01"));
params.add("family", new StringParam("Family1"));
params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male"));
results = myPatientDao.search(params, mySrd);
assertEquals(searchId, results.getUuid());
@ -1272,16 +1364,17 @@ public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test
myMessages.clear();
params = new SearchParameterMap();
params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male"));
params.add("birthdate", new DateParam("2011-01-03"));
params.add("family", new StringParam("Family3"));
results = myPatientDao.search(params, mySrd);
assertThat(toUnqualifiedVersionlessIdValues(results)).isEmpty();
logCapturedMessages();
assertThat(myMessages.toString()).contains("Using UNIQUE index(es) for query for search: Patient?birthdate=2011-01-03&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale");
assertThat(myMessages.toString()).contains("Using UNIQUE index(es) for query for search: " +
"Patient?family=Family3&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale");
myMessages.clear();
myMessages.clear();
params = new SearchParameterMap();
params.add("birthdate", new DateParam("2011-01-03"));
params.add("family", new StringParam("Family3"));
results = myPatientDao.search(params, mySrd);
assertThat(toUnqualifiedVersionlessIdValues(results)).isEmpty();
// STANDARD QUERY
@ -1666,7 +1759,7 @@ public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test
}
@Test
public void testDuplicateUniqueValuesAreRejected() {
public void testDuplicateUniqueValuesWithDateAreRejected() {
createUniqueBirthdateAndGenderSps();
Patient pt1 = new Patient();
@ -1699,13 +1792,75 @@ public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test
}
@Test
public void testReplaceOneWithAnother() {
myStorageSettings.setAdvancedHSearchIndexing(false);
public void testDuplicateUniqueValuesWithDateTimeAreRejected() {
createUniqueObservationDateCode();
Observation obs = new Observation();
obs.getCode().addCoding().setSystem("foo").setCode("bar");
obs.setEffective(new DateTimeType("2017-10-10T00:00:00"));
myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
try {
myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
fail();
} catch (ResourceVersionConflictException e) {
assertThat(e.getMessage())
.contains("new unique index created by SearchParameter/observation-date-code");
}
}
@Test
public void testUniqueComboSearchWithDateNotUsingUniqueIndex() {
createUniqueBirthdateAndGenderSps();
Patient pt1 = new Patient();
pt1.setGender(Enumerations.AdministrativeGender.MALE);
pt1.setBirthDateElement(new DateType("2011-01-01"));
String pId = myPatientDao.create(pt1, mySrd).getId().toUnqualifiedVersionless().getValue();
myCaptureQueriesListener.clear();
SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronousUpTo(100);
params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male"));
params.add("birthdate", new DateParam("2011-01-01"));
IBundleProvider results = myPatientDao.search(params, mySrd);
assertThat(toUnqualifiedVersionlessIdValues(results)).containsExactlyInAnyOrder(pId);
myCaptureQueriesListener.logFirstSelectQueryForCurrentThread();
String unformattedSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
assertThat(unformattedSql).doesNotContain("HFJ_IDX_CMP_STRING_UNIQ");
}
@Test
public void testUniqueComboSearchWithDateTimeNotUsingUniqueIndex() {
createUniqueObservationDateCode();
Observation obs = new Observation();
obs.getCode().addCoding().setSystem("foo").setCode("bar");
obs.setEffective(new DateTimeType("2017-10-10T00:00:00"));
String obsId = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless().getValue();
myCaptureQueriesListener.clear();
SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronousUpTo(100);
params.add("code", new TokenParam("foo", "bar"));
params.add("date", new DateParam("2017-10-10T00:00:00"));
IBundleProvider results = myObservationDao.search(params, mySrd);
assertThat(toUnqualifiedVersionlessIdValues(results)).containsExactlyInAnyOrder(obsId);
myCaptureQueriesListener.logFirstSelectQueryForCurrentThread();
String unformattedSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
assertThat(unformattedSql).doesNotContain("HFJ_IDX_CMP_STRING_UNIQ");
}
@Test
public void testReplaceOneWithAnother() {
myStorageSettings.setAdvancedHSearchIndexing(false);
createUniqueGenderFamilyComboSp();
Patient pt1 = new Patient();
pt1.setGender(Enumerations.AdministrativeGender.MALE);
pt1.getName().add(new HumanName().setFamily("Family1"));
IIdType id1 = myPatientDao.create(pt1, mySrd).getId().toUnqualified();
assertNotNull(id1);
@ -1714,27 +1869,28 @@ public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test
pt1 = new Patient();
pt1.setId(id1);
pt1.setGender(Enumerations.AdministrativeGender.FEMALE);
pt1.setBirthDateElement(new DateType("2011-01-01"));
pt1.getName().add(new HumanName().setFamily("Family1"));
id1 = myPatientDao.update(pt1, mySrd).getId().toUnqualified();
assertNotNull(id1);
assertEquals("2", id1.getVersionIdPart());
Patient pt2 = new Patient();
pt2.setGender(Enumerations.AdministrativeGender.MALE);
pt2.setBirthDateElement(new DateType("2011-01-01"));
pt2.getName().add(new HumanName().setFamily("Family1"));
IIdType id2 = myPatientDao.create(pt2, mySrd).getId().toUnqualifiedVersionless();
myMessages.clear();
SearchParameterMap params = new SearchParameterMap();
params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male"));
params.add("birthdate", new DateParam("2011-01-01"));
params.add("family", new StringParam("Family1"));
IBundleProvider results = myPatientDao.search(params, mySrd);
String searchId = results.getUuid();
assertThat(searchId).isNotBlank();
assertThat(toUnqualifiedVersionlessIdValues(results)).containsExactlyInAnyOrder(id2.getValue());
logCapturedMessages();
assertThat(myMessages.toString()).contains("Using UNIQUE index(es) for query for search: Patient?birthdate=2011-01-01&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale");
assertThat(myMessages.toString()).contains("Using UNIQUE index(es) for query for search: " +
"Patient?family=Family1&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale");
myMessages.clear();
}

View File

@ -413,7 +413,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test {
p.getMeta().addTag("http://system", "code", "diisplay");
p.addName().setFamily("FAM");
p.addIdentifier().setSystem("system").setValue("value");
p.setBirthDateElement(new DateType("2020-01-01"));
p.setGender(Enumerations.AdministrativeGender.MALE);
p.getManagingOrganization().setReferenceElement(orgId);
Long patientId = myPatientDao.create(p, mySrd).getId().getIdPartAsLong();
@ -502,7 +502,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test {
p.getMeta().addTag("http://system", "code", "diisplay");
p.addName().setFamily("FAM");
p.addIdentifier().setSystem("system").setValue("value");
p.setBirthDate(new Date());
p.setGender(Enumerations.AdministrativeGender.MALE);
p.getManagingOrganization().setReferenceElement(orgId);
Long patientId = myPatientDao.create(p, mySrd).getId().getIdPartAsLong();
@ -679,7 +679,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test {
p.getMeta().addTag("http://system", "code", "display");
p.addName().setFamily("FAM");
p.addIdentifier().setSystem("system").setValue("value");
p.setBirthDate(new Date());
p.setGender(Enumerations.AdministrativeGender.MALE);
p.getManagingOrganization().setReference(org.getId());
input.addEntry()
.setFullUrl(p.getId())
@ -2541,14 +2541,14 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test {
public void testSearch_UniqueParam_SearchAllPartitions() {
createUniqueComboSp();
IIdType id = createPatient(withPartition(1), withBirthdate("2020-01-01"), withFamily("FAM"));
IIdType id = createPatient(withPartition(1), withGender("male"), withFamily("FAM"));
addReadAllPartitions();
myCaptureQueriesListener.clear();
SearchParameterMap map = new SearchParameterMap();
map.add(Patient.SP_FAMILY, new StringParam("FAM"));
map.add(Patient.SP_BIRTHDATE, new DateParam("2020-01-01"));
map.add(Patient.SP_GENDER, new TokenParam(null, "male"));
map.setLoadSynchronous(true);
IBundleProvider results = myPatientDao.search(map, mySrd);
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
@ -2558,7 +2558,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test {
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
ourLog.info("Search SQL:\n{}", searchSql);
assertThat(searchSql).doesNotContain("PARTITION_ID");
assertThat(searchSql).containsOnlyOnce("IDX_STRING = 'Patient?birthdate=2020-01-01&family=FAM'");
assertThat(searchSql).containsOnlyOnce("IDX_STRING = 'Patient?family=FAM&gender=male'");
}
@ -2566,13 +2566,13 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test {
public void testSearch_UniqueParam_SearchOnePartition() {
createUniqueComboSp();
IIdType id = createPatient(withPartition(1), withBirthdate("2020-01-01"), withFamily("FAM"));
IIdType id = createPatient(withPartition(1), withGender("male"), withFamily("FAM"));
addReadPartition(1);
myCaptureQueriesListener.clear();
SearchParameterMap map = new SearchParameterMap();
map.add(Patient.SP_FAMILY, new StringParam("FAM"));
map.add(Patient.SP_BIRTHDATE, new DateParam("2020-01-01"));
map.add(Patient.SP_GENDER, new TokenParam(null, "male"));
map.setLoadSynchronous(true);
IBundleProvider results = myPatientDao.search(map, mySrd);
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
@ -2582,13 +2582,13 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test {
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
ourLog.info("Search SQL:\n{}", searchSql);
assertThat(searchSql).containsOnlyOnce( "PARTITION_ID = '1'");
assertThat(searchSql).containsOnlyOnce("IDX_STRING = 'Patient?birthdate=2020-01-01&family=FAM'");
assertThat(searchSql).containsOnlyOnce("IDX_STRING = 'Patient?family=FAM&gender=male'");
// Same query, different partition
addReadPartition(2);
myCaptureQueriesListener.clear();
map = new SearchParameterMap();
map.add(Patient.SP_BIRTHDATE, new DateParam("2020-01-01"));
map.add(Patient.SP_GENDER, new TokenParam(null, "male"));
map.setLoadSynchronous(true);
results = myPatientDao.search(map, mySrd);
ids = toUnqualifiedVersionlessIds(results);
@ -2661,7 +2661,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test {
public void testSearch_RefParam_TargetPid_SearchOnePartition() {
createUniqueComboSp();
IIdType patientId = createPatient(withPartition(myPartitionId), withBirthdate("2020-01-01"));
IIdType patientId = createPatient(withPartition(myPartitionId), withGender("male"));
IIdType observationId = createObservation(withPartition(myPartitionId), withSubject(patientId));
addReadPartition(myPartitionId);
@ -2698,7 +2698,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test {
public void testSearch_RefParam_TargetPid_SearchDefaultPartition() {
createUniqueComboSp();
IIdType patientId = createPatient(withPartition(null), withBirthdate("2020-01-01"));
IIdType patientId = createPatient(withPartition(null), withGender("male"));
IIdType observationId = createObservation(withPartition(null), withSubject(patientId));
addReadDefaultPartition();
@ -2735,7 +2735,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test {
public void testSearch_RefParam_TargetForcedId_SearchOnePartition() {
createUniqueComboSp();
IIdType patientId = createPatient(withPartition(myPartitionId), withId("ONE"), withBirthdate("2020-01-01"));
IIdType patientId = createPatient(withPartition(myPartitionId), withId("ONE"), withGender("male"));
IIdType observationId = createObservation(withPartition(myPartitionId), withSubject(patientId));
addReadPartition(myPartitionId);
@ -2805,7 +2805,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test {
public void testSearch_RefParam_TargetForcedId_SearchDefaultPartition() {
createUniqueComboSp();
IIdType patientId = createPatient(withPartition(null), withId("ONE"), withBirthdate("2020-01-01"));
IIdType patientId = createPatient(withPartition(null), withId("ONE"), withGender("male"));
IIdType observationId = createObservation(withPartition(null), withSubject(patientId));
addReadDefaultPartition();

View File

@ -64,6 +64,7 @@ import ca.uhn.fhir.util.UrlUtil;
import com.google.common.base.Charsets;
import com.google.common.collect.Lists;
import jakarta.annotation.Nonnull;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
@ -168,6 +169,7 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
@ -188,6 +190,9 @@ import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
@ -196,6 +201,7 @@ import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -205,6 +211,7 @@ import static ca.uhn.fhir.util.TestUtil.sleepAtLeast;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
import static org.awaitility.Awaitility.await;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@ -263,6 +270,73 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
myStorageSettings.setSearchPreFetchThresholds(new JpaStorageSettings().getSearchPreFetchThresholds());
}
/**
* We set to run last because we alter our client configs,
* and we don't want to break tests that create new clients
* (with an expectation of a different set of configs)
* after this test.
*/
@Order(Integer.MAX_VALUE)
@Test
public void testGenericClient_preservesClientSettings() {
// setup
int socketTimeout = 700;
int delay = 1200;
String clientHeader = "client";
// we register a delaying interceptor for all requests to this server
Object interceptor = new Object() {
@Hook(Pointcut.SERVER_INCOMING_REQUEST_PRE_PROCESSED)
public void intercept(HttpServletRequest theRequest) {
Instant start = Instant.now();
Instant endTime = Instant.now().plus(delay, ChronoUnit.MILLIS);
await()
.atLeast(socketTimeout, TimeUnit.MILLISECONDS)
.atMost(delay + socketTimeout, TimeUnit.MILLISECONDS)
.until(() -> {
return Instant.now().isAfter(endTime);
});
ourLog.info("Delayed {}ms for server request from {}",
Instant.now().toEpochMilli() - start.toEpochMilli(),
theRequest.getHeader(clientHeader));
}
};
try {
myServer.getInterceptorService().registerInterceptor(interceptor);
// get a first client with our short timeout
myFhirContext.getRestfulClientFactory().setSocketTimeout(socketTimeout);
IGenericClient client1 = myFhirContext.newRestfulGenericClient(myServerBase);
// get a second client with a longer timeout
myFhirContext.getRestfulClientFactory().setSocketTimeout((int)Duration.of(10, ChronoUnit.SECONDS).toMillis());
IGenericClient client2 = myFhirContext.newRestfulGenericClient(myServerBase);
try {
// should fail
client1.search()
.forResource("Patient")
.withAdditionalHeader(clientHeader, "client1")
.execute();
fail();
} catch (Exception ex) {
assertTrue(ex.getMessage().contains("SocketTimeoutException"));
}
// should not fail
IBaseBundle result = client2.search()
.forResource("Patient")
.withAdditionalHeader(clientHeader, "client2")
.execute();
assertNotNull(result);
} finally {
myServer.getInterceptorService().unregisterInterceptor(interceptor);
}
}
@Test
public void testParameterWithNoValueThrowsError_InvalidChainOnCustomSearch() throws IOException {
SearchParameter searchParameter = new SearchParameter();

View File

@ -1,6 +1,5 @@
package ca.uhn.fhir.rest.client;
import static org.junit.jupiter.api.Assertions.assertEquals;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.annotation.IdParam;
@ -11,6 +10,7 @@ import ca.uhn.fhir.rest.annotation.Validate;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.ValidationModeEnum;
import ca.uhn.fhir.rest.client.apache.ApacheRestfulClientFactory;
import ca.uhn.fhir.rest.client.api.IRestfulClient;
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
import ca.uhn.fhir.system.HapiSystemProperties;
@ -44,6 +44,7 @@ import java.nio.charset.Charset;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ -54,13 +55,13 @@ public class NonGenericClientDstu2_1Test {
@BeforeEach
public void before() {
ourCtx.setRestfulClientFactory(new ApacheRestfulClientFactory(ourCtx));
myHttpClient = mock(HttpClient.class, new ReturnsDeepStubs());
ourCtx.getRestfulClientFactory().setHttpClient(myHttpClient);
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
myHttpResponse = mock(HttpResponse.class, new ReturnsDeepStubs());
HapiSystemProperties.enableHapiClientKeepResponses();
}
private String extractBodyAsString(ArgumentCaptor<HttpUriRequest> capt, int theIdx) throws IOException {

View File

@ -11,6 +11,7 @@ import ca.uhn.fhir.rest.annotation.Validate;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.ValidationModeEnum;
import ca.uhn.fhir.rest.client.apache.ApacheRestfulClientFactory;
import ca.uhn.fhir.rest.client.api.IRestfulClient;
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
import ca.uhn.fhir.system.HapiSystemProperties;
@ -54,6 +55,7 @@ public class NonGenericClientDstu3Test {
@BeforeEach
public void before() {
ourCtx.setRestfulClientFactory(new ApacheRestfulClientFactory(ourCtx));
myHttpClient = mock(HttpClient.class, new ReturnsDeepStubs());
ourCtx.getRestfulClientFactory().setHttpClient(myHttpClient);
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);

View File

@ -19,12 +19,14 @@ import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.SummaryEnum;
import ca.uhn.fhir.rest.client.apache.ApacheHttpRequest;
import ca.uhn.fhir.rest.client.apache.ApacheRestfulClientFactory;
import ca.uhn.fhir.rest.client.apache.ResourceEntity;
import ca.uhn.fhir.rest.client.api.IBasicClient;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.api.IRestfulClient;
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException;
import ca.uhn.fhir.rest.client.impl.RestfulClientFactory;
import ca.uhn.fhir.rest.client.interceptor.CapturingInterceptor;
import ca.uhn.fhir.rest.param.CompositeParam;
import ca.uhn.fhir.rest.param.DateParam;
@ -108,7 +110,9 @@ public class ClientR4Test {
@BeforeEach
public void before() {
// we want to reset the client factory so we aren't handing back previously used
// test clients
ourCtx.setRestfulClientFactory(new ApacheRestfulClientFactory(ourCtx));
myHttpClient = mock(HttpClient.class, new ReturnsDeepStubs());
ourCtx.getRestfulClientFactory().setHttpClient(myHttpClient);
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);

View File

@ -10,6 +10,7 @@ import ca.uhn.fhir.rest.annotation.Validate;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.ValidationModeEnum;
import ca.uhn.fhir.rest.client.apache.ApacheRestfulClientFactory;
import ca.uhn.fhir.rest.client.api.IRestfulClient;
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
import ca.uhn.fhir.system.HapiSystemProperties;
@ -54,6 +55,8 @@ public class NonGenericClientR4Test {
@BeforeEach
public void before() {
// reset our client factory so we don't reuse test clients
ourCtx.setRestfulClientFactory(new ApacheRestfulClientFactory(ourCtx));
myHttpClient = mock(HttpClient.class, new ReturnsDeepStubs());
ourCtx.getRestfulClientFactory().setHttpClient(myHttpClient);
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);