Merge branch 'master' into hapi3_refactor

This commit is contained in:
James Agnew 2017-07-28 06:21:02 -04:00
commit b405e51773
266 changed files with 10155 additions and 6382 deletions

View File

@ -99,7 +99,11 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
if (childOrder != null) {
forcedOrder = new HashMap<String, Integer>();
for (int i = 0; i < childOrder.names().length; i++) {
forcedOrder.put(childOrder.names()[i], i);
String nextName = childOrder.names()[i];
if (nextName.endsWith("[x]")) {
nextName = nextName.substring(0, nextName.length() - 3);
}
forcedOrder.put(nextName, i);
}
}
}

View File

@ -10,7 +10,7 @@ package ca.uhn.fhir.rest.param;
* 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
* 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,
@ -44,8 +44,13 @@ public abstract class BaseParamWithPrefix<T extends BaseParam> extends BaseParam
String extractPrefixAndReturnRest(String theString) {
int offset = 0;
while (true) {
if (theString.length() == offset || Character.isDigit(theString.charAt(offset))) {
if (theString.length() == offset) {
break;
} else {
char nextChar = theString.charAt(offset);
if (nextChar == '-' || Character.isDigit(nextChar)) {
break;
}
}
offset++;
}
@ -60,7 +65,7 @@ public abstract class BaseParamWithPrefix<T extends BaseParam> extends BaseParam
}
/**
* @deprecated Use {@link #getPrefix() instead}
* @deprecated Use {@link #getPrefix()} instead
*/
@Deprecated
public QuantityCompararatorEnum getComparator() {
@ -68,7 +73,7 @@ public abstract class BaseParamWithPrefix<T extends BaseParam> extends BaseParam
if (prefix == null) {
return null;
}
return QuantityCompararatorEnum.forCode(prefix.getDstu1Value());
}

View File

@ -10,7 +10,7 @@ package ca.uhn.fhir.util;
* 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
* 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,
@ -41,24 +41,6 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
*/
public class OperationOutcomeUtil {
// /**
// * Add an issue to an OperationOutcome
// *
// * @param theCtx
// * The fhir context
// * @param theOperationOutcome
// * The OO resource to add to
// * @param theSeverity
// * The severity (e.g. "error")
// * @param theDetails
// * The details string
// * @param theCode
// */
// public static void addIssue(FhirContext theCtx, IBaseOperationOutcome theOperationOutcome, String theSeverity, String theDetails, String theCode) {
// IBase issue = createIssue(theCtx, theOperationOutcome);
// populateDetails(theCtx, issue, theSeverity, theDetails, null, theCode);
// }
/**
* Add an issue to an OperationOutcome
*
@ -67,10 +49,10 @@ public class OperationOutcomeUtil {
* @param theOperationOutcome
* The OO resource to add to
* @param theSeverity
* The severity (e.g. "error")
* The severity (fatal | error | warning | information)
* @param theDetails
* The details string
* @param theCode
* @param theCode
*/
public static void addIssue(FhirContext theCtx, IBaseOperationOutcome theOperationOutcome, String theSeverity, String theDetails, String theLocation, String theCode) {
IBase issue = createIssue(theCtx, theOperationOutcome);
@ -150,7 +132,7 @@ public class OperationOutcomeUtil {
BaseRuntimeChildDefinition detailsChild;
if (theCtx.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
detailsChild = issueElement.getChildByName("diagnostics");
BaseRuntimeChildDefinition codeChild = issueElement.getChildByName("code");
IPrimitiveType<?> codeElem = (IPrimitiveType<?>) codeChild.getChildByName("code").newInstance(codeChild.getInstanceConstructorArguments());
codeElem.setValueAsString(theCode);
@ -158,7 +140,7 @@ public class OperationOutcomeUtil {
} else {
detailsChild = issueElement.getChildByName("details");
}
BaseRuntimeElementDefinition<?> stringDef = detailsChild.getChildByName(detailsChild.getElementName());
BaseRuntimeChildDefinition severityChild = issueElement.getChildByName("severity");
BaseRuntimeChildDefinition locationChild = issueElement.getChildByName("location");

View File

@ -27,7 +27,7 @@ import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.lang3.Validate;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.model.api.IFhirVersion;
import ca.uhn.fhir.context.support.IContextValidationSupport;
import javassist.Modifier;
public class ReflectionUtil {
@ -153,20 +153,35 @@ public class ReflectionUtil {
}
public static Object newInstanceOfFhirServerType(String theType) {
String errorMessage = "Unable to instantiate server framework. Please make sure that hapi-fhir-server library is on your classpath!";
String wantedType = "ca.uhn.fhir.rest.api.server.IFhirVersionServer";
Object fhirServerVersion = newInstanceOfType(theType, errorMessage, wantedType);
return fhirServerVersion;
}
@SuppressWarnings("unchecked")
public static <EVS_IN, EVS_OUT, SDT, CST, CDCT, IST> ca.uhn.fhir.context.support.IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST> newInstanceOfFhirProfileValidationSupport(
String theType) {
String errorMessage = "Unable to instantiate validation support! Please make sure that hapi-fhir-validation and the appropriate structures JAR are on your classpath!";
String wantedType = "ca.uhn.fhir.context.support.IContextValidationSupport";
Object fhirServerVersion = newInstanceOfType(theType, errorMessage, wantedType);
return (IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST>) fhirServerVersion;
}
private static Object newInstanceOfType(String theType, String errorMessage, String wantedType) {
Object fhirServerVersion = ourFhirServerVersions.get(theType);
if (fhirServerVersion == null) {
try {
Class<?> type = Class.forName(theType);
Class<?> serverType = Class.forName("ca.uhn.fhir.rest.api.server.IFhirVersionServer");
Class<?> serverType = Class.forName(wantedType);
Validate.isTrue(serverType.isAssignableFrom(type));
fhirServerVersion = type.newInstance();
} catch (Exception e) {
throw new ConfigurationException("Unable to instantiate server framework. Please make sure that hapi-fhir-server library is on your classpath!", e);
throw new ConfigurationException(errorMessage, e);
}
ourFhirServerVersions.put(theType, fhirServerVersion);
}
return fhirServerVersion;
}

View File

@ -21,6 +21,7 @@ import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.demo.ContextHolder;
import ca.uhn.fhir.jpa.demo.FhirServerConfig;
import ca.uhn.fhir.jpa.demo.FhirServerConfigDstu3;
@ -30,6 +31,7 @@ public class RunServerCommand extends BaseCommand {
private static final String OPTION_DISABLE_REFERENTIAL_INTEGRITY = "disable-referential-integrity";
private static final String OPTION_LOWMEM = "lowmem";
private static final String OPTION_ALLOW_EXTERNAL_REFS = "allow-external-refs";
private static final String OPTION_REUSE_SEARCH_RESULTS_MILLIS = "reuse-search-results-milliseconds";
private static final int DEFAULT_PORT = 8080;
private static final String OPTION_P = "p";
@ -51,6 +53,10 @@ public class RunServerCommand extends BaseCommand {
options.addOption(null, OPTION_LOWMEM, false, "If this flag is set, the server will operate in low memory mode (some features disabled)");
options.addOption(null, OPTION_ALLOW_EXTERNAL_REFS, false, "If this flag is set, the server will allow resources to be persisted contaning external resource references");
options.addOption(null, OPTION_DISABLE_REFERENTIAL_INTEGRITY, false, "If this flag is set, the server will not enforce referential integrity");
Long defaultReuseSearchResults = DaoConfig.DEFAULT_REUSE_CACHED_SEARCH_RESULTS_FOR_MILLIS;
String defaultReuseSearchResultsStr = defaultReuseSearchResults == null ? "off" : String.valueOf(defaultReuseSearchResults);
options.addOption(null, OPTION_REUSE_SEARCH_RESULTS_MILLIS, true, "The time in milliseconds within which the same results will be returned for multiple identical searches, or \"off\" (default is " + defaultReuseSearchResultsStr + ")");
return options;
}
@ -58,7 +64,7 @@ public class RunServerCommand extends BaseCommand {
try {
return Integer.parseInt(theCommandLine.getOptionValue(opt, Integer.toString(defaultPort)));
} catch (NumberFormatException e) {
throw new ParseException("Invalid value '" + theCommandLine.getOptionValue(opt) + " (must be numeric)");
throw new ParseException("Invalid value '" + theCommandLine.getOptionValue(opt) + "' (must be numeric)");
}
}
@ -81,6 +87,25 @@ public class RunServerCommand extends BaseCommand {
ContextHolder.setDisableReferentialIntegrity(true);
}
String reuseSearchResults = theCommandLine.getOptionValue(OPTION_REUSE_SEARCH_RESULTS_MILLIS);
if (reuseSearchResults != null) {
if (reuseSearchResults.equals("off")) {
ourLog.info("Server is configured to not reuse search results");
ContextHolder.setReuseCachedSearchResultsForMillis(null);
} else {
try {
long reuseSearchResultsMillis = Long.parseLong(reuseSearchResults);
if (reuseSearchResultsMillis < 0) {
throw new NumberFormatException("expected a positive integer");
}
ourLog.info("Server is configured to reuse search results for " + String.valueOf(reuseSearchResultsMillis) + " milliseconds");
ContextHolder.setReuseCachedSearchResultsForMillis(reuseSearchResultsMillis);
} catch (NumberFormatException e) {
throw new ParseException("Invalid value '" + reuseSearchResults + "' (must be a positive integer)");
}
}
}
ContextHolder.setCtx(getSpecVersionContext(theCommandLine));
ourLog.info("Preparing HAPI FHIR JPA server on port {}", myPort);

View File

@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.demo;
import org.apache.commons.cli.ParseException;
import org.apache.commons.lang3.Validate;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.context.FhirContext;
public class ContextHolder {
@ -11,6 +12,11 @@ public class ContextHolder {
private static FhirContext ourCtx;
private static boolean ourDisableReferentialIntegrity;
private static String ourPath;
private static Long ourReuseSearchResultsMillis;
static {
ourReuseSearchResultsMillis = DaoConfig.DEFAULT_REUSE_CACHED_SEARCH_RESULTS_FOR_MILLIS;
}
public static FhirContext getCtx() {
Validate.notNull(ourPath, "Context not set");
@ -53,4 +59,11 @@ public class ContextHolder {
ourDisableReferentialIntegrity = theDisableReferentialIntegrity;
}
public static void setReuseCachedSearchResultsForMillis(Long reuseSearchResultsMillis) {
ourReuseSearchResultsMillis = reuseSearchResultsMillis;
}
public static Long getReuseCachedSearchResultsForMillis() {
return ourReuseSearchResultsMillis;
}
}

View File

@ -150,7 +150,7 @@ public class JpaServerDemo extends RestfulServer {
daoConfig.setAllowExternalReferences(ContextHolder.isAllowExternalRefs());
daoConfig.setEnforceReferentialIntegrityOnDelete(!ContextHolder.isDisableReferentialIntegrity());
daoConfig.setEnforceReferentialIntegrityOnWrite(!ContextHolder.isDisableReferentialIntegrity());
daoConfig.setReuseCachedSearchResultsForMillis(ContextHolder.getReuseCachedSearchResultsForMillis());
}
}

View File

@ -1,7 +1,7 @@
package ca.uhn.fhir.jpa.config.r4;
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
import org.hl7.fhir.r4.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.r4.hapi.validation.IValidationSupport;
import org.hl7.fhir.r4.utils.IResourceValidator.BestPracticeWarningLevel;
/*

View File

@ -105,6 +105,7 @@ public class FhirResourceDaoSubscriptionDstu2 extends FhirResourceDaoDstu2<Subsc
return retVal;
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
@Override
public int pollForNewUndeliveredResources() {
return pollForNewUndeliveredResources((String) null);

View File

@ -49,6 +49,7 @@ import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamUriDao;
import ca.uhn.fhir.jpa.entity.*;
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
import ca.uhn.fhir.jpa.term.VersionIndependentConcept;
import ca.uhn.fhir.jpa.util.BaseIterator;
import ca.uhn.fhir.jpa.util.StopWatch;
import ca.uhn.fhir.model.api.*;
import ca.uhn.fhir.model.base.composite.*;
@ -1965,7 +1966,7 @@ public class SearchBuilder implements ISearchBuilder {
return thePredicates.toArray(new Predicate[thePredicates.size()]);
}
public class IncludesIterator implements Iterator<Long>{
public class IncludesIterator extends BaseIterator<Long> implements Iterator<Long>{
private Iterator<Long> myCurrentIterator;
private int myCurrentOffset;
@ -2055,7 +2056,7 @@ public class SearchBuilder implements ISearchBuilder {
}
}
private final class QueryIterator implements Iterator<Long> {
private final class QueryIterator extends BaseIterator<Long> implements Iterator<Long> {
private boolean myFirst = true;
private IncludesIterator myIncludesIterator;
@ -2175,7 +2176,7 @@ public class SearchBuilder implements ISearchBuilder {
}
}
public class ScrollableResultsIterator implements Iterator<Long> {
public class ScrollableResultsIterator extends BaseIterator<Long> implements Iterator<Long> {
private Long myNext;
private ScrollableResults myScroll;

View File

@ -3,6 +3,9 @@ package ca.uhn.fhir.jpa.dao.data;
import java.util.Collection;
import java.util.Date;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
/*
* #%L
* HAPI FHIR JPA Server
@ -36,7 +39,7 @@ public interface ISearchDao extends JpaRepository<Search, Long> {
public Search findByUuid(@Param("uuid") String theUuid);
@Query("SELECT s.myId FROM Search s WHERE s.mySearchLastReturned < :cutoff")
public Collection<Long> findWhereLastReturnedBefore(@Param("cutoff") Date theCutoff);
public Slice<Long> findWhereLastReturnedBefore(@Param("cutoff") Date theCutoff, Pageable thePage);
// @Query("SELECT s FROM Search s WHERE s.myCreated < :cutoff")
// public Collection<Search> findWhereCreatedBefore(@Param("cutoff") Date theCutoff);

View File

@ -60,7 +60,7 @@ public class FhirResourceDaoSearchParameterDstu3 extends FhirResourceDaoDstu3<Se
ourLog.info("Marking all resources of type {} for reindexing due to updated search parameter with path: {}", expression);
TransactionTemplate txTemplate = new TransactionTemplate(myPlatformTransactionManager);
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
int updatedCount = txTemplate.execute(new TransactionCallback<Integer>() {
@Override
public Integer doInTransaction(TransactionStatus theStatus) {

View File

@ -104,6 +104,8 @@ public class FhirResourceDaoSubscriptionDstu3 extends FhirResourceDaoDstu3<Subsc
return retVal;
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
@Override
public int pollForNewUndeliveredResources() {
return pollForNewUndeliveredResources((String) null);
}

View File

@ -19,13 +19,16 @@ package ca.uhn.fhir.jpa.dao.dstu3;
* limitations under the License.
* #L%
*/
import static org.apache.commons.lang3.StringUtils.*;
import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.util.*;
import java.util.Map.Entry;
import javax.persistence.TypedQuery;
import org.apache.commons.lang3.Validate;
import org.apache.http.NameValuePair;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.dstu3.model.Bundle.*;
@ -85,6 +88,8 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
for (final BundleEntryComponent nextRequestEntry : theRequest.getEntry()) {
BaseServerResponseExceptionHolder caughtEx = new BaseServerResponseExceptionHolder();
TransactionCallback<Bundle> callback = new TransactionCallback<Bundle>() {
@Override
public Bundle doInTransaction(TransactionStatus theStatus) {
@ -97,13 +102,12 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
}
};
BaseServerResponseException caughtEx;
try {
Bundle nextResponseBundle = txTemplate.execute(callback);
caughtEx = null;
Bundle nextResponseBundle = callback.doInTransaction(null);
BundleEntryComponent subResponseEntry = nextResponseBundle.getEntry().get(0);
resp.addEntry(subResponseEntry);
/*
* If the individual entry didn't have a resource in its response, bring the sub-transaction's OperationOutcome across so the client can see it
*/
@ -112,21 +116,19 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
}
} catch (BaseServerResponseException e) {
caughtEx = e;
caughtEx.setException(e);
} catch (Throwable t) {
ourLog.error("Failure during BATCH sub transaction processing", t);
caughtEx = new InternalErrorException(t);
caughtEx.setException(new InternalErrorException(t));
}
if (caughtEx != null) {
if (caughtEx.getException() != null) {
BundleEntryComponent nextEntry = resp.addEntry();
OperationOutcome oo = new OperationOutcome();
oo.addIssue().setSeverity(IssueSeverity.ERROR).setDiagnostics(caughtEx.getMessage());
nextEntry.setResource(oo);
populateEntryWithOperationOutcome(caughtEx.getException(), nextEntry);
BundleEntryResponseComponent nextEntryResp = nextEntry.getResponse();
nextEntryResp.setStatus(toStatusString(caughtEx.getStatusCode()));
nextEntryResp.setStatus(toStatusString(caughtEx.getException().getStatusCode()));
}
}
@ -137,8 +139,7 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
return resp;
}
@SuppressWarnings("unchecked")
private Bundle doTransaction(ServletRequestDetails theRequestDetails, Bundle theRequest, String theActionName) {
private Bundle doTransaction(final ServletRequestDetails theRequestDetails, final Bundle theRequest, final String theActionName) {
BundleType transactionType = theRequest.getTypeElement().getValue();
if (transactionType == BundleType.BATCH) {
return batch(theRequestDetails, theRequest);
@ -156,11 +157,11 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
ourLog.info("Beginning {} with {} resources", theActionName, theRequest.getEntry().size());
long start = System.currentTimeMillis();
Date updateTime = new Date();
final Date updateTime = new Date();
Set<IdType> allIds = new LinkedHashSet<IdType>();
Map<IdType, IdType> idSubstitutions = new HashMap<IdType, IdType>();
Map<IdType, DaoMethodOutcome> idToPersistedOutcome = new HashMap<IdType, DaoMethodOutcome>();
final Set<IdType> allIds = new LinkedHashSet<IdType>();
final Map<IdType, IdType> idSubstitutions = new HashMap<IdType, IdType>();
final Map<IdType, DaoMethodOutcome> idToPersistedOutcome = new HashMap<IdType, DaoMethodOutcome>();
// Do all entries have a verb?
for (int i = 0; i < theRequest.getEntry().size(); i++) {
@ -181,9 +182,9 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
* are saved in a two-phase way in order to deal with interdependencies, and
* we want the GET processing to use the final indexing state
*/
Bundle response = new Bundle();
final Bundle response = new Bundle();
List<BundleEntryComponent> getEntries = new ArrayList<BundleEntryComponent>();
IdentityHashMap<BundleEntryComponent, Integer> originalRequestOrder = new IdentityHashMap<Bundle.BundleEntryComponent, Integer>();
final IdentityHashMap<BundleEntryComponent, Integer> originalRequestOrder = new IdentityHashMap<Bundle.BundleEntryComponent, Integer>();
for (int i = 0; i < theRequest.getEntry().size(); i++) {
originalRequestOrder.put(theRequest.getEntry().get(i), i);
response.addEntry();
@ -192,20 +193,13 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
}
}
Set<String> deletedResources = new HashSet<String>();
List<DeleteConflict> deleteConflicts = new ArrayList<DeleteConflict>();
Map<BundleEntryComponent, ResourceTable> entriesToProcess = new IdentityHashMap<BundleEntryComponent, ResourceTable>();
Set<ResourceTable> nonUpdatedEntities = new HashSet<ResourceTable>();
Map<String, Class<? extends IBaseResource>> conditionalRequestUrls = new HashMap<String, Class<? extends IBaseResource>>();
List<BundleEntryComponent> entries = new ArrayList<BundleEntryComponent>(theRequest.getEntry());
/*
* See FhirSystemDaoDstu3Test#testTransactionWithPlaceholderIdInMatchUrl
* Basically if the resource has a match URL that references a placeholder,
* we try to handle the resource with the placeholder first.
*/
Set<String> placeholderIds = new HashSet<String>();
final List<BundleEntryComponent> entries = theRequest.getEntry();
for (BundleEntryComponent nextEntry : entries) {
if (isNotBlank(nextEntry.getFullUrl()) && nextEntry.getFullUrl().startsWith(IdType.URN_PREFIX)) {
placeholderIds.add(nextEntry.getFullUrl());
@ -213,17 +207,124 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
}
Collections.sort(entries, new TransactionSorter(placeholderIds));
/*
* All of the write operations in the transaction (PUT, POST, etc.. basically anything
* except GET) are performed in their own database transaction before we do the reads.
* We do this because the reads (specifically the searches) often spawn their own
* secondary database transaction and if we allow that within the primary
* database transaction we can end up with deadlocks if the server is under
* heavy load with lots of concurrent transactions using all available
* database connections.
*/
TransactionTemplate txManager = new TransactionTemplate(myTxManager);
Map<BundleEntryComponent, ResourceTable> entriesToProcess = txManager.execute(new TransactionCallback<Map<BundleEntryComponent, ResourceTable>>() {
@Override
public Map<BundleEntryComponent, ResourceTable> doInTransaction(TransactionStatus status) {
return doTransactionWriteOperations(theRequestDetails, theRequest, theActionName, updateTime, allIds, idSubstitutions, idToPersistedOutcome, response, originalRequestOrder, entries);
}
});
for (Entry<BundleEntryComponent, ResourceTable> nextEntry : entriesToProcess.entrySet()) {
String responseLocation = nextEntry.getValue().getIdDt().toUnqualified().getValue();
String responseEtag = nextEntry.getValue().getIdDt().getVersionIdPart();
nextEntry.getKey().getResponse().setLocation(responseLocation);
nextEntry.getKey().getResponse().setEtag(responseEtag);
}
/*
* Loop through the request and process any entries of type GET
*/
for (int i = 0; i < getEntries.size(); i++) {
BundleEntryComponent nextReqEntry = getEntries.get(i);
Integer originalOrder = originalRequestOrder.get(nextReqEntry);
BundleEntryComponent nextRespEntry = response.getEntry().get(originalOrder);
ServletSubRequestDetails requestDetails = new ServletSubRequestDetails();
requestDetails.setServletRequest(theRequestDetails.getServletRequest());
requestDetails.setRequestType(RequestTypeEnum.GET);
requestDetails.setServer(theRequestDetails.getServer());
String url = extractTransactionUrlOrThrowException(nextReqEntry, HTTPVerb.GET);
int qIndex = url.indexOf('?');
ArrayListMultimap<String, String> paramValues = ArrayListMultimap.create();
requestDetails.setParameters(new HashMap<String, String[]>());
if (qIndex != -1) {
String params = url.substring(qIndex);
List<NameValuePair> parameters = translateMatchUrl(params);
for (NameValuePair next : parameters) {
paramValues.put(next.getName(), next.getValue());
}
for (java.util.Map.Entry<String, Collection<String>> nextParamEntry : paramValues.asMap().entrySet()) {
String[] nextValue = nextParamEntry.getValue().toArray(new String[nextParamEntry.getValue().size()]);
requestDetails.getParameters().put(nextParamEntry.getKey(), nextValue);
}
url = url.substring(0, qIndex);
}
requestDetails.setRequestPath(url);
requestDetails.setFhirServerBase(theRequestDetails.getFhirServerBase());
theRequestDetails.getServer().populateRequestDetailsFromRequestPath(requestDetails, url);
BaseMethodBinding<?> method = theRequestDetails.getServer().determineResourceMethod(requestDetails, url);
if (method == null) {
throw new IllegalArgumentException("Unable to handle GET " + url);
}
if (isNotBlank(nextReqEntry.getRequest().getIfMatch())) {
requestDetails.addHeader(Constants.HEADER_IF_MATCH, nextReqEntry.getRequest().getIfMatch());
}
if (isNotBlank(nextReqEntry.getRequest().getIfNoneExist())) {
requestDetails.addHeader(Constants.HEADER_IF_NONE_EXIST, nextReqEntry.getRequest().getIfNoneExist());
}
if (isNotBlank(nextReqEntry.getRequest().getIfNoneMatch())) {
requestDetails.addHeader(Constants.HEADER_IF_NONE_MATCH, nextReqEntry.getRequest().getIfNoneMatch());
}
Validate.isTrue(method instanceof BaseResourceReturningMethodBinding, "Unable to handle GET {}", url);
try {
IBaseResource resource = ((BaseResourceReturningMethodBinding) method).doInvokeServer(theRequestDetails.getServer(), requestDetails);
if (paramValues.containsKey(Constants.PARAM_SUMMARY) || paramValues.containsKey(Constants.PARAM_CONTENT)) {
resource = filterNestedBundle(requestDetails, resource);
}
nextRespEntry.setResource((Resource) resource);
nextRespEntry.getResponse().setStatus(toStatusString(Constants.STATUS_HTTP_200_OK));
} catch (NotModifiedException e) {
nextRespEntry.getResponse().setStatus(toStatusString(Constants.STATUS_HTTP_304_NOT_MODIFIED));
} catch (BaseServerResponseException e) {
ourLog.info("Failure processing transaction GET {}: {}", url, e.toString());
nextRespEntry.getResponse().setStatus(toStatusString(e.getStatusCode()));
populateEntryWithOperationOutcome(e, nextRespEntry);
}
}
long delay = System.currentTimeMillis() - start;
ourLog.info(theActionName + " completed in {}ms", new Object[] { delay });
response.setType(BundleType.TRANSACTIONRESPONSE);
return response;
}
@SuppressWarnings("unchecked")
private Map<BundleEntryComponent, ResourceTable> doTransactionWriteOperations(ServletRequestDetails theRequestDetails, Bundle theRequest, String theActionName, Date updateTime, Set<IdType> allIds,
Map<IdType, IdType> idSubstitutions, Map<IdType, DaoMethodOutcome> idToPersistedOutcome, Bundle response, IdentityHashMap<BundleEntryComponent, Integer> originalRequestOrder, List<BundleEntryComponent> theEntries) {
Set<String> deletedResources = new HashSet<String>();
List<DeleteConflict> deleteConflicts = new ArrayList<DeleteConflict>();
Map<BundleEntryComponent, ResourceTable> entriesToProcess = new IdentityHashMap<BundleEntryComponent, ResourceTable>();
Set<ResourceTable> nonUpdatedEntities = new HashSet<ResourceTable>();
Map<String, Class<? extends IBaseResource>> conditionalRequestUrls = new HashMap<String, Class<? extends IBaseResource>>();
/*
* Loop through the request and process any entries of type
* PUT, POST or DELETE
*/
for (int i = 0; i < entries.size(); i++) {
for (int i = 0; i < theEntries.size(); i++) {
if (i % 100 == 0) {
ourLog.info("Processed {} non-GET entries out of {}", i, entries.size());
ourLog.info("Processed {} non-GET entries out of {}", i, theEntries.size());
}
BundleEntryComponent nextReqEntry = entries.get(i);
BundleEntryComponent nextReqEntry = theEntries.get(i);
Resource res = nextReqEntry.getResource();
IdType nextResourceId = null;
if (res != null) {
@ -435,86 +536,7 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
}
ourLog.info("Placeholder resource ID \"{}\" was replaced with permanent ID \"{}\"", next, replacement);
}
/*
* Loop through the request and process any entries of type GET
*/
for (int i = 0; i < getEntries.size(); i++) {
BundleEntryComponent nextReqEntry = getEntries.get(i);
Integer originalOrder = originalRequestOrder.get(nextReqEntry);
BundleEntryComponent nextRespEntry = response.getEntry().get(originalOrder);
ServletSubRequestDetails requestDetails = new ServletSubRequestDetails();
requestDetails.setServletRequest(theRequestDetails.getServletRequest());
requestDetails.setRequestType(RequestTypeEnum.GET);
requestDetails.setServer(theRequestDetails.getServer());
String url = extractTransactionUrlOrThrowException(nextReqEntry, HTTPVerb.GET);
int qIndex = url.indexOf('?');
ArrayListMultimap<String, String> paramValues = ArrayListMultimap.create();
requestDetails.setParameters(new HashMap<String, String[]>());
if (qIndex != -1) {
String params = url.substring(qIndex);
List<NameValuePair> parameters = translateMatchUrl(params);
for (NameValuePair next : parameters) {
paramValues.put(next.getName(), next.getValue());
}
for (java.util.Map.Entry<String, Collection<String>> nextParamEntry : paramValues.asMap().entrySet()) {
String[] nextValue = nextParamEntry.getValue().toArray(new String[nextParamEntry.getValue().size()]);
requestDetails.getParameters().put(nextParamEntry.getKey(), nextValue);
}
url = url.substring(0, qIndex);
}
requestDetails.setRequestPath(url);
requestDetails.setFhirServerBase(theRequestDetails.getFhirServerBase());
theRequestDetails.getServer().populateRequestDetailsFromRequestPath(requestDetails, url);
BaseMethodBinding<?> method = theRequestDetails.getServer().determineResourceMethod(requestDetails, url);
if (method == null) {
throw new IllegalArgumentException("Unable to handle GET " + url);
}
if (isNotBlank(nextReqEntry.getRequest().getIfMatch())) {
requestDetails.addHeader(Constants.HEADER_IF_MATCH, nextReqEntry.getRequest().getIfMatch());
}
if (isNotBlank(nextReqEntry.getRequest().getIfNoneExist())) {
requestDetails.addHeader(Constants.HEADER_IF_NONE_EXIST, nextReqEntry.getRequest().getIfNoneExist());
}
if (isNotBlank(nextReqEntry.getRequest().getIfNoneMatch())) {
requestDetails.addHeader(Constants.HEADER_IF_NONE_MATCH, nextReqEntry.getRequest().getIfNoneMatch());
}
if (method instanceof BaseResourceReturningMethodBinding) {
try {
IBaseResource resource = ((BaseResourceReturningMethodBinding) method).doInvokeServer(theRequestDetails.getServer(), requestDetails);
if (paramValues.containsKey(Constants.PARAM_SUMMARY) || paramValues.containsKey(Constants.PARAM_CONTENT)) {
resource = filterNestedBundle(requestDetails, resource);
}
nextRespEntry.setResource((Resource) resource);
nextRespEntry.getResponse().setStatus(toStatusString(Constants.STATUS_HTTP_200_OK));
} catch (NotModifiedException e) {
nextRespEntry.getResponse().setStatus(toStatusString(Constants.STATUS_HTTP_304_NOT_MODIFIED));
}
} else {
throw new IllegalArgumentException("Unable to handle GET " + url);
}
}
for (Entry<BundleEntryComponent, ResourceTable> nextEntry : entriesToProcess.entrySet()) {
String responseLocation = nextEntry.getValue().getIdDt().toUnqualified().getValue();
String responseEtag = nextEntry.getValue().getIdDt().getVersionIdPart();
nextEntry.getKey().getResponse().setLocation(responseLocation);
nextEntry.getKey().getResponse().setEtag(responseEtag);
}
long delay = System.currentTimeMillis() - start;
ourLog.info(theActionName + " completed in {}ms", new Object[] { delay });
response.setType(BundleType.TRANSACTIONRESPONSE);
return response;
return entriesToProcess;
}
private String extractTransactionUrlOrThrowException(BundleEntryComponent nextEntry, HTTPVerb verb) {
@ -562,20 +584,10 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
return retVal;
}
private String performIdSubstitutionsInMatchUrl(Map<IdType, IdType> theIdSubstitutions, String theMatchUrl) {
String matchUrl = theMatchUrl;
if (isNotBlank(matchUrl)) {
for (Entry<IdType, IdType> nextSubstitutionEntry : theIdSubstitutions.entrySet()) {
IdType nextTemporaryId = nextSubstitutionEntry.getKey();
IdType nextReplacementId = nextSubstitutionEntry.getValue();
String nextTemporaryIdPart = nextTemporaryId.getIdPart();
String nextReplacementIdPart = nextReplacementId.getValueAsString();
if (nextTemporaryId.isUrn() && nextTemporaryIdPart.length() > IdType.URN_PREFIX.length()) {
matchUrl = matchUrl.replace(nextTemporaryIdPart, nextReplacementIdPart);
}
}
}
return matchUrl;
private void populateEntryWithOperationOutcome(BaseServerResponseException caughtEx, BundleEntryComponent nextEntry) {
OperationOutcome oo = new OperationOutcome();
oo.addIssue().setSeverity(IssueSeverity.ERROR).setDiagnostics(caughtEx.getMessage());
nextEntry.getResponse().setOutcome(oo);
}
private ca.uhn.fhir.jpa.dao.IFhirResourceDao<? extends IBaseResource> toDao(UrlParts theParts, String theVerb, String theUrl) {
@ -621,7 +633,7 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
return retVal;
}
@Transactional(propagation = Propagation.REQUIRED)
@Transactional(propagation = Propagation.NEVER)
@Override
public Bundle transaction(RequestDetails theRequestDetails, Bundle theRequest) {
if (theRequestDetails != null) {
@ -633,6 +645,26 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
return transaction((ServletRequestDetails) theRequestDetails, theRequest, actionName);
}
private String performIdSubstitutionsInMatchUrl(Map<IdType, IdType> theIdSubstitutions, String theMatchUrl) {
String matchUrl = theMatchUrl;
if (isNotBlank(matchUrl)) {
for (Entry<IdType, IdType> nextSubstitutionEntry : theIdSubstitutions.entrySet()) {
IdType nextTemporaryId = nextSubstitutionEntry.getKey();
IdType nextReplacementId = nextSubstitutionEntry.getValue();
String nextTemporaryIdPart = nextTemporaryId.getIdPart();
String nextReplacementIdPart = nextReplacementId.getValueAsString();
if (nextTemporaryId.isUrn() && nextTemporaryIdPart.length() > IdType.URN_PREFIX.length()) {
matchUrl = matchUrl.replace(nextTemporaryIdPart, nextReplacementIdPart);
}
}
}
return matchUrl;
}
private Bundle transaction(ServletRequestDetails theRequestDetails, Bundle theRequest, String theActionName) {
super.markRequestAsProcessingSubRequest(theRequestDetails);
try {
@ -690,6 +722,20 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
return Integer.toString(theStatusCode) + " " + defaultString(Constants.HTTP_STATUS_NAMES.get(theStatusCode));
}
private static class BaseServerResponseExceptionHolder
{
private BaseServerResponseException myException;
public BaseServerResponseException getException() {
return myException;
}
public void setException(BaseServerResponseException myException) {
this.myException = myException;
}
}
//@formatter:off
/**
* Transaction Order, per the spec:
*
@ -698,6 +744,7 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
* Process any PUT interactions
* Process any GET interactions
*/
//@formatter:off
public class TransactionSorter implements Comparator<BundleEntryComponent> {
private Set<String> myPlaceholderIds;

View File

@ -24,8 +24,8 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.util.*;
import org.hl7.fhir.r4.hapi.validation.IValidationSupport.CodeValidationResult;
import org.hl7.fhir.r4.hapi.validation.ValidationSupportChain;
import org.hl7.fhir.r4.hapi.ctx.ValidationSupportChain;
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport.CodeValidationResult;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.CodeSystem.CodeSystemContentMode;
import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionComponent;

View File

@ -27,8 +27,8 @@ import java.util.Collections;
import java.util.List;
import org.apache.commons.codec.binary.StringUtils;
import org.hl7.fhir.r4.hapi.validation.HapiWorkerContext;
import org.hl7.fhir.r4.hapi.validation.IValidationSupport;
import org.hl7.fhir.r4.hapi.ctx.HapiWorkerContext;
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.Enumerations.PublicationStatus;
import org.hl7.fhir.r4.model.ValueSet.*;

View File

@ -19,13 +19,16 @@ package ca.uhn.fhir.jpa.dao.r4;
* limitations under the License.
* #L%
*/
import static org.apache.commons.lang3.StringUtils.*;
import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.util.*;
import java.util.Map.Entry;
import javax.persistence.TypedQuery;
import org.apache.commons.lang3.Validate;
import org.apache.http.NameValuePair;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.Bundle.*;
@ -85,6 +88,8 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
for (final BundleEntryComponent nextRequestEntry : theRequest.getEntry()) {
BaseServerResponseExceptionHolder caughtEx = new BaseServerResponseExceptionHolder();
TransactionCallback<Bundle> callback = new TransactionCallback<Bundle>() {
@Override
public Bundle doInTransaction(TransactionStatus theStatus) {
@ -97,13 +102,12 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
}
};
BaseServerResponseException caughtEx;
try {
Bundle nextResponseBundle = txTemplate.execute(callback);
caughtEx = null;
Bundle nextResponseBundle = callback.doInTransaction(null);
BundleEntryComponent subResponseEntry = nextResponseBundle.getEntry().get(0);
resp.addEntry(subResponseEntry);
/*
* If the individual entry didn't have a resource in its response, bring the sub-transaction's OperationOutcome across so the client can see it
*/
@ -112,21 +116,19 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
}
} catch (BaseServerResponseException e) {
caughtEx = e;
caughtEx.setException(e);
} catch (Throwable t) {
ourLog.error("Failure during BATCH sub transaction processing", t);
caughtEx = new InternalErrorException(t);
caughtEx.setException(new InternalErrorException(t));
}
if (caughtEx != null) {
if (caughtEx.getException() != null) {
BundleEntryComponent nextEntry = resp.addEntry();
OperationOutcome oo = new OperationOutcome();
oo.addIssue().setSeverity(IssueSeverity.ERROR).setDiagnostics(caughtEx.getMessage());
nextEntry.setResource(oo);
populateEntryWithOperationOutcome(caughtEx.getException(), nextEntry);
BundleEntryResponseComponent nextEntryResp = nextEntry.getResponse();
nextEntryResp.setStatus(toStatusString(caughtEx.getStatusCode()));
nextEntryResp.setStatus(toStatusString(caughtEx.getException().getStatusCode()));
}
}
@ -137,8 +139,7 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
return resp;
}
@SuppressWarnings("unchecked")
private Bundle doTransaction(ServletRequestDetails theRequestDetails, Bundle theRequest, String theActionName) {
private Bundle doTransaction(final ServletRequestDetails theRequestDetails, final Bundle theRequest, final String theActionName) {
BundleType transactionType = theRequest.getTypeElement().getValue();
if (transactionType == BundleType.BATCH) {
return batch(theRequestDetails, theRequest);
@ -156,11 +157,11 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
ourLog.info("Beginning {} with {} resources", theActionName, theRequest.getEntry().size());
long start = System.currentTimeMillis();
Date updateTime = new Date();
final Date updateTime = new Date();
Set<IdType> allIds = new LinkedHashSet<IdType>();
Map<IdType, IdType> idSubstitutions = new HashMap<IdType, IdType>();
Map<IdType, DaoMethodOutcome> idToPersistedOutcome = new HashMap<IdType, DaoMethodOutcome>();
final Set<IdType> allIds = new LinkedHashSet<IdType>();
final Map<IdType, IdType> idSubstitutions = new HashMap<IdType, IdType>();
final Map<IdType, DaoMethodOutcome> idToPersistedOutcome = new HashMap<IdType, DaoMethodOutcome>();
// Do all entries have a verb?
for (int i = 0; i < theRequest.getEntry().size(); i++) {
@ -181,9 +182,9 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
* are saved in a two-phase way in order to deal with interdependencies, and
* we want the GET processing to use the final indexing state
*/
Bundle response = new Bundle();
final Bundle response = new Bundle();
List<BundleEntryComponent> getEntries = new ArrayList<BundleEntryComponent>();
IdentityHashMap<BundleEntryComponent, Integer> originalRequestOrder = new IdentityHashMap<Bundle.BundleEntryComponent, Integer>();
final IdentityHashMap<BundleEntryComponent, Integer> originalRequestOrder = new IdentityHashMap<Bundle.BundleEntryComponent, Integer>();
for (int i = 0; i < theRequest.getEntry().size(); i++) {
originalRequestOrder.put(theRequest.getEntry().get(i), i);
response.addEntry();
@ -192,20 +193,13 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
}
}
Set<String> deletedResources = new HashSet<String>();
List<DeleteConflict> deleteConflicts = new ArrayList<DeleteConflict>();
Map<BundleEntryComponent, ResourceTable> entriesToProcess = new IdentityHashMap<BundleEntryComponent, ResourceTable>();
Set<ResourceTable> nonUpdatedEntities = new HashSet<ResourceTable>();
Map<String, Class<? extends IBaseResource>> conditionalRequestUrls = new HashMap<String, Class<? extends IBaseResource>>();
List<BundleEntryComponent> entries = new ArrayList<BundleEntryComponent>(theRequest.getEntry());
/*
* See FhirSystemDaoDstu3Test#testTransactionWithPlaceholderIdInMatchUrl
* Basically if the resource has a match URL that references a placeholder,
* we try to handle the resource with the placeholder first.
*/
Set<String> placeholderIds = new HashSet<String>();
final List<BundleEntryComponent> entries = theRequest.getEntry();
for (BundleEntryComponent nextEntry : entries) {
if (isNotBlank(nextEntry.getFullUrl()) && nextEntry.getFullUrl().startsWith(IdType.URN_PREFIX)) {
placeholderIds.add(nextEntry.getFullUrl());
@ -213,17 +207,124 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
}
Collections.sort(entries, new TransactionSorter(placeholderIds));
/*
* All of the write operations in the transaction (PUT, POST, etc.. basically anything
* except GET) are performed in their own database transaction before we do the reads.
* We do this because the reads (specifically the searches) often spawn their own
* secondary database transaction and if we allow that within the primary
* database transaction we can end up with deadlocks if the server is under
* heavy load with lots of concurrent transactions using all available
* database connections.
*/
TransactionTemplate txManager = new TransactionTemplate(myTxManager);
Map<BundleEntryComponent, ResourceTable> entriesToProcess = txManager.execute(new TransactionCallback<Map<BundleEntryComponent, ResourceTable>>() {
@Override
public Map<BundleEntryComponent, ResourceTable> doInTransaction(TransactionStatus status) {
return doTransactionWriteOperations(theRequestDetails, theRequest, theActionName, updateTime, allIds, idSubstitutions, idToPersistedOutcome, response, originalRequestOrder, entries);
}
});
for (Entry<BundleEntryComponent, ResourceTable> nextEntry : entriesToProcess.entrySet()) {
String responseLocation = nextEntry.getValue().getIdDt().toUnqualified().getValue();
String responseEtag = nextEntry.getValue().getIdDt().getVersionIdPart();
nextEntry.getKey().getResponse().setLocation(responseLocation);
nextEntry.getKey().getResponse().setEtag(responseEtag);
}
/*
* Loop through the request and process any entries of type GET
*/
for (int i = 0; i < getEntries.size(); i++) {
BundleEntryComponent nextReqEntry = getEntries.get(i);
Integer originalOrder = originalRequestOrder.get(nextReqEntry);
BundleEntryComponent nextRespEntry = response.getEntry().get(originalOrder);
ServletSubRequestDetails requestDetails = new ServletSubRequestDetails();
requestDetails.setServletRequest(theRequestDetails.getServletRequest());
requestDetails.setRequestType(RequestTypeEnum.GET);
requestDetails.setServer(theRequestDetails.getServer());
String url = extractTransactionUrlOrThrowException(nextReqEntry, HTTPVerb.GET);
int qIndex = url.indexOf('?');
ArrayListMultimap<String, String> paramValues = ArrayListMultimap.create();
requestDetails.setParameters(new HashMap<String, String[]>());
if (qIndex != -1) {
String params = url.substring(qIndex);
List<NameValuePair> parameters = translateMatchUrl(params);
for (NameValuePair next : parameters) {
paramValues.put(next.getName(), next.getValue());
}
for (java.util.Map.Entry<String, Collection<String>> nextParamEntry : paramValues.asMap().entrySet()) {
String[] nextValue = nextParamEntry.getValue().toArray(new String[nextParamEntry.getValue().size()]);
requestDetails.getParameters().put(nextParamEntry.getKey(), nextValue);
}
url = url.substring(0, qIndex);
}
requestDetails.setRequestPath(url);
requestDetails.setFhirServerBase(theRequestDetails.getFhirServerBase());
theRequestDetails.getServer().populateRequestDetailsFromRequestPath(requestDetails, url);
BaseMethodBinding<?> method = theRequestDetails.getServer().determineResourceMethod(requestDetails, url);
if (method == null) {
throw new IllegalArgumentException("Unable to handle GET " + url);
}
if (isNotBlank(nextReqEntry.getRequest().getIfMatch())) {
requestDetails.addHeader(Constants.HEADER_IF_MATCH, nextReqEntry.getRequest().getIfMatch());
}
if (isNotBlank(nextReqEntry.getRequest().getIfNoneExist())) {
requestDetails.addHeader(Constants.HEADER_IF_NONE_EXIST, nextReqEntry.getRequest().getIfNoneExist());
}
if (isNotBlank(nextReqEntry.getRequest().getIfNoneMatch())) {
requestDetails.addHeader(Constants.HEADER_IF_NONE_MATCH, nextReqEntry.getRequest().getIfNoneMatch());
}
Validate.isTrue(method instanceof BaseResourceReturningMethodBinding, "Unable to handle GET {}", url);
try {
IBaseResource resource = ((BaseResourceReturningMethodBinding) method).doInvokeServer(theRequestDetails.getServer(), requestDetails);
if (paramValues.containsKey(Constants.PARAM_SUMMARY) || paramValues.containsKey(Constants.PARAM_CONTENT)) {
resource = filterNestedBundle(requestDetails, resource);
}
nextRespEntry.setResource((Resource) resource);
nextRespEntry.getResponse().setStatus(toStatusString(Constants.STATUS_HTTP_200_OK));
} catch (NotModifiedException e) {
nextRespEntry.getResponse().setStatus(toStatusString(Constants.STATUS_HTTP_304_NOT_MODIFIED));
} catch (BaseServerResponseException e) {
ourLog.info("Failure processing transaction GET {}: {}", url, e.toString());
nextRespEntry.getResponse().setStatus(toStatusString(e.getStatusCode()));
populateEntryWithOperationOutcome(e, nextRespEntry);
}
}
long delay = System.currentTimeMillis() - start;
ourLog.info(theActionName + " completed in {}ms", new Object[] { delay });
response.setType(BundleType.TRANSACTIONRESPONSE);
return response;
}
@SuppressWarnings("unchecked")
private Map<BundleEntryComponent, ResourceTable> doTransactionWriteOperations(ServletRequestDetails theRequestDetails, Bundle theRequest, String theActionName, Date updateTime, Set<IdType> allIds,
Map<IdType, IdType> idSubstitutions, Map<IdType, DaoMethodOutcome> idToPersistedOutcome, Bundle response, IdentityHashMap<BundleEntryComponent, Integer> originalRequestOrder, List<BundleEntryComponent> theEntries) {
Set<String> deletedResources = new HashSet<String>();
List<DeleteConflict> deleteConflicts = new ArrayList<DeleteConflict>();
Map<BundleEntryComponent, ResourceTable> entriesToProcess = new IdentityHashMap<BundleEntryComponent, ResourceTable>();
Set<ResourceTable> nonUpdatedEntities = new HashSet<ResourceTable>();
Map<String, Class<? extends IBaseResource>> conditionalRequestUrls = new HashMap<String, Class<? extends IBaseResource>>();
/*
* Loop through the request and process any entries of type
* PUT, POST or DELETE
*/
for (int i = 0; i < entries.size(); i++) {
for (int i = 0; i < theEntries.size(); i++) {
if (i % 100 == 0) {
ourLog.info("Processed {} non-GET entries out of {}", i, entries.size());
ourLog.info("Processed {} non-GET entries out of {}", i, theEntries.size());
}
BundleEntryComponent nextReqEntry = entries.get(i);
BundleEntryComponent nextReqEntry = theEntries.get(i);
Resource res = nextReqEntry.getResource();
IdType nextResourceId = null;
if (res != null) {
@ -435,86 +536,7 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
}
ourLog.info("Placeholder resource ID \"{}\" was replaced with permanent ID \"{}\"", next, replacement);
}
/*
* Loop through the request and process any entries of type GET
*/
for (int i = 0; i < getEntries.size(); i++) {
BundleEntryComponent nextReqEntry = getEntries.get(i);
Integer originalOrder = originalRequestOrder.get(nextReqEntry);
BundleEntryComponent nextRespEntry = response.getEntry().get(originalOrder);
ServletSubRequestDetails requestDetails = new ServletSubRequestDetails();
requestDetails.setServletRequest(theRequestDetails.getServletRequest());
requestDetails.setRequestType(RequestTypeEnum.GET);
requestDetails.setServer(theRequestDetails.getServer());
String url = extractTransactionUrlOrThrowException(nextReqEntry, HTTPVerb.GET);
int qIndex = url.indexOf('?');
ArrayListMultimap<String, String> paramValues = ArrayListMultimap.create();
requestDetails.setParameters(new HashMap<String, String[]>());
if (qIndex != -1) {
String params = url.substring(qIndex);
List<NameValuePair> parameters = translateMatchUrl(params);
for (NameValuePair next : parameters) {
paramValues.put(next.getName(), next.getValue());
}
for (java.util.Map.Entry<String, Collection<String>> nextParamEntry : paramValues.asMap().entrySet()) {
String[] nextValue = nextParamEntry.getValue().toArray(new String[nextParamEntry.getValue().size()]);
requestDetails.getParameters().put(nextParamEntry.getKey(), nextValue);
}
url = url.substring(0, qIndex);
}
requestDetails.setRequestPath(url);
requestDetails.setFhirServerBase(theRequestDetails.getFhirServerBase());
theRequestDetails.getServer().populateRequestDetailsFromRequestPath(requestDetails, url);
BaseMethodBinding<?> method = theRequestDetails.getServer().determineResourceMethod(requestDetails, url);
if (method == null) {
throw new IllegalArgumentException("Unable to handle GET " + url);
}
if (isNotBlank(nextReqEntry.getRequest().getIfMatch())) {
requestDetails.addHeader(Constants.HEADER_IF_MATCH, nextReqEntry.getRequest().getIfMatch());
}
if (isNotBlank(nextReqEntry.getRequest().getIfNoneExist())) {
requestDetails.addHeader(Constants.HEADER_IF_NONE_EXIST, nextReqEntry.getRequest().getIfNoneExist());
}
if (isNotBlank(nextReqEntry.getRequest().getIfNoneMatch())) {
requestDetails.addHeader(Constants.HEADER_IF_NONE_MATCH, nextReqEntry.getRequest().getIfNoneMatch());
}
if (method instanceof BaseResourceReturningMethodBinding) {
try {
IBaseResource resource = ((BaseResourceReturningMethodBinding) method).doInvokeServer(theRequestDetails.getServer(), requestDetails);
if (paramValues.containsKey(Constants.PARAM_SUMMARY) || paramValues.containsKey(Constants.PARAM_CONTENT)) {
resource = filterNestedBundle(requestDetails, resource);
}
nextRespEntry.setResource((Resource) resource);
nextRespEntry.getResponse().setStatus(toStatusString(Constants.STATUS_HTTP_200_OK));
} catch (NotModifiedException e) {
nextRespEntry.getResponse().setStatus(toStatusString(Constants.STATUS_HTTP_304_NOT_MODIFIED));
}
} else {
throw new IllegalArgumentException("Unable to handle GET " + url);
}
}
for (Entry<BundleEntryComponent, ResourceTable> nextEntry : entriesToProcess.entrySet()) {
String responseLocation = nextEntry.getValue().getIdDt().toUnqualified().getValue();
String responseEtag = nextEntry.getValue().getIdDt().getVersionIdPart();
nextEntry.getKey().getResponse().setLocation(responseLocation);
nextEntry.getKey().getResponse().setEtag(responseEtag);
}
long delay = System.currentTimeMillis() - start;
ourLog.info(theActionName + " completed in {}ms", new Object[] { delay });
response.setType(BundleType.TRANSACTIONRESPONSE);
return response;
return entriesToProcess;
}
private String extractTransactionUrlOrThrowException(BundleEntryComponent nextEntry, HTTPVerb verb) {
@ -562,20 +584,10 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
return retVal;
}
private String performIdSubstitutionsInMatchUrl(Map<IdType, IdType> theIdSubstitutions, String theMatchUrl) {
String matchUrl = theMatchUrl;
if (isNotBlank(matchUrl)) {
for (Entry<IdType, IdType> nextSubstitutionEntry : theIdSubstitutions.entrySet()) {
IdType nextTemporaryId = nextSubstitutionEntry.getKey();
IdType nextReplacementId = nextSubstitutionEntry.getValue();
String nextTemporaryIdPart = nextTemporaryId.getIdPart();
String nextReplacementIdPart = nextReplacementId.getValueAsString();
if (nextTemporaryId.isUrn() && nextTemporaryIdPart.length() > IdType.URN_PREFIX.length()) {
matchUrl = matchUrl.replace(nextTemporaryIdPart, nextReplacementIdPart);
}
}
}
return matchUrl;
private void populateEntryWithOperationOutcome(BaseServerResponseException caughtEx, BundleEntryComponent nextEntry) {
OperationOutcome oo = new OperationOutcome();
oo.addIssue().setSeverity(IssueSeverity.ERROR).setDiagnostics(caughtEx.getMessage());
nextEntry.getResponse().setOutcome(oo);
}
private ca.uhn.fhir.jpa.dao.IFhirResourceDao<? extends IBaseResource> toDao(UrlParts theParts, String theVerb, String theUrl) {
@ -621,7 +633,7 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
return retVal;
}
@Transactional(propagation = Propagation.REQUIRED)
@Transactional(propagation = Propagation.NEVER)
@Override
public Bundle transaction(RequestDetails theRequestDetails, Bundle theRequest) {
if (theRequestDetails != null) {
@ -633,6 +645,26 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
return transaction((ServletRequestDetails) theRequestDetails, theRequest, actionName);
}
private String performIdSubstitutionsInMatchUrl(Map<IdType, IdType> theIdSubstitutions, String theMatchUrl) {
String matchUrl = theMatchUrl;
if (isNotBlank(matchUrl)) {
for (Entry<IdType, IdType> nextSubstitutionEntry : theIdSubstitutions.entrySet()) {
IdType nextTemporaryId = nextSubstitutionEntry.getKey();
IdType nextReplacementId = nextSubstitutionEntry.getValue();
String nextTemporaryIdPart = nextTemporaryId.getIdPart();
String nextReplacementIdPart = nextReplacementId.getValueAsString();
if (nextTemporaryId.isUrn() && nextTemporaryIdPart.length() > IdType.URN_PREFIX.length()) {
matchUrl = matchUrl.replace(nextTemporaryIdPart, nextReplacementIdPart);
}
}
}
return matchUrl;
}
private Bundle transaction(ServletRequestDetails theRequestDetails, Bundle theRequest, String theActionName) {
super.markRequestAsProcessingSubRequest(theRequestDetails);
try {
@ -690,6 +722,20 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
return Integer.toString(theStatusCode) + " " + defaultString(Constants.HTTP_STATUS_NAMES.get(theStatusCode));
}
private static class BaseServerResponseExceptionHolder
{
private BaseServerResponseException myException;
public BaseServerResponseException getException() {
return myException;
}
public void setException(BaseServerResponseException myException) {
this.myException = myException;
}
}
//@formatter:off
/**
* Transaction Order, per the spec:
*
@ -698,6 +744,7 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
* Process any PUT interactions
* Process any GET interactions
*/
//@formatter:off
public class TransactionSorter implements Comparator<BundleEntryComponent> {
private Set<String> myPlaceholderIds;

View File

@ -1,6 +1,6 @@
package ca.uhn.fhir.jpa.dao.r4;
import org.hl7.fhir.r4.hapi.validation.IValidationSupport;
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
/*
* #%L

View File

@ -31,7 +31,7 @@ import javax.measure.unit.Unit;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.hl7.fhir.r4.context.IWorkerContext;
import org.hl7.fhir.r4.hapi.validation.IValidationSupport;
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestSecurityComponent;
import org.hl7.fhir.r4.model.Enumeration;
@ -55,7 +55,7 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParamExtractorR4.class);
@Autowired
private org.hl7.fhir.r4.hapi.validation.IValidationSupport myValidationSupport;
private org.hl7.fhir.r4.hapi.ctx.IValidationSupport myValidationSupport;
/**
* Constructor
@ -686,7 +686,7 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
*/
@Override
protected List<Object> extractValues(String thePaths, IBaseResource theResource) {
IWorkerContext worker = new org.hl7.fhir.r4.hapi.validation.HapiWorkerContext(getContext(), myValidationSupport);
IWorkerContext worker = new org.hl7.fhir.r4.hapi.ctx.HapiWorkerContext(getContext(), myValidationSupport);
FHIRPathEngine fp = new FHIRPathEngine(worker);
List<Object> values = new ArrayList<Object>();
@ -732,7 +732,7 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
}
@VisibleForTesting
void setValidationSupportForTesting(org.hl7.fhir.r4.hapi.validation.IValidationSupport theValidationSupport) {
void setValidationSupportForTesting(org.hl7.fhir.r4.hapi.ctx.IValidationSupport theValidationSupport) {
myValidationSupport = theValidationSupport;
}

View File

@ -34,12 +34,10 @@ import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
//@formatter:off
@Entity
@Table(name = "HFJ_SEARCH_RESULT", uniqueConstraints= {
@UniqueConstraint(name="IDX_SEARCHRES_ORDER", columnNames= {"SEARCH_PID", "SEARCH_ORDER"})
})
//@formatter:on
public class SearchResult implements Serializable {
private static final long serialVersionUID = 1L;

View File

@ -79,7 +79,12 @@ public class RestHookSubscriptionDstu2Interceptor extends BaseRestHookSubscripti
* @param theOperation
*/
private void checkSubscriptions(IIdType idType, String resourceType, RestOperationTypeEnum theOperation) {
for (Subscription subscription : myRestHookSubscriptions) {
//avoid a ConcurrentModificationException by copying to an array
for (Object object : myRestHookSubscriptions.toArray()) {
if (object == null) {
continue;
}
Subscription subscription = (Subscription) object;
// see if the criteria matches the created object
ourLog.info("Checking subscription {} for {} with criteria {}", subscription.getIdElement().getIdPart(), resourceType, subscription.getCriteria());
@ -117,6 +122,8 @@ public class RestHookSubscriptionDstu2Interceptor extends BaseRestHookSubscripti
}
}
/**
* Creates an HTTP Post for a subscription
*/

View File

@ -76,7 +76,13 @@ public class RestHookSubscriptionDstu3Interceptor extends BaseRestHookSubscripti
* @param theOperation
*/
private void checkSubscriptions(IIdType idType, String resourceType, RestOperationTypeEnum theOperation) {
for (Subscription subscription : myRestHookSubscriptions) {
//avoid a ConcurrentModificationException by copying to an array
for (Object object : myRestHookSubscriptions.toArray()) {
//for (Subscription subscription : myRestHookSubscriptions) {
if (object == null) {
continue;
}
Subscription subscription = (Subscription) object;
// see if the criteria matches the created object
ourLog.info("Checking subscription {} for {} with criteria {}", subscription.getIdElement().getIdPart(), resourceType, subscription.getCriteria());

View File

@ -27,8 +27,7 @@ import javax.persistence.criteria.*;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.transaction.support.*;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.IDao;
@ -103,16 +102,23 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
return retVal;
}
protected List<IBaseResource> doSearchOrEverythingInTransaction(final int theFromIndex, final int theToIndex) {
ISearchBuilder sb = myDao.newSearchBuilder();
protected List<IBaseResource> doSearchOrEverything(final int theFromIndex, final int theToIndex) {
final ISearchBuilder sb = myDao.newSearchBuilder();
String resourceName = mySearchEntity.getResourceType();
Class<? extends IBaseResource> resourceType = myContext.getResourceDefinition(resourceName).getImplementingClass();
sb.setType(resourceType, resourceName);
List<Long> pidsSubList = mySearchCoordinatorSvc.getResources(myUuid, theFromIndex, theToIndex);
final List<Long> pidsSubList = mySearchCoordinatorSvc.getResources(myUuid, theFromIndex, theToIndex);
return toResourceList(sb, pidsSubList);
TransactionTemplate template = new TransactionTemplate(myPlatformTransactionManager);
template.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRED);
return template.execute(new TransactionCallback<List<IBaseResource>>() {
@Override
public List<IBaseResource> doInTransaction(TransactionStatus theStatus) {
return toResourceList(sb, pidsSubList);
}
});
}
private void ensureDependenciesInjected() {
@ -165,22 +171,26 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
TransactionTemplate template = new TransactionTemplate(myPlatformTransactionManager);
return template.execute(new TransactionCallback<List<IBaseResource>>() {
template.execute(new TransactionCallbackWithoutResult() {
@Override
public List<IBaseResource> doInTransaction(TransactionStatus theStatus) {
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
ensureSearchEntityLoaded();
switch (mySearchEntity.getSearchType()) {
case HISTORY:
return doHistoryInTransaction(theFromIndex, theToIndex);
case SEARCH:
case EVERYTHING:
default:
return doSearchOrEverythingInTransaction(theFromIndex, theToIndex);
}
}
});
switch (mySearchEntity.getSearchType()) {
case HISTORY:
return template.execute(new TransactionCallback<List<IBaseResource>>() {
@Override
public List<IBaseResource> doInTransaction(TransactionStatus theStatus) {
return doHistoryInTransaction(theFromIndex, theToIndex);
}
});
case SEARCH:
case EVERYTHING:
default:
return doSearchOrEverything(theFromIndex, theToIndex);
}
}
public String getUuid() {

View File

@ -23,8 +23,6 @@ import java.util.*;
import java.util.concurrent.*;
import javax.persistence.EntityManager;
import javax.transaction.Transactional;
import javax.transaction.Transactional.TxType;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.Validate;
@ -35,6 +33,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
import org.springframework.transaction.*;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.*;
import com.google.common.annotations.VisibleForTesting;
@ -78,13 +78,13 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
private int mySyncSize = DEFAULT_SYNC_SIZE;
// @Autowired
// private DataSource myDataSource;
// @PostConstruct
// public void start() {
// JpaTransactionManager txManager = (JpaTransactionManager) myManagedTxManager;
// }
// @Autowired
// private DataSource myDataSource;
// @PostConstruct
// public void start() {
// JpaTransactionManager txManager = (JpaTransactionManager) myManagedTxManager;
// }
/**
* Constructor
*/
@ -106,7 +106,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
}
@Override
@Transactional(value = TxType.NOT_SUPPORTED)
@Transactional(propagation = Propagation.NEVER)
public List<Long> getResources(final String theUuid, int theFrom, int theTo) {
if (myNeverUseLocalSearchForUnitTests == false) {
SearchTask task = myIdToSearchTask.get(theUuid);
@ -116,7 +116,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
}
TransactionTemplate txTemplate = new TransactionTemplate(myManagedTxManager);
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
Search search;
StopWatch sw = new StopWatch();
@ -186,9 +186,9 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
}
@Override
public IBundleProvider registerSearch(final IDao theCallingDao, SearchParameterMap theParams, String theResourceType) {
public IBundleProvider registerSearch(final IDao theCallingDao, final SearchParameterMap theParams, String theResourceType) {
StopWatch w = new StopWatch();
String searchUuid = UUID.randomUUID().toString();
final String searchUuid = UUID.randomUUID().toString();
Class<? extends IBaseResource> resourceTypeClass = myContext.getResourceDefinition(theResourceType).getImplementingClass();
final ISearchBuilder sb = theCallingDao.newSearchBuilder();
@ -196,36 +196,37 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
if (theParams.isLoadSynchronous()) {
// Load the results synchronously
final List<Long> pids = new ArrayList<Long>();
Iterator<Long> resultIter = sb.createQuery(theParams, searchUuid);
while (resultIter.hasNext()) {
pids.add(resultIter.next());
if (theParams.getLoadSynchronousUpTo() != null && pids.size() >= theParams.getLoadSynchronousUpTo()) {
break;
}
}
/*
* For synchronous queries, we load all the includes right away
* since we're returning a static bundle with all the results
* pre-loaded. This is ok because syncronous requests are not
* expected to be paged
*
* On the other hand for async queries we load includes/revincludes
* individually for pages as we return them to clients
*/
final Set<Long> includedPids = new HashSet<Long>();
includedPids.addAll(sb.loadReverseIncludes(theCallingDao, myContext, myEntityManager, pids, theParams.getRevIncludes(), true, theParams.getLastUpdated()));
includedPids.addAll(sb.loadReverseIncludes(theCallingDao, myContext, myEntityManager, pids, theParams.getIncludes(), false, theParams.getLastUpdated()));
// Execute the query and make sure we return distinct results
TransactionTemplate txTemplate = new TransactionTemplate(myManagedTxManager);
txTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRED);
return txTemplate.execute(new TransactionCallback<SimpleBundleProvider>() {
@Override
public SimpleBundleProvider doInTransaction(TransactionStatus theStatus) {
// Load the results synchronously
final List<Long> pids = new ArrayList<Long>();
Iterator<Long> resultIter = sb.createQuery(theParams, searchUuid);
while (resultIter.hasNext()) {
pids.add(resultIter.next());
if (theParams.getLoadSynchronousUpTo() != null && pids.size() >= theParams.getLoadSynchronousUpTo()) {
break;
}
}
/*
* For synchronous queries, we load all the includes right away
* since we're returning a static bundle with all the results
* pre-loaded. This is ok because syncronous requests are not
* expected to be paged
*
* On the other hand for async queries we load includes/revincludes
* individually for pages as we return them to clients
*/
final Set<Long> includedPids = new HashSet<Long>();
includedPids.addAll(sb.loadReverseIncludes(theCallingDao, myContext, myEntityManager, pids, theParams.getRevIncludes(), true, theParams.getLastUpdated()));
includedPids.addAll(sb.loadReverseIncludes(theCallingDao, myContext, myEntityManager, pids, theParams.getIncludes(), false, theParams.getLastUpdated()));
List<IBaseResource> resources = new ArrayList<IBaseResource>();
sb.loadResourcesByPid(pids, resources, includedPids, false, myEntityManager, myContext, theCallingDao);
return new SimpleBundleProvider(resources);
@ -441,7 +442,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
try {
saveSearch();
TransactionTemplate txTemplate = new TransactionTemplate(myManagedTxManager);
txTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW);
txTemplate.execute(new TransactionCallbackWithoutResult() {
@ -454,7 +455,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
ourLog.info("Completed search for {} resources in {}ms", mySyncedPids.size(), sw.getMillis());
} catch (Throwable t) {
/*
* Don't print a stack trace for client errors.. that's just noisy
*/
@ -465,8 +466,8 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
logged = true;
ourLog.warn("Failed during search due to invalid request: {}", t.toString());
}
}
}
if (!logged) {
ourLog.error("Failed during search loading after {}ms", sw.getMillis(), t);
}

View File

@ -1,6 +1,6 @@
package ca.uhn.fhir.jpa.search;
/*
/*-
* #%L
* HAPI FHIR JPA Server
* %%
@ -10,7 +10,7 @@ package ca.uhn.fhir.jpa.search;
* 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
* 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,
@ -20,25 +20,24 @@ package ca.uhn.fhir.jpa.search;
* #L%
*/
import java.util.Collection;
import java.util.Date;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Slice;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import com.google.common.annotations.VisibleForTesting;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
import ca.uhn.fhir.jpa.dao.data.ISearchIncludeDao;
import ca.uhn.fhir.jpa.dao.data.ISearchResultDao;
import ca.uhn.fhir.jpa.dao.data.*;
import ca.uhn.fhir.jpa.entity.Search;
/**
@ -47,7 +46,6 @@ import ca.uhn.fhir.jpa.entity.Search;
public class StaleSearchDeletingSvcImpl implements IStaleSearchDeletingSvc {
public static final long DEFAULT_CUTOFF_SLACK = 10 * DateUtils.MILLIS_PER_SECOND;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(StaleSearchDeletingSvcImpl.class);
/*
* We give a bit of extra leeway just to avoid race conditions where a query result
@ -71,41 +69,48 @@ public class StaleSearchDeletingSvcImpl implements IStaleSearchDeletingSvc {
@Autowired
private PlatformTransactionManager myTransactionManager;
protected void deleteSearch(final Long theSearchPid) {
TransactionTemplate tt = new TransactionTemplate(myTransactionManager);
tt.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theArg0) {
Search searchToDelete = mySearchDao.findOne(theSearchPid);
ourLog.info("Deleting search {}/{} - Created[{}] -- Last returned[{}]", searchToDelete.getId(), searchToDelete.getUuid(), searchToDelete.getCreated(), searchToDelete.getSearchLastReturned());
mySearchIncludeDao.deleteForSearch(searchToDelete.getId());
mySearchResultDao.deleteForSearch(searchToDelete.getId());
mySearchDao.delete(searchToDelete);
}
});
private void deleteSearch(final Long theSearchPid) {
Search searchToDelete = mySearchDao.findOne(theSearchPid);
ourLog.info("Deleting search {}/{} - Created[{}] -- Last returned[{}]", searchToDelete.getId(), searchToDelete.getUuid(), searchToDelete.getCreated(), searchToDelete.getSearchLastReturned());
mySearchIncludeDao.deleteForSearch(searchToDelete.getId());
mySearchResultDao.deleteForSearch(searchToDelete.getId());
mySearchDao.delete(searchToDelete);
}
@Override
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void pollForStaleSearchesAndDeleteThem() {
long cutoffMillis = myDaoConfig.getExpireSearchResultsAfterMillis();
if (myDaoConfig.getReuseCachedSearchResultsForMillis() != null) {
cutoffMillis = Math.max(cutoffMillis, myDaoConfig.getReuseCachedSearchResultsForMillis());
}
Date cutoff = new Date((System.currentTimeMillis() - cutoffMillis) - myCutoffSlack);
final Date cutoff = new Date((System.currentTimeMillis() - cutoffMillis) - myCutoffSlack);
ourLog.debug("Searching for searches which are before {}", cutoff);
Collection<Long> toDelete = mySearchDao.findWhereLastReturnedBefore(cutoff);
if (!toDelete.isEmpty()) {
for (final Long next : toDelete) {
deleteSearch(next);
TransactionTemplate tt = new TransactionTemplate(myTransactionManager);
int count = tt.execute(new TransactionCallback<Integer>() {
@Override
public Integer doInTransaction(TransactionStatus theStatus) {
Slice<Long> toDelete = mySearchDao.findWhereLastReturnedBefore(cutoff, new PageRequest(0, 1000));
for (final Long next : toDelete) {
deleteSearch(next);
}
return toDelete.getContent().size();
}
});
ourLog.info("Deleted {} searches, {} remaining", toDelete.size(), mySearchDao.count());
if (count > 0) {
long total = tt.execute(new TransactionCallback<Long>() {
@Override
public Long doInTransaction(TransactionStatus theStatus) {
return mySearchDao.count();
}
});
ourLog.info("Deleted {} searches, {} remaining", count, total);
}
}
@Scheduled(fixedDelay = DEFAULT_CUTOFF_SLACK)

View File

@ -10,7 +10,7 @@ package ca.uhn.fhir.jpa.term;
* 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
* 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,
@ -19,21 +19,10 @@ package ca.uhn.fhir.jpa.term;
* limitations under the License.
* #L%
*/
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.concurrent.TimeUnit;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
import javax.persistence.*;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.time.DateUtils;
@ -51,19 +40,12 @@ import org.springframework.transaction.support.TransactionTemplate;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Stopwatch;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimaps;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao;
import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemVersionDao;
import ca.uhn.fhir.jpa.dao.data.ITermConceptDao;
import ca.uhn.fhir.jpa.dao.data.ITermConceptParentChildLinkDao;
import ca.uhn.fhir.jpa.entity.TermCodeSystem;
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink;
import ca.uhn.fhir.jpa.dao.data.*;
import ca.uhn.fhir.jpa.entity.*;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
import ca.uhn.fhir.jpa.util.StopWatch;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
@ -76,6 +58,8 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseHapiTerminologySvc.class);
private static final Object PLACEHOLDER_OBJECT = new Object();
private ArrayListMultimap<Long, Long> myChildToParentPidCache;
@Autowired
protected ITermCodeSystemDao myCodeSystemDao;
@ -100,8 +84,8 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
protected EntityManager myEntityManager;
private long myNextReindexPass;
private boolean myProcessDeferred = true;
@Autowired
@ -121,7 +105,7 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
private int ensureParentsSaved(Collection<TermConceptParentChildLink> theParents) {
ourLog.trace("Checking {} parents", theParents.size());
int retVal = 0;
for (TermConceptParentChildLink nextLink : theParents) {
if (nextLink.getRelationshipType() == RelationshipTypeEnum.ISA) {
TermConcept nextParent = nextLink.getParent();
@ -133,7 +117,7 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
}
}
}
return retVal;
}
@ -206,8 +190,11 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
/**
* Subclasses may override
* @param theSystem The code system
* @param theCode The code
*
* @param theSystem
* The code system
* @param theCode
* The code
*/
protected List<VersionIndependentConcept> findCodesAboveUsingBuiltInSystems(String theSystem, String theCode) {
return Collections.emptyList();
@ -244,15 +231,19 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
ArrayList<VersionIndependentConcept> retVal = toVersionIndependentConcepts(theSystem, codes);
return retVal;
}
/**
* Subclasses may override
* @param theSystem The code system
* @param theCode The code
*
* @param theSystem
* The code system
* @param theCode
* The code
*/
protected List<VersionIndependentConcept> findCodesBelowUsingBuiltInSystems(String theSystem, String theCode) {
return Collections.emptyList();
}
private TermCodeSystemVersion findCurrentCodeSystemVersionForSystem(String theCodeSystem) {
TermCodeSystem cs = getCodeSystem(theCodeSystem);
if (cs == null || cs.getCurrentVersion() == null) {
@ -274,7 +265,7 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
if (theConceptsStack.size() == 1 || theConceptsStack.size() % 10000 == 0) {
float pct = (float) theConceptsStack.size() / (float) theTotalConcepts;
ourLog.info("Have processed {}/{} concepts ({}%)", theConceptsStack.size(), theTotalConcepts, (int)( pct*100.0f));
ourLog.info("Have processed {}/{} concepts ({}%)", theConceptsStack.size(), theTotalConcepts, (int) (pct * 100.0f));
}
theConcept.setCodeSystem(theCodeSystem);
@ -285,7 +276,7 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
} else {
myConceptsToSaveLater.add(theConcept);
}
for (TermConceptParentChildLink next : theConcept.getChildren()) {
persistChildren(next.getChild(), theCodeSystem, theConceptsStack, theTotalConcepts);
}
@ -297,7 +288,7 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
myConceptLinksToSaveLater.add(next);
}
}
}
private void populateVersion(TermConcept theNext, TermCodeSystemVersion theCodeSystemVersion) {
@ -310,48 +301,56 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
}
}
private ArrayListMultimap<Long, Long> myChildToParentPidCache;
private void processDeferredConcepts() {
int codeCount = 0, relCount = 0;
StopWatch stopwatch = new StopWatch();
int count = Math.min(myDaoConfig.getDeferIndexingForCodesystemsOfSize(), myConceptsToSaveLater.size());
ourLog.info("Saving {} deferred concepts...", count);
while (codeCount < count && myConceptsToSaveLater.size() > 0) {
TermConcept next = myConceptsToSaveLater.remove(0);
codeCount += saveConcept(next);
}
if (codeCount > 0) {
ourLog.info("Saved {} deferred concepts ({} codes remain and {} relationships remain) in {}ms ({}ms / code)",
new Object[] { codeCount, myConceptsToSaveLater.size(), myConceptLinksToSaveLater.size(), stopwatch.getMillis(), stopwatch.getMillisPerOperation(codeCount) });
}
if (codeCount == 0) {
count = Math.min(myDaoConfig.getDeferIndexingForCodesystemsOfSize(), myConceptLinksToSaveLater.size());
ourLog.info("Saving {} deferred concept relationships...", count);
while (relCount < count && myConceptLinksToSaveLater.size() > 0) {
TermConceptParentChildLink next = myConceptLinksToSaveLater.remove(0);
if (myConceptDao.findOne(next.getChild().getId()) == null || myConceptDao.findOne(next.getParent().getId()) == null) {
ourLog.warn("Not inserting link from child {} to parent {} because it appears to have been deleted", next.getParent().getCode(), next.getChild().getCode());
continue;
}
saveConceptLink(next);
relCount++;
}
}
if (relCount > 0) {
ourLog.info("Saved {} deferred relationships ({} remain) in {}ms ({}ms / code)",
new Object[] { relCount, myConceptLinksToSaveLater.size(), stopwatch.getMillis(), stopwatch.getMillisPerOperation(codeCount) });
}
if ((myConceptsToSaveLater.size() + myConceptLinksToSaveLater.size()) == 0) {
ourLog.info("All deferred concepts and relationships have now been synchronized to the database");
}
}
private void processReindexing() {
if (System.currentTimeMillis() < myNextReindexPass && !ourForceSaveDeferredAlwaysForUnitTest) {
return;
}
TransactionTemplate tt = new TransactionTemplate(myTransactionMgr);
tt.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW);
tt.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theArg0) {
int maxResult = 1000;
Page<TermConcept> concepts = myConceptDao.findResourcesRequiringReindexing(new PageRequest(0, maxResult));
if (concepts.hasContent() == false) {
myNextReindexPass = System.currentTimeMillis() + DateUtils.MILLIS_PER_MINUTE;
myChildToParentPidCache = null;
return;
}
if (myChildToParentPidCache == null) {
myChildToParentPidCache = ArrayListMultimap.create();
}
ourLog.info("Indexing {} / {} concepts", concepts.getContent().size(), concepts.getTotalElements());
int count = 0;
StopWatch stopwatch = new StopWatch();
for (TermConcept nextConcept : concepts) {
StringBuilder parentsBuilder = new StringBuilder();
createParentsString(parentsBuilder, nextConcept.getId());
nextConcept.setParentPids(parentsBuilder.toString());
saveConcept(nextConcept);
count++;
}
ourLog.info("Indexed {} / {} concepts in {}ms - Avg {}ms / resource", new Object[] { count, concepts.getContent().size(), stopwatch.getMillis(), stopwatch.getMillisPerOperation(count) });
}
private void createParentsString(StringBuilder theParentsBuilder, Long theConceptPid) {
Validate.notNull(theConceptPid, "theConceptPid must not be null");
List<Long> parents = myChildToParentPidCache.get(theConceptPid);
@ -368,8 +367,7 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
}
}
}
for (Long nextParent : parents) {
if (theParentsBuilder.length() > 0) {
theParentsBuilder.append(' ');
@ -378,13 +376,45 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
createParentsString(theParentsBuilder, nextParent);
}
}
@Override
protected void doInTransactionWithoutResult(TransactionStatus theArg0) {
int maxResult = 1000;
Page<TermConcept> concepts = myConceptDao.findResourcesRequiringReindexing(new PageRequest(0, maxResult));
if (concepts.hasContent() == false) {
myNextReindexPass = System.currentTimeMillis() + DateUtils.MILLIS_PER_MINUTE;
myChildToParentPidCache = null;
return;
}
if (myChildToParentPidCache == null) {
myChildToParentPidCache = ArrayListMultimap.create();
}
ourLog.info("Indexing {} / {} concepts", concepts.getContent().size(), concepts.getTotalElements());
int count = 0;
StopWatch stopwatch = new StopWatch();
for (TermConcept nextConcept : concepts) {
StringBuilder parentsBuilder = new StringBuilder();
createParentsString(parentsBuilder, nextConcept.getId());
nextConcept.setParentPids(parentsBuilder.toString());
saveConcept(nextConcept);
count++;
}
ourLog.info("Indexed {} / {} concepts in {}ms - Avg {}ms / resource", new Object[] { count, concepts.getContent().size(), stopwatch.getMillis(), stopwatch.getMillisPerOperation(count) });
}
});
}
private int saveConcept(TermConcept theConcept) {
int retVal = 0;
/*
* If the concept has an ID, we're reindexing, so there's no need to
* save parent concepts first (it's way too slow to do that)
@ -392,25 +422,25 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
if (theConcept.getId() == null) {
retVal += ensureParentsSaved(theConcept.getParents());
}
if (theConcept.getId() == null || theConcept.getIndexStatus() == null) {
retVal++;
theConcept.setIndexStatus(BaseHapiFhirDao.INDEX_STATUS_INDEXED);
myConceptDao.save(theConcept);
}
ourLog.trace("Saved {} and got PID {}", theConcept.getCode(), theConcept.getId());
return retVal;
}
private void saveConceptLink(TermConceptParentChildLink next) {
if (next.getId() == null) {
myConceptParentChildLinkDao.save(next);
}
}
@Scheduled(fixedRate=5000)
@Transactional(propagation=Propagation.REQUIRED)
@Scheduled(fixedRate = 5000)
@Transactional(propagation = Propagation.NEVER)
@Override
public synchronized void saveDeferred() {
if (!myProcessDeferred) {
@ -419,46 +449,18 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
processReindexing();
return;
}
int codeCount = 0, relCount = 0;
StopWatch stopwatch = new StopWatch();
int count = Math.min(myDaoConfig.getDeferIndexingForCodesystemsOfSize(), myConceptsToSaveLater.size());
ourLog.info("Saving {} deferred concepts...", count);
while (codeCount < count && myConceptsToSaveLater.size() > 0) {
TermConcept next = myConceptsToSaveLater.remove(0);
codeCount += saveConcept(next);
}
if (codeCount > 0) {
ourLog.info("Saved {} deferred concepts ({} codes remain and {} relationships remain) in {}ms ({}ms / code)", new Object[] {codeCount, myConceptsToSaveLater.size(), myConceptLinksToSaveLater.size(), stopwatch.getMillis(), stopwatch.getMillisPerOperation(codeCount)});
}
if (codeCount == 0) {
count = Math.min(myDaoConfig.getDeferIndexingForCodesystemsOfSize(), myConceptLinksToSaveLater.size());
ourLog.info("Saving {} deferred concept relationships...", count);
while (relCount < count && myConceptLinksToSaveLater.size() > 0) {
TermConceptParentChildLink next = myConceptLinksToSaveLater.remove(0);
if (myConceptDao.findOne(next.getChild().getId()) == null || myConceptDao.findOne(next.getParent().getId()) == null) {
ourLog.warn("Not inserting link from child {} to parent {} because it appears to have been deleted", next.getParent().getCode(), next.getChild().getCode());
continue;
}
saveConceptLink(next);
relCount++;
TransactionTemplate tt = new TransactionTemplate(myTransactionMgr);
tt.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW);
tt.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theArg0) {
processDeferredConcepts();
}
}
if (relCount > 0) {
ourLog.info("Saved {} deferred relationships ({} remain) in {}ms ({}ms / code)", new Object[] {relCount, myConceptLinksToSaveLater.size(), stopwatch.getMillis(), stopwatch.getMillisPerOperation(codeCount)});
}
if ((myConceptsToSaveLater.size() + myConceptLinksToSaveLater.size()) == 0) {
ourLog.info("All deferred concepts and relationships have now been synchronized to the database");
}
});
}
@Override
public void setProcessDeferred(boolean theProcessDeferred) {
myProcessDeferred = theProcessDeferred;
@ -487,7 +489,7 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
}
ourLog.info("Flushing...");
myConceptParentChildLinkDao.flush();
myConceptDao.flush();
@ -540,7 +542,7 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
}
ourLog.info("Saving {} concepts...", totalCodeCount);
IdentityHashMap<TermConcept, Object> conceptsStack2 = new IdentityHashMap<TermConcept, Object>();
for (TermConcept next : theCodeSystemVersion.getConcepts()) {
persistChildren(next, codeSystemVersion, conceptsStack2, totalCodeCount);
@ -552,7 +554,7 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
myConceptParentChildLinkDao.flush();
ourLog.info("Done deleting old code system versions");
if (myConceptsToSaveLater.size() > 0 || myConceptLinksToSaveLater.size() > 0) {
ourLog.info("Note that some concept saving was deferred - still have {} concepts and {} relationships", myConceptsToSaveLater.size(), myConceptLinksToSaveLater.size());
}
@ -563,7 +565,7 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
TermCodeSystem cs = getCodeSystem(theSystem);
return cs != null;
}
private ArrayList<VersionIndependentConcept> toVersionIndependentConcepts(String theSystem, Set<TermConcept> codes) {
ArrayList<VersionIndependentConcept> retVal = new ArrayList<VersionIndependentConcept>(codes.size());
for (TermConcept next : codes) {
@ -582,7 +584,7 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
throw new InvalidRequestException("CodeSystem contains circular reference around code " + theConcept.getCode());
}
theConceptsStack.add(theConcept.getCode());
int retVal = 0;
if (theAllConcepts.put(theConcept, theAllConcepts) == null) {
if (theAllConcepts.size() % 1000 == 0) {

View File

@ -29,8 +29,8 @@ import org.hibernate.search.jpa.FullTextEntityManager;
import org.hibernate.search.jpa.FullTextQuery;
import org.hibernate.search.query.dsl.BooleanJunction;
import org.hibernate.search.query.dsl.QueryBuilder;
import org.hl7.fhir.r4.hapi.validation.HapiWorkerContext;
import org.hl7.fhir.r4.hapi.validation.IValidationSupport;
import org.hl7.fhir.r4.hapi.ctx.HapiWorkerContext;
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.CodeSystem.CodeSystemContentMode;
import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionComponent;

View File

@ -1,26 +1,6 @@
package ca.uhn.fhir.jpa.term;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2017 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import org.hl7.fhir.r4.hapi.validation.IValidationSupport;
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
public interface IHapiTerminologySvcR4 extends IHapiTerminologySvc, IValidationSupport {
// nothing

View File

@ -0,0 +1,12 @@
package ca.uhn.fhir.jpa.util;
import java.util.Iterator;
public abstract class BaseIterator<T> implements Iterator<T> {
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}

View File

@ -23,8 +23,8 @@ package ca.uhn.fhir.jpa.validation;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.hl7.fhir.r4.hapi.validation.DefaultProfileValidationSupport;
import org.hl7.fhir.r4.hapi.validation.ValidationSupportChain;
import org.hl7.fhir.r4.hapi.ctx.DefaultProfileValidationSupport;
import org.hl7.fhir.r4.hapi.ctx.ValidationSupportChain;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

View File

@ -0,0 +1,289 @@
package ca.uhn.fhir.jpa.config;
import java.sql.*;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;
public class ConnectionWrapper implements Connection {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ConnectionWrapper.class);
private Connection myWrap;
public ConnectionWrapper(Connection theConnection) {
myWrap = theConnection;
}
@Override
public void abort(Executor theExecutor) throws SQLException {
myWrap.abort(theExecutor);
}
@Override
public void clearWarnings() throws SQLException {
myWrap.clearWarnings();
}
@Override
public void close() throws SQLException {
// ourLog.info("** Closing connection");
myWrap.close();
}
@Override
public void commit() throws SQLException {
myWrap.commit();
}
@Override
public Array createArrayOf(String theTypeName, Object[] theElements) throws SQLException {
return myWrap.createArrayOf(theTypeName, theElements);
}
@Override
public Blob createBlob() throws SQLException {
return myWrap.createBlob();
}
@Override
public Clob createClob() throws SQLException {
return myWrap.createClob();
}
@Override
public NClob createNClob() throws SQLException {
return myWrap.createNClob();
}
@Override
public SQLXML createSQLXML() throws SQLException {
return myWrap.createSQLXML();
}
@Override
public Statement createStatement() throws SQLException {
return myWrap.createStatement();
}
@Override
public Statement createStatement(int theResultSetType, int theResultSetConcurrency) throws SQLException {
return myWrap.createStatement(theResultSetType, theResultSetConcurrency);
}
@Override
public Statement createStatement(int theResultSetType, int theResultSetConcurrency, int theResultSetHoldability) throws SQLException {
return myWrap.createStatement(theResultSetType, theResultSetConcurrency, theResultSetHoldability);
}
@Override
public Struct createStruct(String theTypeName, Object[] theAttributes) throws SQLException {
return myWrap.createStruct(theTypeName, theAttributes);
}
@Override
public boolean getAutoCommit() throws SQLException {
return myWrap.getAutoCommit();
}
@Override
public String getCatalog() throws SQLException {
return myWrap.getCatalog();
}
@Override
public Properties getClientInfo() throws SQLException {
return myWrap.getClientInfo();
}
@Override
public String getClientInfo(String theName) throws SQLException {
return getClientInfo(theName);
}
@Override
public int getHoldability() throws SQLException {
return myWrap.getHoldability();
}
@Override
public DatabaseMetaData getMetaData() throws SQLException {
return myWrap.getMetaData();
}
@Override
public int getNetworkTimeout() throws SQLException {
return myWrap.getNetworkTimeout();
}
@Override
public String getSchema() throws SQLException {
return myWrap.getSchema();
}
@Override
public int getTransactionIsolation() throws SQLException {
return myWrap.getTransactionIsolation();
}
@Override
public Map<String, Class<?>> getTypeMap() throws SQLException {
return myWrap.getTypeMap();
}
@Override
public SQLWarning getWarnings() throws SQLException {
return myWrap.getWarnings();
}
@Override
public boolean isClosed() throws SQLException {
return myWrap.isClosed();
}
@Override
public boolean isReadOnly() throws SQLException {
return myWrap.isReadOnly();
}
@Override
public boolean isValid(int theTimeout) throws SQLException {
return myWrap.isValid(theTimeout);
}
@Override
public boolean isWrapperFor(Class<?> theIface) throws SQLException {
return myWrap.isWrapperFor(theIface);
}
@Override
public String nativeSQL(String theSql) throws SQLException {
return myWrap.nativeSQL(theSql);
}
@Override
public CallableStatement prepareCall(String theSql) throws SQLException {
return myWrap.prepareCall(theSql);
}
@Override
public CallableStatement prepareCall(String theSql, int theResultSetType, int theResultSetConcurrency) throws SQLException {
return myWrap.prepareCall(theSql, theResultSetType, theResultSetConcurrency);
}
@Override
public CallableStatement prepareCall(String theSql, int theResultSetType, int theResultSetConcurrency, int theResultSetHoldability) throws SQLException {
return myWrap.prepareCall(theSql, theResultSetType, theResultSetConcurrency, theResultSetHoldability);
}
@Override
public PreparedStatement prepareStatement(String theSql) throws SQLException {
return myWrap.prepareStatement(theSql);
}
@Override
public PreparedStatement prepareStatement(String theSql, int theAutoGeneratedKeys) throws SQLException {
return myWrap.prepareStatement(theSql, theAutoGeneratedKeys);
}
@Override
public PreparedStatement prepareStatement(String theSql, int theResultSetType, int theResultSetConcurrency) throws SQLException {
return myWrap.prepareStatement(theSql, theResultSetType, theResultSetConcurrency);
}
@Override
public PreparedStatement prepareStatement(String theSql, int theResultSetType, int theResultSetConcurrency, int theResultSetHoldability) throws SQLException {
return myWrap.prepareStatement(theSql, theResultSetType, theResultSetConcurrency, theResultSetHoldability);
}
@Override
public PreparedStatement prepareStatement(String theSql, int[] theColumnIndexes) throws SQLException {
return myWrap.prepareStatement(theSql, theColumnIndexes);
}
@Override
public PreparedStatement prepareStatement(String theSql, String[] theColumnNames) throws SQLException {
return myWrap.prepareStatement(theSql, theColumnNames);
}
@Override
public void releaseSavepoint(Savepoint theSavepoint) throws SQLException {
myWrap.releaseSavepoint(theSavepoint);
}
@Override
public void rollback() throws SQLException {
myWrap.rollback();
}
@Override
public void rollback(Savepoint theSavepoint) throws SQLException {
myWrap.rollback(theSavepoint);
}
@Override
public void setAutoCommit(boolean theAutoCommit) throws SQLException {
myWrap.setAutoCommit(theAutoCommit);
}
@Override
public void setCatalog(String theCatalog) throws SQLException {
myWrap.setCatalog(theCatalog);
}
@Override
public void setClientInfo(Properties theProperties) throws SQLClientInfoException {
myWrap.setClientInfo(theProperties);
}
@Override
public void setClientInfo(String theName, String theValue) throws SQLClientInfoException {
myWrap.setClientInfo(theName, theValue);
}
@Override
public void setHoldability(int theHoldability) throws SQLException {
myWrap.setHoldability(theHoldability);
}
@Override
public void setNetworkTimeout(Executor theExecutor, int theMilliseconds) throws SQLException {
myWrap.setNetworkTimeout(theExecutor, theMilliseconds);
}
@Override
public void setReadOnly(boolean theReadOnly) throws SQLException {
myWrap.setReadOnly(theReadOnly);
}
@Override
public Savepoint setSavepoint() throws SQLException {
return myWrap.setSavepoint();
}
@Override
public Savepoint setSavepoint(String theName) throws SQLException {
return myWrap.setSavepoint(theName);
}
@Override
public void setSchema(String theSchema) throws SQLException {
myWrap.setSchema(theSchema);
}
@Override
public void setTransactionIsolation(int theLevel) throws SQLException {
myWrap.setTransactionIsolation(theLevel);
}
@Override
public void setTypeMap(Map<String, Class<?>> theMap) throws SQLException {
myWrap.setTypeMap(theMap);
}
@Override
public <T> T unwrap(Class<T> theIface) throws SQLException {
return myWrap.unwrap(theIface);
}
}

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.jpa.config;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
@ -8,9 +10,7 @@ import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSource;
import org.hibernate.jpa.HibernatePersistenceProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.*;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@ -18,30 +18,82 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
import ca.uhn.fhir.validation.ResultSeverityEnum;
import net.ttddyy.dsproxy.listener.logging.SLF4JLogLevel;
import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder;
@Configuration
@EnableTransactionManagement()
public class TestDstu3Config extends BaseJavaConfigDstu3 {
static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TestDstu3Config.class);
@Bean()
public DaoConfig daoConfig() {
return new DaoConfig();
}
private Exception myLastStackTrace;
@Bean()
public DataSource dataSource() {
BasicDataSource retVal = new BasicDataSource();
BasicDataSource retVal = new BasicDataSource() {
@Override
public Connection getConnection() throws SQLException {
ConnectionWrapper retVal;
try {
retVal = new ConnectionWrapper(super.getConnection());
} catch (Exception e) {
ourLog.error("Exceeded maximum wait for connection", e);
logGetConnectionStackTrace();
System.exit(1);
retVal = null;
}
try {
throw new Exception();
} catch (Exception e) {
myLastStackTrace = e;
}
return retVal;
}
private void logGetConnectionStackTrace() {
StringBuilder b = new StringBuilder();
b.append("Last connection request stack trace:");
for (StackTraceElement next : myLastStackTrace.getStackTrace()) {
b.append("\n ");
b.append(next.getClassName());
b.append(".");
b.append(next.getMethodName());
b.append("(");
b.append(next.getFileName());
b.append(":");
b.append(next.getLineNumber());
b.append(")");
}
ourLog.info(b.toString());
}
};
retVal.setDriver(new org.apache.derby.jdbc.EmbeddedDriver());
retVal.setUrl("jdbc:derby:memory:myUnitTestDB;create=true");
retVal.setMaxWaitMillis(10000);
retVal.setUsername("");
retVal.setPassword("");
retVal.setMaxTotal(4);
/*
* We use a randomized number of maximum threads in order to try
* and catch any potential deadlocks caused by database connection
* starvation
*/
int maxThreads = (int) (Math.random() * 6) + 1;
retVal.setMaxTotal(maxThreads);
DataSource dataSource = ProxyDataSourceBuilder
.create(retVal)
.logQueryBySlf4j(SLF4JLogLevel.INFO, "SQL")
// .logQueryBySlf4j(SLF4JLogLevel.INFO, "SQL")
.logSlowQueryBySlf4j(10, TimeUnit.SECONDS)
.countQuery()
.build();
@ -49,13 +101,6 @@ public class TestDstu3Config extends BaseJavaConfigDstu3 {
return dataSource;
}
@Bean()
public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager retVal = new JpaTransactionManager();
retVal.setEntityManagerFactory(entityManagerFactory);
return retVal;
}
@Bean()
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean retVal = new LocalContainerEntityManagerFactoryBean();
@ -95,4 +140,11 @@ public class TestDstu3Config extends BaseJavaConfigDstu3 {
return requestValidator;
}
@Bean()
public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager retVal = new JpaTransactionManager();
retVal.setEntityManagerFactory(entityManagerFactory);
return retVal;
}
}

View File

@ -16,7 +16,6 @@ import java.util.*;
import org.apache.commons.io.IOUtils;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.dstu3.model.Bundle.*;
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryRequestComponent;
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryResponseComponent;
@ -170,6 +169,74 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
return null;
}
@Test
public void testBatchCreateWithBadRead() {
Bundle request = new Bundle();
request.setType(BundleType.BATCH);
Patient p;
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue("FOO");
request
.addEntry()
.setResource(p)
.getRequest()
.setMethod(HTTPVerb.POST)
.setUrl("Patient");
request
.addEntry()
.getRequest()
.setMethod(HTTPVerb.GET)
.setUrl("Patient/BABABABA");
Bundle response = mySystemDao.transaction(mySrd, request);
assertEquals(2, response.getEntry().size());
assertEquals("201 Created", response.getEntry().get(0).getResponse().getStatus());
assertThat(response.getEntry().get(0).getResponse().getLocation(), matchesPattern(".*Patient/[0-9]+.*"));
assertEquals("404 Not Found", response.getEntry().get(1).getResponse().getStatus());
OperationOutcome oo = (OperationOutcome) response.getEntry().get(1).getResponse().getOutcome();
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(oo));
assertEquals(IssueSeverity.ERROR, oo.getIssue().get(0).getSeverity());
assertEquals("Resource Patient/BABABABA is not known", oo.getIssue().get(0).getDiagnostics());
}
@Test
public void testBatchCreateWithBadSearch() {
Bundle request = new Bundle();
request.setType(BundleType.BATCH);
Patient p;
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue("FOO");
request
.addEntry()
.setResource(p)
.getRequest()
.setMethod(HTTPVerb.POST)
.setUrl("Patient");
request
.addEntry()
.getRequest()
.setMethod(HTTPVerb.GET)
.setUrl("Patient?foobadparam=1");
Bundle response = mySystemDao.transaction(mySrd, request);
assertEquals(2, response.getEntry().size());
assertEquals("201 Created", response.getEntry().get(0).getResponse().getStatus());
assertThat(response.getEntry().get(0).getResponse().getLocation(), matchesPattern(".*Patient/[0-9]+.*"));
assertEquals("400 Bad Request", response.getEntry().get(1).getResponse().getStatus());
OperationOutcome oo = (OperationOutcome) response.getEntry().get(1).getResponse().getOutcome();
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(oo));
assertEquals(IssueSeverity.ERROR, oo.getIssue().get(0).getSeverity());
assertThat(oo.getIssue().get(0).getDiagnostics(), containsString("Unknown search parameter"));
}
@Test
public void testCircularCreateAndDelete() {
Encounter enc = new Encounter();
@ -552,9 +619,10 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
assertThat(resp.getEntry().get(0).getResponse().getLocation(), startsWith("Patient/"));
// Bundle.entry[1] is failed read response
assertEquals(OperationOutcome.class, resp.getEntry().get(1).getResource().getClass());
assertEquals(IssueSeverity.ERROR, ((OperationOutcome) resp.getEntry().get(1).getResource()).getIssue().get(0).getSeverityElement().getValue());
assertEquals("Resource Patient/THIS_ID_DOESNT_EXIST is not known", ((OperationOutcome) resp.getEntry().get(1).getResource()).getIssue().get(0).getDiagnostics());
Resource oo = resp.getEntry().get(1).getResponse().getOutcome();
assertEquals(OperationOutcome.class, oo.getClass());
assertEquals(IssueSeverity.ERROR, ((OperationOutcome) oo).getIssue().get(0).getSeverityElement().getValue());
assertEquals("Resource Patient/THIS_ID_DOESNT_EXIST is not known", ((OperationOutcome) oo).getIssue().get(0).getDiagnostics());
assertEquals("404 Not Found", resp.getEntry().get(1).getResponse().getStatus());
// Check POST
@ -869,6 +937,74 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
}
@Test
public void testTransactionCreateWithBadRead() {
Bundle request = new Bundle();
request.setType(BundleType.TRANSACTION);
Patient p;
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue("FOO");
request
.addEntry()
.setResource(p)
.getRequest()
.setMethod(HTTPVerb.POST)
.setUrl("Patient");
request
.addEntry()
.getRequest()
.setMethod(HTTPVerb.GET)
.setUrl("Patient/BABABABA");
Bundle response = mySystemDao.transaction(mySrd, request);
assertEquals(2, response.getEntry().size());
assertEquals("201 Created", response.getEntry().get(0).getResponse().getStatus());
assertThat(response.getEntry().get(0).getResponse().getLocation(), matchesPattern(".*Patient/[0-9]+.*"));
assertEquals("404 Not Found", response.getEntry().get(1).getResponse().getStatus());
OperationOutcome oo = (OperationOutcome) response.getEntry().get(1).getResponse().getOutcome();
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(oo));
assertEquals(IssueSeverity.ERROR, oo.getIssue().get(0).getSeverity());
assertEquals("Resource Patient/BABABABA is not known", oo.getIssue().get(0).getDiagnostics());
}
@Test
public void testTransactionCreateWithBadSearch() {
Bundle request = new Bundle();
request.setType(BundleType.TRANSACTION);
Patient p;
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue("FOO");
request
.addEntry()
.setResource(p)
.getRequest()
.setMethod(HTTPVerb.POST)
.setUrl("Patient");
request
.addEntry()
.getRequest()
.setMethod(HTTPVerb.GET)
.setUrl("Patient?foobadparam=1");
Bundle response = mySystemDao.transaction(mySrd, request);
assertEquals(2, response.getEntry().size());
assertEquals("201 Created", response.getEntry().get(0).getResponse().getStatus());
assertThat(response.getEntry().get(0).getResponse().getLocation(), matchesPattern(".*Patient/[0-9]+.*"));
assertEquals("400 Bad Request", response.getEntry().get(1).getResponse().getStatus());
OperationOutcome oo = (OperationOutcome) response.getEntry().get(1).getResponse().getOutcome();
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(oo));
assertEquals(IssueSeverity.ERROR, oo.getIssue().get(0).getSeverity());
assertThat(oo.getIssue().get(0).getDiagnostics(), containsString("Unknown search parameter"));
}
@Test
public void testTransactionCreateWithDuplicateMatchUrl01() {
String methodName = "testTransactionCreateWithDuplicateMatchUrl01";
@ -914,7 +1050,7 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
"Unable to process Transaction - Request would cause multiple resources to match URL: \"Patient?identifier=urn%3Asystem%7CtestTransactionCreateWithDuplicateMatchUrl02\". Does transaction request contain duplicates?");
}
}
@Test
public void testTransactionCreateWithInvalidMatchUrl() {
String methodName = "testTransactionCreateWithInvalidMatchUrl";
@ -949,6 +1085,7 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
assertEquals("Failed to parse match URL[Patient?foo=bar] - Resource type Patient does not have a parameter with name: foo", e.getMessage());
}
}
@Test
public void testTransactionCreateWithInvalidReferenceNumeric() {
@ -1332,20 +1469,15 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
@Test
public void testTransactionDoesNotLeavePlaceholderIds() throws Exception {
newTxTemplate().execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
String input;
try {
input = IOUtils.toString(getClass().getResourceAsStream("/cdr-bundle.json"), StandardCharsets.UTF_8);
} catch (IOException e) {
fail(e.toString());
return;
}
Bundle bundle = myFhirCtx.newJsonParser().parseResource(Bundle.class, input);
mySystemDao.transaction(mySrd, bundle);
}
});
String input;
try {
input = IOUtils.toString(getClass().getResourceAsStream("/cdr-bundle.json"), StandardCharsets.UTF_8);
} catch (IOException e) {
fail(e.toString());
return;
}
Bundle bundle = myFhirCtx.newJsonParser().parseResource(Bundle.class, input);
mySystemDao.transaction(mySrd, bundle);
IBundleProvider history = mySystemDao.history(null, null, null);
Bundle list = toBundle(history);
@ -2161,16 +2293,16 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
* </pre>
*/
@Test
public void testTransactionWithPlaceholderIdInMatchUrlPut() {
public void testTransactionWithPlaceholderIdInMatchUrlPost() {
Bundle input = createInputTransactionWithPlaceholderIdInMatchUrl(HTTPVerb.PUT);
Bundle input = createInputTransactionWithPlaceholderIdInMatchUrl(HTTPVerb.POST);
Bundle output = mySystemDao.transaction(null, input);
assertEquals("201 Created", output.getEntry().get(0).getResponse().getStatus());
assertEquals("201 Created", output.getEntry().get(1).getResponse().getStatus());
assertEquals("201 Created", output.getEntry().get(2).getResponse().getStatus());
Bundle input2 = createInputTransactionWithPlaceholderIdInMatchUrl(HTTPVerb.PUT);
Bundle input2 = createInputTransactionWithPlaceholderIdInMatchUrl(HTTPVerb.POST);
Bundle output2 = mySystemDao.transaction(null, input2);
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(output2));
@ -2191,16 +2323,16 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
* </pre>
*/
@Test
public void testTransactionWithPlaceholderIdInMatchUrlPost() {
public void testTransactionWithPlaceholderIdInMatchUrlPut() {
Bundle input = createInputTransactionWithPlaceholderIdInMatchUrl(HTTPVerb.POST);
Bundle input = createInputTransactionWithPlaceholderIdInMatchUrl(HTTPVerb.PUT);
Bundle output = mySystemDao.transaction(null, input);
assertEquals("201 Created", output.getEntry().get(0).getResponse().getStatus());
assertEquals("201 Created", output.getEntry().get(1).getResponse().getStatus());
assertEquals("201 Created", output.getEntry().get(2).getResponse().getStatus());
Bundle input2 = createInputTransactionWithPlaceholderIdInMatchUrl(HTTPVerb.POST);
Bundle input2 = createInputTransactionWithPlaceholderIdInMatchUrl(HTTPVerb.PUT);
Bundle output2 = mySystemDao.transaction(null, input2);
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(output2));
@ -2247,6 +2379,7 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
assertEquals("Joshua", patient.getNameFirstRep().getGivenAsSingleString());
}
@Test
public void testTransactionWithReferenceResource() {
Bundle request = new Bundle();
@ -2274,58 +2407,59 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
assertEquals(1, found.size().intValue());
}
@Test
public void testTransactionWithReferenceToCreateIfNoneExist() {
Bundle bundle = new Bundle();
bundle.setType(BundleType.TRANSACTION);
Medication med = new Medication();
IdType medId = IdType.newRandomUuid();
med.setId(medId);
med.getCode().addCoding().setSystem("billscodes").setCode("theCode");
bundle.addEntry().setResource(med).setFullUrl(medId.getValue()).getRequest().setMethod(HTTPVerb.POST).setIfNoneExist("Medication?code=billscodes|theCode");
MedicationRequest mo = new MedicationRequest();
mo.setMedication(new Reference(medId));
bundle.addEntry().setResource(mo).setFullUrl(mo.getIdElement().getValue()).getRequest().setMethod(HTTPVerb.POST);
ourLog.info("Request:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle));
Bundle outcome = mySystemDao.transaction(mySrd, bundle);
ourLog.info("Response:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome));
IdType medId1 = new IdType(outcome.getEntry().get(0).getResponse().getLocation());
IdType medOrderId1 = new IdType(outcome.getEntry().get(1).getResponse().getLocation());
/*
* Again!
*/
bundle = new Bundle();
bundle.setType(BundleType.TRANSACTION);
med = new Medication();
medId = IdType.newRandomUuid();
med.getCode().addCoding().setSystem("billscodes").setCode("theCode");
bundle.addEntry().setResource(med).setFullUrl(medId.getValue()).getRequest().setMethod(HTTPVerb.POST).setIfNoneExist("Medication?code=billscodes|theCode");
mo = new MedicationRequest();
mo.setMedication(new Reference(medId));
bundle.addEntry().setResource(mo).setFullUrl(mo.getIdElement().getValue()).getRequest().setMethod(HTTPVerb.POST);
outcome = mySystemDao.transaction(mySrd, bundle);
IdType medId2 = new IdType(outcome.getEntry().get(0).getResponse().getLocation());
IdType medOrderId2 = new IdType(outcome.getEntry().get(1).getResponse().getLocation());
assertTrue(medId1.isIdPartValidLong());
assertTrue(medId2.isIdPartValidLong());
assertTrue(medOrderId1.isIdPartValidLong());
assertTrue(medOrderId2.isIdPartValidLong());
assertEquals(medId1, medId2);
assertNotEquals(medOrderId1, medOrderId2);
}
public void testTransactionWithReferenceToCreateIfNoneExist() {
Bundle bundle = new Bundle();
bundle.setType(BundleType.TRANSACTION);
Medication med = new Medication();
IdType medId = IdType.newRandomUuid();
med.setId(medId);
med.getCode().addCoding().setSystem("billscodes").setCode("theCode");
bundle.addEntry().setResource(med).setFullUrl(medId.getValue()).getRequest().setMethod(HTTPVerb.POST).setIfNoneExist("Medication?code=billscodes|theCode");
MedicationRequest mo = new MedicationRequest();
mo.setMedication(new Reference(medId));
bundle.addEntry().setResource(mo).setFullUrl(mo.getIdElement().getValue()).getRequest().setMethod(HTTPVerb.POST);
ourLog.info("Request:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle));
Bundle outcome = mySystemDao.transaction(mySrd, bundle);
ourLog.info("Response:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome));
IdType medId1 = new IdType(outcome.getEntry().get(0).getResponse().getLocation());
IdType medOrderId1 = new IdType(outcome.getEntry().get(1).getResponse().getLocation());
/*
* Again!
*/
bundle = new Bundle();
bundle.setType(BundleType.TRANSACTION);
med = new Medication();
medId = IdType.newRandomUuid();
med.getCode().addCoding().setSystem("billscodes").setCode("theCode");
bundle.addEntry().setResource(med).setFullUrl(medId.getValue()).getRequest().setMethod(HTTPVerb.POST).setIfNoneExist("Medication?code=billscodes|theCode");
mo = new MedicationRequest();
mo.setMedication(new Reference(medId));
bundle.addEntry().setResource(mo).setFullUrl(mo.getIdElement().getValue()).getRequest().setMethod(HTTPVerb.POST);
outcome = mySystemDao.transaction(mySrd, bundle);
IdType medId2 = new IdType(outcome.getEntry().get(0).getResponse().getLocation());
IdType medOrderId2 = new IdType(outcome.getEntry().get(1).getResponse().getLocation());
assertTrue(medId1.isIdPartValidLong());
assertTrue(medId2.isIdPartValidLong());
assertTrue(medOrderId1.isIdPartValidLong());
assertTrue(medOrderId2.isIdPartValidLong());
assertEquals(medId1, medId2);
assertNotEquals(medOrderId1, medOrderId2);
}
//
//
@ -2429,7 +2563,8 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
//
// }
@Test
@Test
public void testTransactionWithReferenceUuid() {
Bundle request = new Bundle();

View File

@ -2,9 +2,16 @@ package ca.uhn.fhir.jpa.provider.dstu3;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.*;
import java.math.BigDecimal;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
@ -1973,7 +1980,9 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
.execute();
assertEquals(10, response.getEntry().size());
assertEquals(null, response.getTotalElement().getValueAsString());
if (response.getTotalElement().getValueAsString() != null) {
assertEquals("21", response.getTotalElement().getValueAsString());
}
assertThat(response.getLink("next").getUrl(), not(emptyString()));
// Load page 2
@ -1982,7 +1991,9 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
response = ourClient.fetchResourceFromUrl(Bundle.class, nextUrl);
assertEquals(10, response.getEntry().size());
assertEquals(null, response.getTotalElement().getValueAsString());
if (response.getTotalElement().getValueAsString() != null) {
assertEquals("21", response.getTotalElement().getValueAsString());
}
assertThat(response.getLink("next").getUrl(), not(emptyString()));
// Load page 3
@ -1992,7 +2003,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
response = ourClient.fetchResourceFromUrl(Bundle.class, nextUrl);
assertEquals(1, response.getEntry().size());
assertEquals(21, response.getTotal());
assertEquals("21", response.getTotalElement().getValueAsString());
assertEquals(null, response.getLink("next"));
}
@ -3187,6 +3198,31 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
}
}
@Test()
public void testSearchNegativeNumbers() throws Exception {
Observation o = new Observation();
o.setValue(new Quantity().setValue(new BigDecimal("-10")));
String oid1 = myObservationDao.create(o, mySrd).getId().toUnqualifiedVersionless().getValue();
Observation o2 = new Observation();
o2.setValue(new Quantity().setValue(new BigDecimal("-20")));
String oid2 = myObservationDao.create(o2, mySrd).getId().toUnqualifiedVersionless().getValue();
HttpGet get = new HttpGet(ourServerBase + "/Observation?value-quantity=gt-15");
CloseableHttpResponse resp = ourHttpClient.execute(get);
try {
assertEquals(200, resp.getStatusLine().getStatusCode());
Bundle bundle = myFhirCtx.newXmlParser().parseResource(Bundle.class, IOUtils.toString(resp.getEntity().getContent(), Constants.CHARSET_UTF8));
List<String> ids = toUnqualifiedVersionlessIdValues(bundle);
assertThat(ids, contains(oid1));
assertThat(ids, not(contains(oid2)));
} finally {
IOUtils.closeQuietly(resp);
}
}
@Test(expected = InvalidRequestException.class)
public void testSearchWithInvalidSort() throws Exception {
Observation o = new Observation();

View File

@ -24,6 +24,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.*;
import ca.uhn.fhir.jpa.dao.data.*;
import ca.uhn.fhir.jpa.entity.*;
import ca.uhn.fhir.jpa.util.BaseIterator;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.StringParam;
@ -139,9 +140,6 @@ public class SearchCoordinatorSvcImplTest {
}
private String newUuid() {
return UUID.randomUUID().toString();
}
@Test
public void testAsyncSearchLargeResultSetBigCountSameCoordinator() {
SearchParameterMap params = new SearchParameterMap();
@ -392,7 +390,7 @@ public class SearchCoordinatorSvcImplTest {
TestUtil.clearAllStaticFieldsForUnitTest();
}
public static class FailAfterNIterator<T> implements Iterator<T> {
public static class FailAfterNIterator<T> extends BaseIterator<T> implements Iterator<T> {
private int myCount;
private Iterator<T> myWrap;
@ -419,7 +417,7 @@ public class SearchCoordinatorSvcImplTest {
}
public static class SlowIterator<T> implements Iterator<T> {
public static class SlowIterator<T> extends BaseIterator<T> implements Iterator<T> {
private int myDelay;
private Iterator<T> myWrap;

View File

@ -6,6 +6,7 @@ import static org.junit.Assert.fail;
import java.util.List;
import java.util.UUID;
import ca.uhn.fhir.rest.api.Constants;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
@ -15,6 +16,7 @@ import org.hl7.fhir.dstu3.model.Bundle.BundleType;
import org.hl7.fhir.dstu3.model.Bundle.HTTPVerb;
import org.junit.*;
import com.google.common.base.Charsets;
import com.google.common.collect.Lists;
import ca.uhn.fhir.jpa.provider.dstu3.BaseResourceProviderDstu3Test;
@ -42,6 +44,37 @@ public class StressTestDstu3Test extends BaseResourceProviderDstu3Test {
ourRestServer.unregisterInterceptor(myRequestValidatingInterceptor);
}
@Test
public void testMultithreadedSearch() throws Exception {
Bundle input = new Bundle();
input.setType(BundleType.TRANSACTION);
for (int i = 0; i < 500; i++) {
Patient p = new Patient();
p.addIdentifier().setSystem("http://test").setValue("BAR");
input.addEntry().setResource(p).getRequest().setMethod(HTTPVerb.POST).setUrl("Patient");
}
ourClient.transaction().withBundle(input).execute();
List<BaseTask> tasks = Lists.newArrayList();
try {
for (int threadIndex = 0; threadIndex < 10; threadIndex++) {
SearchTask task = new SearchTask();
tasks.add(task);
task.start();
}
} finally {
for (BaseTask next : tasks) {
next.join();
}
}
validateNoErrors(tasks);
}
/**
* This test prevents a deadlock that was detected with a large number of
@ -73,12 +106,12 @@ public class StressTestDstu3Test extends BaseResourceProviderDstu3Test {
List<BaseTask> tasks = Lists.newArrayList();
try {
for (int threadIndex = 0; threadIndex < 8; threadIndex++) {
for (int threadIndex = 0; threadIndex < 5; threadIndex++) {
SearchTask task = new SearchTask();
tasks.add(task);
task.start();
}
for (int threadIndex = 0; threadIndex < 8; threadIndex++) {
for (int threadIndex = 0; threadIndex < 5; threadIndex++) {
CreateTask task = new CreateTask();
tasks.add(task);
task.start();
@ -89,6 +122,10 @@ public class StressTestDstu3Test extends BaseResourceProviderDstu3Test {
}
}
validateNoErrors(tasks);
}
private void validateNoErrors(List<BaseTask> tasks) {
int total = 0;
for (BaseTask next : tasks) {
if (next.getError() != null) {
@ -127,16 +164,37 @@ public class StressTestDstu3Test extends BaseResourceProviderDstu3Test {
@Override
public void run() {
CloseableHttpResponse get = null;
for (int i = 0; i < 20; i++) {
CloseableHttpResponse getResp = null;
for (int i = 0; i < 10; i++) {
try {
get = ourHttpClient.execute(new HttpGet(ourServerBase + "/Patient?identifier=http%3A%2F%2Ftest%7CBAR," + UUID.randomUUID().toString()));
Bundle respBundle;
// Load search
HttpGet get = new HttpGet(ourServerBase + "/Patient?identifier=http%3A%2F%2Ftest%7CBAR," + UUID.randomUUID().toString());
get.addHeader(Constants.HEADER_CONTENT_TYPE, Constants.CT_FHIR_JSON_NEW);
getResp = ourHttpClient.execute(get);
try {
assertEquals(200, get.getStatusLine().getStatusCode());
assertEquals(200, getResp.getStatusLine().getStatusCode());
String respBundleString = IOUtils.toString(getResp.getEntity().getContent(), Charsets.UTF_8);
respBundle = myFhirCtx.newJsonParser().parseResource(Bundle.class, respBundleString);
myTaskCount++;
} finally {
IOUtils.closeQuietly(get);
IOUtils.closeQuietly(getResp);
}
// Load page 2
get = new HttpGet(respBundle.getLink("next").getUrl());
get.addHeader(Constants.HEADER_CONTENT_TYPE, Constants.CT_FHIR_JSON_NEW);
getResp = ourHttpClient.execute(get);
try {
assertEquals(200, getResp.getStatusLine().getStatusCode());
String respBundleString = IOUtils.toString(getResp.getEntity().getContent(), Charsets.UTF_8);
respBundle = myFhirCtx.newJsonParser().parseResource(Bundle.class, respBundleString);
myTaskCount++;
} finally {
IOUtils.closeQuietly(getResp);
}
} catch (Throwable e) {
ourLog.error("Failure during search", e);
myError = e;

View File

@ -1,19 +1,18 @@
package ca.uhn.fhir.rest.param;
import static org.junit.Assert.*;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu.composite.CodingDt;
import ca.uhn.fhir.rest.method.QualifiedParamList;
import ca.uhn.fhir.util.TestUtil;
import org.junit.AfterClass;
import org.junit.Test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.List;
public class TokenOrListParamTest {
import org.junit.AfterClass;
import org.junit.Test;
import ca.uhn.fhir.model.dstu2.composite.CodingDt;
import ca.uhn.fhir.util.TestUtil;
public class TokenOrListParamDstu2Test {
@Test
public void testWhenParamListHasAnyMatchingCodingsForCodingList_doesCodingListMatch_shouldBeTrue() {
TokenOrListParam params = new TokenOrListParam();
@ -53,22 +52,6 @@ public class TokenOrListParamTest {
assertFalse(params.doesCodingListMatch(codings));
}
/**
* See #192
*/
@Test
public void testParseExcaped() {
TokenOrListParam params = new TokenOrListParam();
params.setValuesAsQueryTokens(ourCtx, null, QualifiedParamList.singleton("system|code-include-but-not-end-with-comma\\,suffix"));
assertEquals(1, params.getListAsCodings().size());
assertEquals("system", params.getListAsCodings().get(0).getSystemElement().getValue());
assertEquals("code-include-but-not-end-with-comma,suffix", params.getListAsCodings().get(0).getCodeElement().getValue());
}
private static FhirContext ourCtx = FhirContext.forDstu1();
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();

View File

@ -47,7 +47,8 @@ import org.hl7.fhir.exceptions.FHIRException;
* A statement of relationships from one set of concepts to one or more other concepts - either code systems or data elements, or classes in class models.
*/
@ResourceDef(name="ConceptMap", profile="http://hl7.org/fhir/Profile/ConceptMap")
@ChildOrder(names={"url", "identifier", "version", "name", "title", "status", "experimental", "date", "publisher", "contact", "description", "useContext", "jurisdiction", "purpose", "copyright", "source[x]", "target[x]", "group"})
//@ChildOrder(names={"url", "identifier", "version", "name", "title", "status", "experimental", "date", "publisher", "contact", "description", "useContext", "jurisdiction", "purpose", "copyright", "source[x]", "target[x]", "group"})
@ChildOrder(names={"url", "identifier", "version", "name", "title", "status", "experimental", "date", "publisher", "contact", "description", "useContext", "jurisdiction", "purpose", "copyright", "source", "target", "group"})
public class ConceptMap extends MetadataResource {
public enum ConceptMapGroupUnmappedMode {

View File

@ -1,17 +1,10 @@
package ca.uhn.fhir.parser;
import org.hl7.fhir.dstu3.model.DateTimeType;
import org.hl7.fhir.dstu3.model.DomainResource;
import org.hl7.fhir.dstu3.model.ResourceType;
import org.hl7.fhir.dstu3.model.Type;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.instance.model.api.ICompositeType;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.model.api.IElement;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.model.api.annotation.*;
import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.util.ElementUtil;

View File

@ -31,6 +31,8 @@ import java.util.*;
import org.apache.commons.io.IOUtils;
import org.hamcrest.Matchers;
import org.hamcrest.core.StringContains;
import org.hl7.fhir.dstu3.hapi.validation.DefaultProfileValidationSupport;
import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.dstu3.model.Address.AddressUse;
import org.hl7.fhir.dstu3.model.Address.AddressUseEnumFactory;
@ -42,6 +44,7 @@ import org.hl7.fhir.dstu3.model.Enumeration;
import org.hl7.fhir.dstu3.model.Enumerations.AdministrativeGender;
import org.hl7.fhir.dstu3.model.Identifier.IdentifierUse;
import org.hl7.fhir.dstu3.model.Observation.ObservationStatus;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
import org.junit.*;
@ -59,8 +62,7 @@ import ca.uhn.fhir.parser.XmlParserDstu3Test.TestPatientFor327;
import ca.uhn.fhir.parser.json.JsonLikeValue.ScalarType;
import ca.uhn.fhir.parser.json.JsonLikeValue.ValueType;
import ca.uhn.fhir.util.TestUtil;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.ValidationResult;
import ca.uhn.fhir.validation.*;
import net.sf.json.*;
public class JsonParserDstu3Test {
@ -71,6 +73,78 @@ public class JsonParserDstu3Test {
public void after() {
ourCtx.setNarrativeGenerator(null);
}
@Test
public void testActivityDefinitionElementsOrder() throws Exception {
final String origContent = "{\"resourceType\":\"ActivityDefinition\",\"id\":\"x1\",\"url\":\"http://testing.org\",\"status\":\"draft\",\"timingDateTime\":\"2011-02-03\"}";
final IParser parser = ourCtx.newJsonParser();
DefaultProfileValidationSupport validationSupport = new DefaultProfileValidationSupport();
// verify that InstanceValidator likes the format
{
IValidationContext<IBaseResource> validationCtx = ValidationContext.forText(ourCtx, origContent);
new FhirInstanceValidator(validationSupport).validateResource(validationCtx);
ValidationResult result = validationCtx.toResult();
for (SingleValidationMessage msg : result.getMessages()) {
ourLog.info("{}", msg);
}
Assert.assertEquals(0, result.getMessages().size());
}
ActivityDefinition fhirObj = parser.parseResource(ActivityDefinition.class, origContent);
String content = parser.encodeResourceToString(fhirObj);
ourLog.info("Serialized form: {}", content);
// verify that InstanceValidator still likes the format
{
IValidationContext<IBaseResource> validationCtx = ValidationContext.forText(ourCtx, content);
new FhirInstanceValidator(validationSupport).validateResource(validationCtx);
ValidationResult result = validationCtx.toResult();
for (SingleValidationMessage msg : result.getMessages()) {
ourLog.info("{}", msg);
}
Assert.assertEquals(0, result.getMessages().size());
}
// verify that the original and newly serialized match
Assert.assertEquals(origContent, content);
}
@Test
public void testConceptMapElementsOrder() throws Exception {
final String origContent = "{\"resourceType\":\"ConceptMap\",\"id\":\"x1\",\"url\":\"http://testing.org\",\"status\":\"draft\",\"sourceUri\":\"http://y1\"}";
final IParser parser = ourCtx.newJsonParser();
DefaultProfileValidationSupport validationSupport = new DefaultProfileValidationSupport();
// verify that InstanceValidator likes the format
{
IValidationContext<IBaseResource> validationCtx = ValidationContext.forText(ourCtx, origContent);
new FhirInstanceValidator(validationSupport).validateResource(validationCtx);
ValidationResult result = validationCtx.toResult();
for (SingleValidationMessage msg : result.getMessages()) {
ourLog.info("{}", msg);
}
Assert.assertEquals(0, result.getMessages().size());
}
ConceptMap fhirObj = parser.parseResource(ConceptMap.class, origContent);
String content = parser.encodeResourceToString(fhirObj);
ourLog.info("Serialized form: {}", content);
// verify that InstanceValidator still likes the format
{
IValidationContext<IBaseResource> validationCtx = ValidationContext.forText(ourCtx, content);
new FhirInstanceValidator(validationSupport).validateResource(validationCtx);
ValidationResult result = validationCtx.toResult();
for (SingleValidationMessage msg : result.getMessages()) {
ourLog.info("{}", msg);
}
Assert.assertEquals(0, result.getMessages().size());
}
// verify that the original and newly serialized match
Assert.assertEquals(origContent, content);
}
/**
* See #563

View File

@ -34,6 +34,8 @@ import org.custommonkey.xmlunit.XMLUnit;
import org.hamcrest.collection.IsEmptyCollection;
import org.hamcrest.core.StringContains;
import org.hamcrest.text.StringContainsInOrder;
import org.hl7.fhir.dstu3.hapi.validation.DefaultProfileValidationSupport;
import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.dstu3.model.Address.AddressUse;
import org.hl7.fhir.dstu3.model.Address.AddressUseEnumFactory;
@ -64,6 +66,10 @@ import ca.uhn.fhir.parser.IParserErrorHandler.IParseLocation;
import ca.uhn.fhir.parser.PatientWithCustomCompositeExtension.FooParentExtension;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.util.TestUtil;
import ca.uhn.fhir.validation.IValidationContext;
import ca.uhn.fhir.validation.SingleValidationMessage;
import ca.uhn.fhir.validation.ValidationContext;
import ca.uhn.fhir.validation.ValidationResult;
public class XmlParserDstu3Test {
private static FhirContext ourCtx = FhirContext.forDstu3();
@ -76,7 +82,7 @@ public class XmlParserDstu3Test {
}
ourCtx.setNarrativeGenerator(null);
}
/**
* See #544
*/
@ -104,7 +110,7 @@ public class XmlParserDstu3Test {
assertNotNull(subject);
assertEquals("FAMILY", subject.getNameFirstRep().getFamily());
}
@Test
public void testBundleWithBinary() {
@ -135,6 +141,81 @@ public class XmlParserDstu3Test {
}
/**
* See #683
*/
@Test
public void testChildOrderWithChoiceType() throws Exception {
final String origContent = "<ActivityDefinition xmlns=\"http://hl7.org/fhir\"><id value=\"x1\"/><url value=\"http://testing.org\"/><status value=\"draft\"/><timingDateTime value=\"2011-02-03\"/></ActivityDefinition>";
final IParser parser = ourCtx.newXmlParser();
DefaultProfileValidationSupport validationSupport = new DefaultProfileValidationSupport();
// verify that InstanceValidator likes the format
{
IValidationContext<IBaseResource> validationCtx = ValidationContext.forText(ourCtx, origContent);
new FhirInstanceValidator(validationSupport).validateResource(validationCtx);
ValidationResult result = validationCtx.toResult();
for (SingleValidationMessage msg : result.getMessages()) {
ourLog.info("{}", msg);
}
Assert.assertEquals(0, result.getMessages().size());
}
ActivityDefinition fhirObj = parser.parseResource(ActivityDefinition.class, origContent);
String content = parser.encodeResourceToString(fhirObj);
ourLog.info("Serialized form: {}", content);
// verify that InstanceValidator still likes the format
{
IValidationContext<IBaseResource> validationCtx = ValidationContext.forText(ourCtx, content);
new FhirInstanceValidator(validationSupport).validateResource(validationCtx);
ValidationResult result = validationCtx.toResult();
for (SingleValidationMessage msg : result.getMessages()) {
ourLog.info("{}", msg);
}
Assert.assertEquals(0, result.getMessages().size());
}
// verify that the original and newly serialized match
Assert.assertEquals(origContent, content);
}
@Test
public void testConceptMapElementsOrder() throws Exception {
final String origContent = "<ConceptMap xmlns=\"http://hl7.org/fhir\"><id value=\"x1\"/><url value=\"http://testing.org\"/><status value=\"draft\"/><sourceUri value=\"http://url1\"/></ConceptMap>";
final IParser parser = ourCtx.newXmlParser();
DefaultProfileValidationSupport validationSupport = new DefaultProfileValidationSupport();
// verify that InstanceValidator likes the format
{
IValidationContext<IBaseResource> validationCtx = ValidationContext.forText(ourCtx, origContent);
new FhirInstanceValidator(validationSupport).validateResource(validationCtx);
ValidationResult result = validationCtx.toResult();
for (SingleValidationMessage msg : result.getMessages()) {
ourLog.info("{}", msg);
}
Assert.assertEquals(0, result.getMessages().size());
}
ConceptMap fhirObj = parser.parseResource(ConceptMap.class, origContent);
String content = parser.encodeResourceToString(fhirObj);
ourLog.info("Serialized form: {}", content);
// verify that InstanceValidator still likes the format
{
IValidationContext<IBaseResource> validationCtx = ValidationContext.forText(ourCtx, content);
new FhirInstanceValidator(validationSupport).validateResource(validationCtx);
ValidationResult result = validationCtx.toResult();
for (SingleValidationMessage msg : result.getMessages()) {
ourLog.info("{}", msg);
}
Assert.assertEquals(0, result.getMessages().size());
}
// verify that the original and newly serialized match
Assert.assertEquals(origContent, content);
}
@Test
public void testContainedResourceInExtensionUndeclared() {
Patient p = new Patient();

View File

@ -5,10 +5,7 @@ import static org.junit.Assert.assertNotNull;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
import java.util.*;
import org.junit.AfterClass;
import org.junit.Test;
@ -18,13 +15,15 @@ import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum;
import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.rest.method.QualifiedParamList;
import ca.uhn.fhir.rest.api.QualifiedParamList;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.util.TestUtil;
public class DateRangeParamTest {
private static FhirContext ourCtx = FhirContext.forDstu3();
private static final SimpleDateFormat ourFmt;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DateRangeParamTest.class);
static {
@ -35,54 +34,6 @@ public class DateRangeParamTest {
return new DateRangeParam(new DateParam(theString));
}
@Test
public void testRangeFromDates() {
TimeZone tz = TimeZone.getDefault();
TimeZone.setDefault(TimeZone.getTimeZone("America/Toronto"));
try {
Date startDate = new InstantDt("2010-01-01T00:00:00.000Z").getValue();
Date endDate = new InstantDt("2010-01-01T00:00:00.001Z").getValue();
DateTimeDt startDateTime = new DateTimeDt(startDate, TemporalPrecisionEnum.MILLI);
DateTimeDt endDateTime = new DateTimeDt(endDate, TemporalPrecisionEnum.MILLI);
DateRangeParam range = new DateRangeParam(startDateTime, endDateTime);
assertEquals("2009-12-31T19:00:00.000-05:00", range.getValuesAsQueryTokens().get(0).getValueAsString());
assertEquals("2009-12-31T19:00:00.001-05:00", range.getValuesAsQueryTokens().get(1).getValueAsString());
// Now try with arguments reversed (should still create same range)
range = new DateRangeParam(endDateTime, startDateTime);
assertEquals("2009-12-31T19:00:00.000-05:00", range.getValuesAsQueryTokens().get(0).getValueAsString());
assertEquals("2009-12-31T19:00:00.001-05:00", range.getValuesAsQueryTokens().get(1).getValueAsString());
} finally {
TimeZone.setDefault(tz);
}
}
@Test
public void testRange() {
InstantDt start = new InstantDt("2015-09-23T07:43:34.811-04:00");
InstantDt end = new InstantDt("2015-09-23T07:43:34.899-04:00");
DateParam lowerBound = new DateParam(QuantityCompararatorEnum.GREATERTHAN, start.getValue());
DateParam upperBound = new DateParam(QuantityCompararatorEnum.LESSTHAN, end.getValue());
assertEquals(QuantityCompararatorEnum.GREATERTHAN, lowerBound.getComparator());
assertEquals(QuantityCompararatorEnum.LESSTHAN, upperBound.getComparator());
/*
* When DateParam (which extends DateTimeDt) gets passed in, make sure we preserve the comparators..
*/
DateRangeParam param = new DateRangeParam(lowerBound, upperBound);
ourLog.info(param.toString());
assertEquals(QuantityCompararatorEnum.GREATERTHAN, param.getLowerBound().getComparator());
assertEquals(QuantityCompararatorEnum.LESSTHAN, param.getUpperBound().getComparator());
param = new DateRangeParam(new DateTimeDt(lowerBound.getValue()), new DateTimeDt(upperBound.getValue()));
ourLog.info(param.toString());
assertEquals(QuantityCompararatorEnum.GREATERTHAN_OR_EQUALS, param.getLowerBound().getComparator());
assertEquals(QuantityCompararatorEnum.LESSTHAN_OR_EQUALS, param.getUpperBound().getComparator());
}
@Test
public void testAddAnd() {
assertEquals(1, new DateAndListParam().addAnd(new DateOrListParam()).getValuesAsQueryTokens().size());
@ -159,6 +110,54 @@ public class DateRangeParamTest {
assertNotNull(new StringOrListParam().newInstance());
}
@Test
public void testRange() {
InstantDt start = new InstantDt("2015-09-23T07:43:34.811-04:00");
InstantDt end = new InstantDt("2015-09-23T07:43:34.899-04:00");
DateParam lowerBound = new DateParam(QuantityCompararatorEnum.GREATERTHAN, start.getValue());
DateParam upperBound = new DateParam(QuantityCompararatorEnum.LESSTHAN, end.getValue());
assertEquals(QuantityCompararatorEnum.GREATERTHAN, lowerBound.getComparator());
assertEquals(QuantityCompararatorEnum.LESSTHAN, upperBound.getComparator());
/*
* When DateParam (which extends DateTimeDt) gets passed in, make sure we preserve the comparators..
*/
DateRangeParam param = new DateRangeParam(lowerBound, upperBound);
ourLog.info(param.toString());
assertEquals(QuantityCompararatorEnum.GREATERTHAN, param.getLowerBound().getComparator());
assertEquals(QuantityCompararatorEnum.LESSTHAN, param.getUpperBound().getComparator());
param = new DateRangeParam(new DateTimeDt(lowerBound.getValue()), new DateTimeDt(upperBound.getValue()));
ourLog.info(param.toString());
assertEquals(QuantityCompararatorEnum.GREATERTHAN_OR_EQUALS, param.getLowerBound().getComparator());
assertEquals(QuantityCompararatorEnum.LESSTHAN_OR_EQUALS, param.getUpperBound().getComparator());
}
@Test
public void testRangeFromDates() {
TimeZone tz = TimeZone.getDefault();
TimeZone.setDefault(TimeZone.getTimeZone("America/Toronto"));
try {
Date startDate = new InstantDt("2010-01-01T00:00:00.000Z").getValue();
Date endDate = new InstantDt("2010-01-01T00:00:00.001Z").getValue();
DateTimeDt startDateTime = new DateTimeDt(startDate, TemporalPrecisionEnum.MILLI);
DateTimeDt endDateTime = new DateTimeDt(endDate, TemporalPrecisionEnum.MILLI);
DateRangeParam range = new DateRangeParam(startDateTime, endDateTime);
assertEquals("2009-12-31T19:00:00.000-05:00", range.getValuesAsQueryTokens().get(0).getValueAsString());
assertEquals("2009-12-31T19:00:00.001-05:00", range.getValuesAsQueryTokens().get(1).getValueAsString());
// Now try with arguments reversed (should still create same range)
range = new DateRangeParam(endDateTime, startDateTime);
assertEquals("2009-12-31T19:00:00.000-05:00", range.getValuesAsQueryTokens().get(0).getValueAsString());
assertEquals("2009-12-31T19:00:00.001-05:00", range.getValuesAsQueryTokens().get(1).getValueAsString());
} finally {
TimeZone.setDefault(tz);
}
}
@Test
public void testSecond() throws Exception {
assertEquals(parse("2011-01-01 00:00:00.0000"), create(">=2011-01-01T00:00:00", "<2011-01-01T01:00:00").getLowerBoundAsInstant());
@ -177,6 +176,11 @@ public class DateRangeParamTest {
assertEquals(parseM1("2014-01-01 00:00:00.0000"), create(">2011", "<=2013").getUpperBoundAsInstant());
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
private static DateRangeParam create(String theLower, String theUpper) throws InvalidRequestException {
DateRangeParam p = new DateRangeParam();
List<QualifiedParamList> tokens = new ArrayList<QualifiedParamList>();
@ -188,8 +192,6 @@ public class DateRangeParamTest {
return p;
}
private static FhirContext ourCtx = FhirContext.forDstu1();
public static Date parse(String theString) throws ParseException {
return ourFmt.parse(theString);
}
@ -198,9 +200,4 @@ public class DateRangeParamTest {
return new Date(ourFmt.parse(theString).getTime() - 1L);
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
}

View File

@ -0,0 +1,76 @@
package ca.uhn.fhir.rest.param;
import static org.junit.Assert.assertEquals;
import java.math.BigDecimal;
import org.junit.AfterClass;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.util.TestUtil;
public class NumberParamTest {
private static FhirContext ourCtx = FhirContext.forDstu3();
@Test
public void testFull() {
NumberParam p = new NumberParam();
p.setValueAsQueryToken(ourCtx, null, null, "<5.4");
assertEquals(ParamPrefixEnum.LESSTHAN, p.getPrefix());
assertEquals("5.4", p.getValue().toPlainString());
assertEquals("lt5.4", p.getValueAsQueryToken(ourCtx));
}
@Test
public void testApproximateLegacy() {
NumberParam p = new NumberParam();
p.setValueAsQueryToken(ourCtx, null, null, "~5.4");
assertEquals(null,p.getComparator());
assertEquals(ParamPrefixEnum.APPROXIMATE, p.getPrefix());
assertEquals("5.4", p.getValue().toPlainString());
assertEquals("ap5.4", p.getValueAsQueryToken(ourCtx));
}
@Test
public void testApproximate() {
NumberParam p = new NumberParam();
p.setValueAsQueryToken(ourCtx, null, null, "ap5.4");
assertEquals(null,p.getComparator());
assertEquals(ParamPrefixEnum.APPROXIMATE, p.getPrefix());
assertEquals("5.4", p.getValue().toPlainString());
assertEquals("ap5.4", p.getValueAsQueryToken(ourCtx));
}
@Test
public void testNoQualifier() {
NumberParam p = new NumberParam();
p.setValueAsQueryToken(ourCtx, null, null, "5.4");
assertEquals(null, p.getComparator());
assertEquals(null, p.getPrefix());
assertEquals("5.4", p.getValue().toPlainString());
assertEquals("5.4", p.getValueAsQueryToken(ourCtx));
}
/**
* See #696
*/
@Test
public void testNegativeNumber() {
NumberParam p = new NumberParam();
p.setValueAsQueryToken(ourCtx, null, null, "-5.4");
assertEquals(null, p.getComparator());
assertEquals(null, p.getPrefix());
assertEquals("-5.4", p.getValue().toPlainString());
assertEquals(new BigDecimal("-5.4"), p.getValue());
assertEquals("-5.4", p.getValueAsQueryToken(ourCtx));
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
}

View File

@ -2,6 +2,8 @@ package ca.uhn.fhir.rest.param;
import static org.junit.Assert.*;
import java.math.BigDecimal;
import org.junit.AfterClass;
import org.junit.Test;
@ -10,37 +12,52 @@ import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum;
import ca.uhn.fhir.util.TestUtil;
public class QuantityParamTest {
private static FhirContext ourCtx = FhirContext.forDstu1();
private static FhirContext ourCtx = FhirContext.forDstu3();
@Test
public void testFull() {
QuantityParam p = new QuantityParam();
p.setValueAsQueryToken(ourCtx, null, null, "<5.4|http://unitsofmeasure.org|mg");
assertEquals(QuantityCompararatorEnum.LESSTHAN,p.getComparator());
assertEquals(ParamPrefixEnum.LESSTHAN, p.getPrefix());
assertEquals("5.4", p.getValue().toPlainString());
assertEquals("http://unitsofmeasure.org", p.getSystem());
assertEquals("mg", p.getUnits());
assertEquals("<5.4|http://unitsofmeasure.org|mg", p.getValueAsQueryToken(ourCtx));
assertEquals("lt5.4|http://unitsofmeasure.org|mg", p.getValueAsQueryToken(ourCtx));
}
@Test
public void testApproximateLegacy() {
QuantityParam p = new QuantityParam();
p.setValueAsQueryToken(ourCtx, null, null, "~5.4|http://unitsofmeasure.org|mg");
assertEquals(null,p.getComparator());
assertEquals(ParamPrefixEnum.APPROXIMATE, p.getPrefix());
assertEquals(true, p.isApproximate());
assertEquals("5.4", p.getValue().toPlainString());
assertEquals("http://unitsofmeasure.org", p.getSystem());
assertEquals("mg", p.getUnits());
assertEquals("ap5.4|http://unitsofmeasure.org|mg", p.getValueAsQueryToken(ourCtx));
}
@Test
public void testApproximate() {
QuantityParam p = new QuantityParam();
p.setValueAsQueryToken(ourCtx, null, null, "~5.4|http://unitsofmeasure.org|mg");
p.setValueAsQueryToken(ourCtx, null, null, "ap5.4|http://unitsofmeasure.org|mg");
assertEquals(null,p.getComparator());
assertEquals(ParamPrefixEnum.APPROXIMATE, p.getPrefix());
assertEquals(true, p.isApproximate());
assertEquals("5.4", p.getValue().toPlainString());
assertEquals("http://unitsofmeasure.org", p.getSystem());
assertEquals("mg", p.getUnits());
assertEquals("~5.4|http://unitsofmeasure.org|mg", p.getValueAsQueryToken(ourCtx));
assertEquals("ap5.4|http://unitsofmeasure.org|mg", p.getValueAsQueryToken(ourCtx));
}
@Test
public void testNoQualifier() {
QuantityParam p = new QuantityParam();
p.setValueAsQueryToken(ourCtx, null, null, "5.4|http://unitsofmeasure.org|mg");
assertEquals(null, p.getComparator());
assertEquals(null, p.getPrefix());
assertEquals("5.4", p.getValue().toPlainString());
assertEquals("http://unitsofmeasure.org", p.getSystem());
assertEquals("mg", p.getUnits());
@ -53,6 +70,7 @@ public class QuantityParamTest {
QuantityParam p = new QuantityParam();
p.setValueAsQueryToken(ourCtx, null, null, "5.4");
assertEquals(null, p.getComparator());
assertEquals(null, p.getPrefix());
assertEquals("5.4", p.getValue().toPlainString());
assertEquals(null, p.getSystem());
assertEquals(null, p.getUnits());
@ -84,6 +102,54 @@ public class QuantityParamTest {
assertEquals("mg", param.getUnits());
}
/**
* See #696
*/
@Test
public void testNegativeQuantityWithUnits() {
QuantityParam p = new QuantityParam();
p.setValueAsQueryToken(ourCtx, null, null, "-5.4|http://unitsofmeasure.org|mg");
assertEquals(null, p.getComparator());
assertEquals(null, p.getPrefix());
assertEquals("-5.4", p.getValue().toPlainString());
assertEquals(new BigDecimal("-5.4"), p.getValue());
assertEquals("http://unitsofmeasure.org", p.getSystem());
assertEquals("mg", p.getUnits());
assertEquals("-5.4|http://unitsofmeasure.org|mg", p.getValueAsQueryToken(ourCtx));
}
/**
* See #696
*/
@Test
public void testNegativeQuantityWithoutUnits() {
QuantityParam p = new QuantityParam();
p.setValueAsQueryToken(ourCtx, null, null, "-5.4");
assertEquals(null, p.getComparator());
assertEquals(null, p.getPrefix());
assertEquals("-5.4", p.getValue().toPlainString());
assertEquals(new BigDecimal("-5.4"), p.getValue());
assertEquals(null, p.getSystem());
assertEquals(null, p.getUnits());
assertEquals("-5.4||", p.getValueAsQueryToken(ourCtx));
}
/**
* See #696
*/
@Test
public void testNegativeQuantityWithoutUnitsWithComparator() {
QuantityParam p = new QuantityParam();
p.setValueAsQueryToken(ourCtx, null, null, "gt-5.4");
assertEquals(QuantityCompararatorEnum.GREATERTHAN, p.getComparator());
assertEquals(ParamPrefixEnum.GREATERTHAN, p.getPrefix());
assertEquals("-5.4", p.getValue().toPlainString());
assertEquals(new BigDecimal("-5.4"), p.getValue());
assertEquals(null, p.getSystem());
assertEquals(null, p.getUnits());
assertEquals("gt-5.4||", p.getValueAsQueryToken(ourCtx));
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();

View File

@ -10,15 +10,7 @@ import ca.uhn.fhir.util.TestUtil;
public class ReferenceParamTest {
@Test
public void testWithResourceTypeAsQualifier() {
ReferenceParam rp = new ReferenceParam();
rp.setValueAsQueryToken(ourCtx, null, ":Location", "123");
assertEquals("Location", rp.getResourceType());
assertEquals("123", rp.getIdPart());
}
private FhirContext ourCtx = FhirContext.forDstu3();
@Test
public void testWithResourceType() {
@ -30,7 +22,15 @@ public class ReferenceParamTest {
}
private FhirContext ourCtx = FhirContext.forDstu1();
@Test
public void testWithResourceTypeAsQualifier() {
ReferenceParam rp = new ReferenceParam();
rp.setValueAsQueryToken(ourCtx, null, ":Location", "123");
assertEquals("Location", rp.getResourceType());
assertEquals("123", rp.getIdPart());
}
@AfterClass
public static void afterClassClearContext() {

View File

@ -0,0 +1,34 @@
package ca.uhn.fhir.rest.param;
import static org.junit.Assert.assertEquals;
import org.junit.AfterClass;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.api.QualifiedParamList;
import ca.uhn.fhir.util.TestUtil;
public class TokenOrListParamDstu3Test {
private static FhirContext ourCtx = FhirContext.forDstu3();
/**
* See #192
*/
@Test
public void testParseExcaped() {
TokenOrListParam params = new TokenOrListParam();
params.setValuesAsQueryTokens(ourCtx, null, QualifiedParamList.singleton("system|code-include-but-not-end-with-comma\\,suffix"));
assertEquals(1, params.getListAsCodings().size());
assertEquals("system", params.getListAsCodings().get(0).getSystemElement().getValue());
assertEquals("code-include-but-not-end-with-comma,suffix", params.getListAsCodings().get(0).getCodeElement().getValue());
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
}

View File

@ -1,4 +1,4 @@
package org.hl7.fhir.r4.hapi.validation;
package org.hl7.fhir.r4.hapi.ctx;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
@ -68,9 +68,9 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
codeSystems = new HashMap<String, CodeSystem>();
valueSets = new HashMap<String, ValueSet>();
loadCodeSystems(theContext, codeSystems, valueSets, "/org/hl7/fhir/instance/model/dstu3/valueset/valuesets.xml");
loadCodeSystems(theContext, codeSystems, valueSets, "/org/hl7/fhir/instance/model/dstu3/valueset/v2-tables.xml");
loadCodeSystems(theContext, codeSystems, valueSets, "/org/hl7/fhir/instance/model/dstu3/valueset/v3-codesystems.xml");
loadCodeSystems(theContext, codeSystems, valueSets, "/org/hl7/fhir/r4/model/valueset/valuesets.xml");
loadCodeSystems(theContext, codeSystems, valueSets, "/org/hl7/fhir/r4/model/valueset/v2-tables.xml");
loadCodeSystems(theContext, codeSystems, valueSets, "/org/hl7/fhir/r4/model/valueset/v3-codesystems.xml");
myCodeSystems = codeSystems;
myValueSets = valueSets;
@ -184,9 +184,9 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
if (structureDefinitions == null) {
structureDefinitions = new HashMap<String, StructureDefinition>();
loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/instance/model/dstu3/profile/profiles-resources.xml");
loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/instance/model/dstu3/profile/profiles-types.xml");
loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/instance/model/dstu3/profile/profiles-others.xml");
loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/r4/model/profile/profiles-resources.xml");
loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/r4/model/profile/profiles-types.xml");
loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/r4/model/profile/profiles-others.xml");
myStructureDefinitions = structureDefinitions;
}

View File

@ -28,7 +28,6 @@ import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.*;
import org.hl7.fhir.r4.hapi.fluentpath.FluentPathR4;
import org.hl7.fhir.r4.hapi.rest.server.R4BundleFactory;
import org.hl7.fhir.r4.hapi.validation.DefaultProfileValidationSupport;
import org.hl7.fhir.r4.model.*;
import ca.uhn.fhir.context.*;
@ -41,89 +40,90 @@ import ca.uhn.fhir.util.ReflectionUtil;
public class FhirR4 implements IFhirVersion {
private String myId;
private String myId;
@Override
public IFluentPath createFluentPathExecutor(FhirContext theFhirContext) {
return new FluentPathR4(theFhirContext);
}
@Override
public IFluentPath createFluentPathExecutor(FhirContext theFhirContext) {
return new FluentPathR4(theFhirContext);
}
@Override
public IContextValidationSupport<?, ?, ?, ?, ?, ?> createValidationSupport() {
return new DefaultProfileValidationSupport();
}
@Override
public IContextValidationSupport<?, ?, ?, ?, ?, ?> createValidationSupport() {
return ReflectionUtil.newInstanceOfFhirProfileValidationSupport("org.hl7.fhir.r4.hapi.validation.DefaultProfileValidationSupport");
}
@Override
public IBaseResource generateProfile(RuntimeResourceDefinition theRuntimeResourceDefinition, String theServerBase) {
StructureDefinition retVal = new StructureDefinition();
@Override
public IBaseResource generateProfile(RuntimeResourceDefinition theRuntimeResourceDefinition, String theServerBase) {
StructureDefinition retVal = new StructureDefinition();
RuntimeResourceDefinition def = theRuntimeResourceDefinition;
RuntimeResourceDefinition def = theRuntimeResourceDefinition;
myId = def.getId();
if (StringUtils.isBlank(myId)) {
myId = theRuntimeResourceDefinition.getName().toLowerCase();
}
myId = def.getId();
if (StringUtils.isBlank(myId)) {
myId = theRuntimeResourceDefinition.getName().toLowerCase();
}
retVal.setId(new IdDt(myId));
return retVal;
}
retVal.setId(new IdDt(myId));
return retVal;
}
@SuppressWarnings("rawtypes")
@Override
public Class<List> getContainedType() {
return List.class;
}
@SuppressWarnings("rawtypes")
@Override
public Class<List> getContainedType() {
return List.class;
}
@Override
public InputStream getFhirVersionPropertiesFile() {
InputStream str = FhirR4.class.getResourceAsStream("/org/hl7/fhir/r4/model/fhirversion.properties");
if (str == null) {
str = FhirR4.class.getResourceAsStream("/org/hl7/fhir/r4/model/fhirversion.properties");
}
if (str == null) {
throw new ConfigurationException("Can not find model property file on classpath: " + "/ca/uhn/fhir/model/r4/fhirversion.properties");
}
return str;
}
@Override
public InputStream getFhirVersionPropertiesFile() {
String path = "org/hl7/fhir/r4/model/fhirversion.properties";
InputStream str = FhirR4.class.getResourceAsStream("/" + path);
if (str == null) {
str = FhirR4.class.getResourceAsStream(path);
}
if (str == null) {
throw new ConfigurationException("Can not find model property file on classpath: " + path);
}
return str;
}
@Override
public IPrimitiveType<Date> getLastUpdated(IBaseResource theResource) {
return ((Resource) theResource).getMeta().getLastUpdatedElement();
}
@Override
public IPrimitiveType<Date> getLastUpdated(IBaseResource theResource) {
return ((Resource) theResource).getMeta().getLastUpdatedElement();
}
@Override
public String getPathToSchemaDefinitions() {
return "/org/hl7/fhir/instance/model/dstu3/schema";
}
@Override
public String getPathToSchemaDefinitions() {
return "/org/hl7/fhir/r4/model/schema";
}
@Override
public Class<? extends IBaseReference> getResourceReferenceType() {
return Reference.class;
}
@Override
public Class<? extends IBaseReference> getResourceReferenceType() {
return Reference.class;
}
@Override
public Object getServerVersion() {
return ReflectionUtil.newInstanceOfFhirServerType("org.hl7.fhir.dstu3.hapi.ctx.FhirServerDstu3");
}
@Override
public Object getServerVersion() {
return ReflectionUtil.newInstanceOfFhirServerType("org.hl7.fhir.r4.hapi.ctx.FhirServerR4");
}
@Override
public FhirVersionEnum getVersion() {
return FhirVersionEnum.R4;
}
@Override
public FhirVersionEnum getVersion() {
return FhirVersionEnum.R4;
}
@Override
public IVersionSpecificBundleFactory newBundleFactory(FhirContext theContext) {
return new R4BundleFactory(theContext);
}
@Override
public IVersionSpecificBundleFactory newBundleFactory(FhirContext theContext) {
return new R4BundleFactory(theContext);
}
@Override
public IBaseCoding newCodingDt() {
return new Coding();
}
@Override
public IBaseCoding newCodingDt() {
return new Coding();
}
@Override
public IIdType newIdType() {
return new IdType();
}
@Override
public IIdType newIdType() {
return new IdType();
}
}

View File

@ -1,19 +1,17 @@
package org.hl7.fhir.r4.hapi.validation;
package org.hl7.fhir.r4.hapi.ctx;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.util.*;
import javax.annotation.Resource;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.TerminologyServiceException;
import org.hl7.fhir.r4.context.IWorkerContext;
import org.hl7.fhir.r4.formats.IParser;
import org.hl7.fhir.r4.formats.ParserType;
import org.hl7.fhir.r4.hapi.validation.IValidationSupport.CodeValidationResult;
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport.CodeValidationResult;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionBindingComponent;

View File

@ -1,4 +1,4 @@
package org.hl7.fhir.r4.hapi.validation;
package org.hl7.fhir.r4.hapi.ctx;
import java.util.List;

View File

@ -1,4 +1,4 @@
package org.hl7.fhir.r4.hapi.validation;
package org.hl7.fhir.r4.hapi.ctx;
import static org.apache.commons.lang3.StringUtils.isNotBlank;

View File

@ -1,4 +1,4 @@
package org.hl7.fhir.r4.hapi.validation;
package org.hl7.fhir.r4.hapi.ctx;
import static org.apache.commons.lang3.StringUtils.isBlank;

View File

@ -4,8 +4,8 @@ import java.util.List;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.r4.hapi.validation.HapiWorkerContext;
import org.hl7.fhir.r4.hapi.validation.IValidationSupport;
import org.hl7.fhir.r4.hapi.ctx.HapiWorkerContext;
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
import org.hl7.fhir.r4.model.Base;
import org.hl7.fhir.r4.utils.FHIRPathEngine;

View File

@ -1,40 +0,0 @@
package org.hl7.fhir.r4.hapi.validation;
import java.util.List;
import org.hl7.fhir.r4.model.StructureDefinition;
import org.hl7.fhir.r4.utils.FHIRPathEngine.IEvaluationContext;
import org.hl7.fhir.r4.utils.IResourceValidator.BestPracticeWarningLevel;
import org.hl7.fhir.r4.utils.IResourceValidator.IdStatus;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.w3c.dom.Document;
import com.google.gson.JsonObject;
public class InstanceValidator {
public InstanceValidator(HapiWorkerContext theWorkerContext, IEvaluationContext theEvaluationCtx) {
throw new Error();
}
public void setBestPracticeWarningLevel(BestPracticeWarningLevel theBestPracticeWarningLevel) {
throw new Error();
}
public void setAnyExtensionsAllowed(boolean theAnyExtensionsAllowed) {
throw new Error();
}
public void setResourceIdRule(IdStatus theOptional) {
throw new Error();
}
public void validate(Object theObject, List<ValidationMessage> theMessages, Document theDocument, StructureDefinition theProfile) {
throw new Error();
}
public void validate(Object theObject, List<ValidationMessage> theMessages, JsonObject theJson, StructureDefinition theProfile) {
throw new Error();
}
}

View File

@ -167,7 +167,6 @@ import org.hl7.fhir.utilities.xml.XmlGenerator;
import org.w3c.dom.Element;
import com.github.rjeschke.txtmark.Processor;
import com.sun.webkit.BackForwardList;
public class NarrativeGenerator implements INarrativeGenerator {

View File

@ -0,0 +1,92 @@
package ca.uhn.fhir.parser;
import org.hl7.fhir.instance.model.api.ICompositeType;
import org.hl7.fhir.r4.model.*;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.model.api.annotation.*;
import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.util.ElementUtil;
/**
* This is an example of a custom resource that also uses a custom
* datatype.
*
* See #364
*/
@ResourceDef(name = "CustomResource", profile = "http://hl7.org/fhir/profiles/custom-resource", id = "custom-resource")
public class CustomResource364R4 extends DomainResource {
private static final long serialVersionUID = 1L;
@Child(name = "baseValue", min = 1, max = Child.MAX_UNLIMITED, type= {})
private Type baseValues;
public Type getBaseValues() {
return baseValues;
}
@Override
public FhirVersionEnum getStructureFhirVersionEnum() {
return FhirVersionEnum.R4;
}
@Override
public boolean isEmpty() {
return ElementUtil.isEmpty(baseValues);
}
public void setBaseValues(Type theValue) {
this.baseValues = theValue;
}
@DatatypeDef(name="CustomDate")
public static class CustomResource364CustomDate extends Type implements ICompositeType {
private static final long serialVersionUID = 1L;
@Child(name = "date", order = 0, min = 1, max = 1, type = { DateTimeDt.class })
private DateTimeType date;
public DateTimeType getDate() {
if (date == null)
date = new DateTimeType();
return date;
}
@Override
public boolean isEmpty() {
return ElementUtil.isEmpty(date);
}
public CustomResource364CustomDate setDate(DateTimeType theValue) {
date = theValue;
return this;
}
@Override
protected CustomResource364CustomDate typedCopy() {
CustomResource364CustomDate retVal = new CustomResource364CustomDate();
super.copyValues(retVal);
retVal.date = date;
return retVal;
}
}
@Override
public CustomResource364R4 copy() {
CustomResource364R4 retVal = new CustomResource364R4();
super.copyValues(retVal);
retVal.baseValues = baseValues;
return retVal;
}
@Override
public ResourceType getResourceType() {
return null;
}
}

View File

@ -0,0 +1,647 @@
package ca.uhn.fhir.parser;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.stringContainsInOrder;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import java.util.ArrayList;
import java.util.List;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.r4.model.*;
import org.junit.*;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.api.AddProfileTagEnum;
import ca.uhn.fhir.model.api.annotation.*;
import ca.uhn.fhir.model.api.annotation.Extension;
import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.parser.CustomResource364R4.CustomResource364CustomDate;
import ca.uhn.fhir.util.ElementUtil;
import ca.uhn.fhir.util.TestUtil;
public class CustomTypeR4Test {
private static FhirContext ourCtx = FhirContext.forR4();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CustomTypeR4Test.class);
@Before
public void before() {
ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.ONLY_FOR_CUSTOM);
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@SuppressWarnings("serial")
@ResourceDef
public static class PatientWithExtensionWithTwoTypes extends Patient {
@Child(name = "foo", type = { DateTimeType.class, Period.class }, min = 0, max = 1)
@Extension(url = "http://example.com/extension#foo", definedLocally = true, isModifier = false)
@Description(shortDefinition = "Some description")
private Type foo;
public Type getFoo() {
return foo;
}
public void setFoo(Type theFoo) {
foo = theFoo;
}
}
@Test
public void testExtensionWithTwoTypes() {
PatientWithExtensionWithTwoTypes pt = new PatientWithExtensionWithTwoTypes();
pt.setFoo(new DateTimeType("2011-01-01T00:00:00Z"));
String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(pt);
ourLog.info(encoded);
pt = ourCtx.newXmlParser().parseResource(PatientWithExtensionWithTwoTypes.class, encoded);
assertEquals("2011-01-01T00:00:00Z", ((DateTimeType)pt.getFoo()).getValueAsString());
}
@SuppressWarnings("serial")
@ResourceDef
public static class PatientWithExtensionWithOneTypes extends Patient {
@Child(name = "foo", type = { DateTimeType.class }, min = 0, max = 1)
@Extension(url = "http://example.com/extension#foo", definedLocally = true, isModifier = false)
@Description(shortDefinition = "Some description")
private Type foo;
public Type getFoo() {
return foo;
}
public void setFoo(Type theFoo) {
foo = theFoo;
}
}
@Test
public void testExtensionWithOneTypes() {
PatientWithExtensionWithOneTypes pt = new PatientWithExtensionWithOneTypes();
pt.setFoo(new DateTimeType("2011-01-01T00:00:00Z"));
String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(pt);
ourLog.info(encoded);
pt = ourCtx.newXmlParser().parseResource(PatientWithExtensionWithOneTypes.class, encoded);
assertEquals("2011-01-01T00:00:00Z", ((DateTimeType)pt.getFoo()).getValueAsString());
}
/**
* See #364
*/
@Test
public void testCustomTypeWithCustomDatatype() {
FhirContext context = FhirContext.forR4();
context.registerCustomType(CustomResource364R4.class);
context.registerCustomType(CustomResource364CustomDate.class);
IParser parser = context.newXmlParser();
CustomResource364R4 resource = new CustomResource364R4();
resource.setBaseValues(new CustomResource364CustomDate().setDate(new DateTimeType("2016-05-13")));
String xml = parser.encodeResourceToString(resource);
ourLog.info(xml);
//@formatter:on
assertThat(xml, stringContainsInOrder(
"<CustomResource xmlns=\"http://hl7.org/fhir\">",
"<meta><profile value=\"http://hl7.org/fhir/profiles/custom-resource\"/></meta>",
"<baseValueCustomDate><date value=\"2016-05-13\"/></baseValueCustomDate>",
"</CustomResource>"));
//@formatter:on
CustomResource364R4 parsedResource = parser.parseResource(CustomResource364R4.class, xml);
assertEquals("2016-05-13", ((CustomResource364CustomDate) parsedResource.getBaseValues()).getDate().getValueAsString());
}
/**
* See #364
*/
@Test
public void testCustomTypeWithPrimitiveType() {
FhirContext context = FhirContext.forR4();
context.registerCustomTypes(new ArrayList<Class<? extends IBase>>());
IParser parser = context.newXmlParser();
CustomResource364R4 resource = new CustomResource364R4();
resource.setBaseValues(new StringType("2016-05-13"));
String xml = parser.encodeResourceToString(resource);
//@formatter:on
assertThat(xml, stringContainsInOrder(
"<CustomResource xmlns=\"http://hl7.org/fhir\">",
"<meta><profile value=\"http://hl7.org/fhir/profiles/custom-resource\"/></meta>",
"<baseValueString value=\"2016-05-13\"/>",
"</CustomResource>"));
//@formatter:on
CustomResource364R4 parsedResource = parser.parseResource(CustomResource364R4.class, xml);
assertEquals("2016-05-13", ((StringType) parsedResource.getBaseValues()).getValueAsString());
}
@Test
public void parseBundleWithResourceDirective() {
String input = createBundle(createResource(false), createResource(true));
FhirContext ctx = FhirContext.forR4();
ctx.setDefaultTypeForProfile("http://example.com/foo", MyCustomPatient.class);
Bundle bundle = ctx.newXmlParser().parseResource(Bundle.class, input);
Patient res0 = (Patient) bundle.getEntry().get(0).getResource();
assertEquals(0, res0.getMeta().getProfile().size());
List<org.hl7.fhir.r4.model.Extension> exts = res0.getExtensionsByUrl("http://example.com/Weight");
assertEquals(1, exts.size());
assertEquals("185 cm", ((StringType) exts.get(0).getValue()).getValue());
MyCustomPatient res1 = (MyCustomPatient) bundle.getEntry().get(1).getResource();
assertEquals(1, res1.getMeta().getProfile().size());
assertEquals("http://example.com/foo", res1.getMeta().getProfile().get(0).getValue());
exts = res1.getExtensionsByUrl("http://example.com/Weight");
assertEquals(0, exts.size());
assertEquals("185 cm", res1.getWeight().getValue());
}
@Test
public void parseResourceWithDirective() {
String input = createResource(true);
FhirContext ctx = FhirContext.forR4();
ctx.setDefaultTypeForProfile("http://example.com/foo", MyCustomPatient.class);
MyCustomPatient parsed = (MyCustomPatient) ctx.newXmlParser().parseResource(input);
assertEquals(1, parsed.getMeta().getProfile().size());
assertEquals("http://example.com/foo", parsed.getMeta().getProfile().get(0).getValue());
List<org.hl7.fhir.r4.model.Extension> exts = parsed.getExtensionsByUrl("http://example.com/Weight");
assertEquals(0, exts.size());
assertEquals("185 cm", parsed.getWeight().getValue());
}
@Test
public void parseResourceWithNoDirective() {
String input = createResource(true);
FhirContext ctx = FhirContext.forR4();
Patient parsed = (Patient) ctx.newXmlParser().parseResource(input);
assertEquals(1, parsed.getMeta().getProfile().size());
assertEquals("http://example.com/foo", parsed.getMeta().getProfile().get(0).getValue());
List<org.hl7.fhir.r4.model.Extension> exts = parsed.getExtensionsByUrl("http://example.com/Weight");
assertEquals(1, exts.size());
assertEquals("185 cm", ((StringType) exts.get(0).getValue()).getValue());
}
@Test
public void testAccessEmptyMetaLists() {
Patient p = new Patient();
assertThat(p.getMeta().getProfile(), empty());
assertThat(p.getMeta().getFormatCommentsPost(), empty());
assertThat(p.getMeta().getFormatCommentsPre(), empty());
assertThat(p.getMeta().getLastUpdated(), nullValue());
assertThat(p.getMeta().getSecurity(), empty());
assertThat(p.getMeta().getSecurity("foo", "bar"), nullValue());
assertThat(p.getMeta().getTag(), empty());
assertThat(p.getMeta().getTag("foo", "bar"), nullValue());
assertThat(p.getMeta().getVersionId(), nullValue());
}
@Test
public void testEncodeCompleteMetaLists() {
Patient p = new Patient();
p.getMeta().addProfile("http://foo/profile1");
p.getMeta().addProfile("http://foo/profile2");
p.getMeta().addSecurity().setSystem("SEC_S1").setCode("SEC_C1").setDisplay("SED_D1");
p.getMeta().addSecurity().setSystem("SEC_S2").setCode("SEC_C2").setDisplay("SED_D2");
p.getMeta().addTag().setSystem("TAG_S1").setCode("TAG_C1").setDisplay("TAG_D1");
p.getMeta().addTag().setSystem("TAG_S2").setCode("TAG_C2").setDisplay("TAG_D2");
String out = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(p);
ourLog.info(out);
//@formatter:off
assertThat(out, stringContainsInOrder(
"<meta>",
"<profile value=\"http://foo/profile1\"/>",
"<profile value=\"http://foo/profile2\"/>",
"<security>",
"<system value=\"SEC_S1\"/>",
"<code value=\"SEC_C1\"/>",
"<display value=\"SED_D1\"/>",
"</security>",
"<security>",
"<system value=\"SEC_S2\"/>",
"<code value=\"SEC_C2\"/>",
"<display value=\"SED_D2\"/>",
"</security>",
"<tag>",
"<system value=\"TAG_S1\"/>",
"<display value=\"TAG_D1\"/>",
"</tag>",
"<tag>",
"<system value=\"TAG_S2\"/>",
"<display value=\"TAG_D2\"/>",
"</tag>",
"</meta>"));
//@formatter:on
}
@Test
public void testEncodeWithCustomType() {
MyCustomPatient patient = new MyCustomPatient();
patient.addIdentifier().setSystem("urn:system").setValue("1234");
patient.addName().setFamily("Rossi").addGiven("Mario");
patient.setInsulinLevel(new Quantity());
patient.setGlucoseLevel(new Quantity());
patient.setHbA1c(new Quantity());
patient.setBloodPressure(new Quantity());
patient.setCholesterol(new Quantity());
patient.setWeight(new StringDt("80 kg"));
patient.setWeight(new StringDt("185 cm"));
patient.setCheckDates(new ArrayList<DateTimeDt>());
patient.getCheckDates().add(new DateTimeDt("2014-01-26T11:11:11"));
ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.ONLY_FOR_CUSTOM);
IParser p = ourCtx.newXmlParser().setPrettyPrint(true);
String messageString = p.encodeResourceToString(patient);
ourLog.info(messageString);
//@formatter:off
assertThat(messageString, stringContainsInOrder(
"<meta>",
"<profile value=\"http://example.com/foo\"/>",
"</meta>"));
//@formatter:on
//@formatter:off
assertThat(messageString, not(stringContainsInOrder(
"<meta>",
"<profile value=\"http://example.com/foo\"", "/>",
"<profile value=\"http://example.com/foo\"/>",
"</meta>")));
//@formatter:on
}
@Test
public void testEncodeWithCustomTypeAndAutoInsertedProfile() {
MyCustomPatient patient = new MyCustomPatient();
patient.getMeta().addProfile("http://example.com/foo");
patient.getMeta().addProfile("http://example.com/bar");
patient.addIdentifier().setSystem("urn:system").setValue("1234");
patient.addName().setFamily("Rossi").addGiven("Mario");
patient.setInsulinLevel(new Quantity());
patient.setGlucoseLevel(new Quantity());
patient.setHbA1c(new Quantity());
patient.setBloodPressure(new Quantity());
patient.setCholesterol(new Quantity());
patient.setWeight(new StringDt("80 kg"));
patient.setWeight(new StringDt("185 cm"));
patient.setCheckDates(new ArrayList<DateTimeDt>());
patient.getCheckDates().add(new DateTimeDt("2014-01-26T11:11:11"));
ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.ONLY_FOR_CUSTOM);
IParser p = ourCtx.newXmlParser().setPrettyPrint(true);
String messageString = p.encodeResourceToString(patient);
ourLog.info(messageString);
//@formatter:off
assertThat(messageString, stringContainsInOrder(
"<meta>",
"<profile value=\"http://example.com/foo\"/>",
"<profile value=\"http://example.com/bar\"/>",
"</meta>"));
//@formatter:on
//@formatter:off
assertThat(messageString, not(stringContainsInOrder(
"<meta>",
"<profile value=\"http://example.com/foo\"", "/>",
"<profile value=\"http://example.com/foo\"/>",
"</meta>")));
//@formatter:on
}
/**
* See #318
*/
@Test
public void testParseResourceWithContainedResourcesWithProfile() {
//@formatter:off
String input = "<MedicationRequest xmlns=\"http://hl7.org/fhir\">"
+ "<id value=\"44cfa24c-52e1-a8ff-8428-4e7ce1165460-local\"/> "
+ "<meta> "
+ "<profile value=\"http://fhir.something.com/StructureDefinition/our-medication-order\"/> "
+ "</meta> "
+ "<contained> "
+ "<Medication xmlns=\"http://hl7.org/fhir\"> "
+ "<id value=\"1\"/>"
+ "<meta> "
+ "<profile value=\"http://fhir.something.com/StructureDefinition/our-medication\"/> "
+ "</meta> "
+ "<code> "
+ "<text value=\"medication\"/> "
+ "</code> "
+ "</Medication> "
+ "</contained> "
+ "<medication> "
+ "<reference value=\"#1\"/> "
+ "</medication> "
+ "</MedicationRequest>";
//@formatter:on
FhirContext ctx = FhirContext.forR4();
ctx.setDefaultTypeForProfile("http://fhir.something.com/StructureDefinition/our-medication", MyMedication.class);
MedicationRequest mo = ctx.newXmlParser().parseResource(MedicationRequest.class, input);
assertEquals(MyMedication.class, mo.getContained().get(0).getClass());
}
public static String createBundle(String... theResources) {
StringBuilder b = new StringBuilder();
b.append("<Bundle xmlns=\"http://hl7.org/fhir\">\n");
for (String next : theResources) {
b.append(" <entry>\n");
b.append(" <resource>\n");
b.append(next);
b.append(" </resource>\n");
b.append(" </entry>\n");
}
b.append("</Bundle>");
return b.toString();
}
public static String createResource(boolean theWithProfile) {
StringBuilder b = new StringBuilder();
b.append("<Patient xmlns=\"http://hl7.org/fhir\">\n");
if (theWithProfile) {
b.append(" <meta>\n");
b.append(" <profile value=\"http://example.com/foo\"/>\n");
b.append(" </meta>\n");
}
b.append(" <extension url=\"http://example.com/BloodPressure\">\n");
b.append(" <valueQuantity>\n");
b.append(" <value value=\"110\"/>\n");
b.append(" <system value=\"http://unitsofmeasure.org\"/>\n");
b.append(" <code value=\"mmHg\"/>\n");
b.append(" </valueQuantity>\n");
b.append(" </extension>\n");
b.append(" <modifierExtension url=\"http://example.com/diabetes2\">\n");
b.append(" <valueDateTime value=\"2010-01-02\"/>\n");
b.append(" </modifierExtension>\n");
b.append(" <modifierExtension url=\"http://example.com/diabetes2\">\n");
b.append(" <valueDateTime value=\"2014-01-26T11:11:11\"/>\n");
b.append(" </modifierExtension>\n");
b.append(" <extension url=\"http://example.com/Cholesterol\">\n");
b.append(" <valueQuantity>\n");
b.append(" <value value=\"2\"/>\n");
b.append(" <system value=\"http://unitsofmeasure.org\"/>\n");
b.append(" <code value=\"mmol/l\"/>\n");
b.append(" </valueQuantity>\n");
b.append(" </extension>\n");
b.append(" <extension url=\"http://example.com/Glucose\">\n");
b.append(" <valueQuantity>\n");
b.append(" <value value=\"95\"/>\n");
b.append(" <system value=\"http://unitsofmeasure.org\"/>\n");
b.append(" <code value=\"mg/dl\"/>\n");
b.append(" </valueQuantity>\n");
b.append(" </extension>\n");
b.append(" <extension url=\"http://example.com/HbA1c\">\n");
b.append(" <valueQuantity>\n");
b.append(" <value value=\"48\"/>\n");
b.append(" <system value=\"http://unitsofmeasure.org\"/>\n");
b.append(" <code value=\"mmol/mol\"/>\n");
b.append(" </valueQuantity>\n");
b.append(" </extension>\n");
b.append(" <extension url=\"http://example.com/Insuline\">\n");
b.append(" <valueQuantity>\n");
b.append(" <value value=\"125\"/>\n");
b.append(" <system value=\"http://unitsofmeasure.org\"/>\n");
b.append(" <code value=\"pmol/l\"/>\n");
b.append(" </valueQuantity>\n");
b.append(" </extension>\n");
b.append(" <extension url=\"http://example.com/Weight\">\n");
b.append(" <valueString value=\"185 cm\"/>\n");
b.append(" </extension>\n");
b.append(" <identifier>\n");
b.append(" <system value=\"urn:system\"/>\n");
b.append(" <value value=\"1234\"/>\n");
b.append(" </identifier>\n");
b.append(" <name>\n");
b.append(" <family value=\"Rossi\"/>\n");
b.append(" <given value=\"Mario\"/>\n");
b.append(" </name>\n");
b.append("</Patient>");
String input = b.toString();
return input;
}
@ResourceDef(name = "Patient", profile = "http://example.com/foo")
public static class MyCustomPatient extends Patient {
private static final long serialVersionUID = 1L;
@Child(name = "bloodPressure") // once every 3 month. The average target is 130/80 mmHg or less
@Extension(url = "http://example.com/BloodPressure", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The value of the patient's blood pressure")
private Quantity myBloodPressure;
// Dates of periodic tests
@Child(name = "CheckDates", max = Child.MAX_UNLIMITED)
@Extension(url = "http://example.com/diabetes2", definedLocally = false, isModifier = true)
@Description(shortDefinition = "Dates of periodic tests")
private List<DateTimeDt> myCheckDates;
@Child(name = "cholesterol") // once a year. The target is triglycerides =< 2 mmol/l e cholesterol =< 4 mmol/l
@Extension(url = "http://example.com/Cholesterol", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The value of the patient's cholesterol")
private Quantity myCholesterol;
@Child(name = "glucoseLevel") // fingerprick test
@Extension(url = "http://example.com/Glucose", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The value of the patient's blood glucose")
private Quantity myGlucoseLevel;
// Periodic Tests
@Child(name = "hbA1c") // once every 6 month. The average target is 53 mmol/mol (or 7%) or less.
@Extension(url = "http://example.com/HbA1c", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The value of the patient's glucose")
private Quantity myHbA1c;
@Child(name = "Height")
@Extension(url = "http://example.com/Height", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The patient's height in cm")
private StringDt myHeight;
@Child(name = "insulinLevel") // Normal range is [43,208] pmol/l
@Extension(url = "http://example.com/Insuline", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The value of the patient's insulin")
private Quantity myInsulinLevel;
// Other parameters
@Child(name = "weight")
@Extension(url = "http://example.com/Weight", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The patient's weight in Kg")
private StringDt myWeight;
public Quantity Cholesterol() {
if (myCholesterol == null) {
myCholesterol = new Quantity();
}
myCholesterol.getValue();
myCholesterol.getSystem();
myCholesterol.getCode();
return myCholesterol;
}
public Quantity getBloodPressure() {
if (myBloodPressure == null) {
myBloodPressure = new Quantity();
}
myBloodPressure.getValue();
myBloodPressure.getSystem();
myBloodPressure.getCode();
return myBloodPressure;
}
public List<DateTimeDt> getCheckDates() {
if (myCheckDates == null) {
myCheckDates = new ArrayList<DateTimeDt>();
}
return myCheckDates;
}
public Quantity getGlucoseLevel() {
if (myGlucoseLevel == null) {
myGlucoseLevel = new Quantity();
}
myGlucoseLevel.getValue();
myGlucoseLevel.getSystem();
myGlucoseLevel.getCode();
return myGlucoseLevel;
}
public Quantity getHbA1c() {
if (myHbA1c == null) {
myHbA1c = new Quantity();
}
myHbA1c.getValue();
myHbA1c.getSystem();
myHbA1c.getCode();
return myHbA1c;
}
public StringDt getHeight() {
if (myHeight == null) {
myHeight = new StringDt();
}
return myHeight;
}
public Quantity getInsulinLevel() {
if (myInsulinLevel == null) {
myInsulinLevel = new Quantity();
}
myInsulinLevel.getValue();
myInsulinLevel.getSystem();
myInsulinLevel.getCode();
return myInsulinLevel;
}
public StringDt getWeight() {
if (myWeight == null) {
myWeight = new StringDt();
}
return myWeight;
}
@Override
public boolean isEmpty() {
return super.isEmpty() && ElementUtil.isEmpty(myInsulinLevel, myGlucoseLevel, myHbA1c, myBloodPressure, myCholesterol, myWeight, myHeight, myCheckDates);
}
public void setBloodPressure(Quantity bloodPressure) {
myBloodPressure = bloodPressure;
myBloodPressure.setValue(110);
myBloodPressure.setSystem("http://unitsofmeasure.org");
myBloodPressure.setCode("mmHg");
}
public void setCheckDates(List<DateTimeDt> theCheckDates) {
myCheckDates = theCheckDates;
myCheckDates.add(new DateTimeDt("2010-01-02"));
}
public void setCholesterol(Quantity cholesterol) {
myCholesterol = cholesterol;
myCholesterol.setValue(2);
myCholesterol.setSystem("http://unitsofmeasure.org");
myCholesterol.setCode("mmol/l");
}
public void setGlucoseLevel(Quantity glucoseLevel) {
myGlucoseLevel = glucoseLevel;
myGlucoseLevel.setValue(95);
myGlucoseLevel.setSystem("http://unitsofmeasure.org");
myGlucoseLevel.setCode("mg/dl");
}
public void setHbA1c(Quantity hba1c) {
myHbA1c = hba1c;
myHbA1c.setValue(48);
myHbA1c.setSystem("http://unitsofmeasure.org");
myHbA1c.setCode("mmol/mol");
}
public void setHeight(StringDt height) {
myHeight = height;
}
// Setter/Getter methods
public void setInsulinLevel(Quantity insulinLevel) {
myInsulinLevel = insulinLevel;
myInsulinLevel.setValue(125);
myInsulinLevel.setSystem("http://unitsofmeasure.org");
myInsulinLevel.setCode("pmol/l");
}
public void setWeight(StringDt weight) {
myWeight = weight;
}
}
@ResourceDef()
public static class MyMedication extends Medication {
private static final long serialVersionUID = 1L;
}
}

View File

@ -0,0 +1,171 @@
package ca.uhn.fhir.rest.client;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.io.StringReader;
import java.nio.charset.Charset;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.ReaderInputStream;
import org.apache.http.*;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicStatusLine;
import org.hl7.fhir.r4.model.Patient;
import org.junit.*;
import org.mockito.ArgumentCaptor;
import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
import ca.uhn.fhir.util.TestUtil;
/**
* http://gforge.hl7.org/gf/project/fhir/tracker/?action=TrackerItemEdit&tracker_id=677&tracker_item_id=10199
*/
public class ClientMimetypeR4Test {
private static FhirContext ourCtx;
private HttpClient myHttpClient;
private HttpResponse myHttpResponse;
@Before
public void before() {
myHttpClient = mock(HttpClient.class, new ReturnsDeepStubs());
ourCtx.getRestfulClientFactory().setHttpClient(myHttpClient);
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
myHttpResponse = mock(HttpResponse.class, new ReturnsDeepStubs());
}
@Test
public void testMimetypeXmlNew() throws Exception {
String requestCt = Constants.CT_FHIR_XML_NEW;
ArgumentCaptor<HttpUriRequest> capt = prepareMimetypePostTest(requestCt, true);
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
Patient pt = new Patient();
pt.getText().setDivAsString("A PATIENT");
MethodOutcome outcome = client.create().resource(pt).execute();
assertEquals("<div xmlns=\"http://www.w3.org/1999/xhtml\">FINAL VALUE</div>", ((Patient) outcome.getResource()).getText().getDivAsString());
assertEquals("http://example.com/fhir/Patient", capt.getAllValues().get(0).getURI().toASCIIString());
assertEquals(Constants.CT_FHIR_XML_NEW, capt.getAllValues().get(0).getFirstHeader("content-type").getValue().replaceAll(";.*", ""));
assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_NON_LEGACY, capt.getAllValues().get(0).getFirstHeader("accept").getValue());
assertEquals("<Patient xmlns=\"http://hl7.org/fhir\"><text><div xmlns=\"http://www.w3.org/1999/xhtml\">A PATIENT</div></text></Patient>", extractBodyAsString(capt));
}
@Test
public void testMimetypeXmlLegacy() throws Exception {
String requestCt = Constants.CT_FHIR_XML;
ArgumentCaptor<HttpUriRequest> capt = prepareMimetypePostTest(requestCt, true);
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
Patient pt = new Patient();
pt.getText().setDivAsString("A PATIENT");
MethodOutcome outcome = client.create().resource(pt).execute();
assertEquals("<div xmlns=\"http://www.w3.org/1999/xhtml\">FINAL VALUE</div>", ((Patient) outcome.getResource()).getText().getDivAsString());
assertEquals("http://example.com/fhir/Patient", capt.getAllValues().get(0).getURI().toASCIIString());
assertEquals(Constants.CT_FHIR_XML_NEW, capt.getAllValues().get(0).getFirstHeader("content-type").getValue().replaceAll(";.*", ""));
assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_NON_LEGACY, capt.getAllValues().get(0).getFirstHeader("accept").getValue());
assertEquals("<Patient xmlns=\"http://hl7.org/fhir\"><text><div xmlns=\"http://www.w3.org/1999/xhtml\">A PATIENT</div></text></Patient>", extractBodyAsString(capt));
}
@Test
public void testMimetypeJsonNew() throws Exception {
String requestCt = Constants.CT_FHIR_JSON_NEW;
ArgumentCaptor<HttpUriRequest> capt = prepareMimetypePostTest(requestCt, false);
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
Patient pt = new Patient();
pt.getText().setDivAsString("A PATIENT");
MethodOutcome outcome = client.create().resource(pt).encodedJson().execute();
assertEquals("<div xmlns=\"http://www.w3.org/1999/xhtml\">FINAL VALUE</div>", ((Patient) outcome.getResource()).getText().getDivAsString());
assertEquals("http://example.com/fhir/Patient", capt.getAllValues().get(0).getURI().toASCIIString());
assertEquals(Constants.CT_FHIR_JSON_NEW, capt.getAllValues().get(0).getFirstHeader("content-type").getValue().replaceAll(";.*", ""));
assertEquals(Constants.HEADER_ACCEPT_VALUE_JSON_NON_LEGACY, capt.getAllValues().get(0).getFirstHeader("accept").getValue());
assertEquals("{\"resourceType\":\"Patient\",\"text\":{\"div\":\"<div xmlns=\\\"http://www.w3.org/1999/xhtml\\\">A PATIENT</div>\"}}", extractBodyAsString(capt));
}
@Test
public void testMimetypeJsonLegacy() throws Exception {
String requestCt = Constants.CT_FHIR_JSON;
ArgumentCaptor<HttpUriRequest> capt = prepareMimetypePostTest(requestCt, false);
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
Patient pt = new Patient();
pt.getText().setDivAsString("A PATIENT");
MethodOutcome outcome = client.create().resource(pt).encodedJson().execute();
assertEquals("<div xmlns=\"http://www.w3.org/1999/xhtml\">FINAL VALUE</div>", ((Patient) outcome.getResource()).getText().getDivAsString());
assertEquals("http://example.com/fhir/Patient", capt.getAllValues().get(0).getURI().toASCIIString());
assertEquals(Constants.CT_FHIR_JSON_NEW, capt.getAllValues().get(0).getFirstHeader("content-type").getValue().replaceAll(";.*", ""));
assertEquals(Constants.HEADER_ACCEPT_VALUE_JSON_NON_LEGACY, capt.getAllValues().get(0).getFirstHeader("accept").getValue());
assertEquals("{\"resourceType\":\"Patient\",\"text\":{\"div\":\"<div xmlns=\\\"http://www.w3.org/1999/xhtml\\\">A PATIENT</div>\"}}", extractBodyAsString(capt));
}
private String extractBodyAsString(ArgumentCaptor<HttpUriRequest> capt) throws IOException {
String body = IOUtils.toString(((HttpEntityEnclosingRequestBase) capt.getAllValues().get(0)).getEntity().getContent(), "UTF-8");
return body;
}
private ArgumentCaptor<HttpUriRequest> prepareMimetypePostTest(String requestCt, boolean theXml) throws IOException, ClientProtocolException {
final IParser p = theXml ? ourCtx.newXmlParser() : ourCtx.newJsonParser();
final Patient resp1 = new Patient();
resp1.getText().setDivAsString("FINAL VALUE");
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getAllHeaders()).thenAnswer(new Answer<Header[]>() {
@Override
public Header[] answer(InvocationOnMock theInvocation) throws Throwable {
return new Header[] { new BasicHeader(Constants.HEADER_LOCATION, "http://foo.com/base/Patient/222/_history/3") };
}
});
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", requestCt + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(p.encodeResourceToString(resp1)), Charset.forName("UTF-8"));
}
});
return capt;
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@BeforeClass
public static void beforeClass() {
ourCtx = FhirContext.forR4();
}
}

View File

@ -0,0 +1,179 @@
package ca.uhn.fhir.rest.client;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.util.List;
import org.apache.commons.io.input.ReaderInputStream;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicStatusLine;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Organization;
import org.junit.*;
import org.mockito.ArgumentCaptor;
import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
import ca.uhn.fhir.rest.param.DateParam;
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
import ca.uhn.fhir.util.TestUtil;
public class ClientWithCustomTypeR4Test {
private static FhirContext ourCtx;
private HttpClient myHttpClient;
private HttpResponse myHttpResponse;
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@Before
public void before() {
myHttpClient = mock(HttpClient.class, new ReturnsDeepStubs());
ourCtx.getRestfulClientFactory().setHttpClient(myHttpClient);
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
myHttpResponse = mock(HttpResponse.class, new ReturnsDeepStubs());
}
@Test
public void testReadCustomType() throws Exception {
IParser p = ourCtx.newXmlParser();
MyPatientWithExtensions response = new MyPatientWithExtensions();
response.addName().setFamily("FAMILY");
response.getStringExt().setValue("STRINGVAL");
response.getDateExt().setValueAsString("2011-01-02");
final String respString = p.encodeResourceToString(response);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
//@formatter:off
MyPatientWithExtensions value = client
.read()
.resource(MyPatientWithExtensions.class)
.withId("123")
.execute();
//@formatter:on
HttpUriRequest request = capt.getAllValues().get(0);
assertEquals("http://example.com/fhir/Patient/123", request.getURI().toASCIIString());
assertEquals("GET", request.getMethod());
assertEquals(1, value.getName().size());
assertEquals("FAMILY", value.getName().get(0).getFamily());
assertEquals("STRINGVAL", value.getStringExt().getValue());
assertEquals("2011-01-02", value.getDateExt().getValueAsString());
}
@Test
public void testSearchWithGenericReturnType() throws Exception {
final Bundle bundle = new Bundle();
final ExtendedPatient patient = new ExtendedPatient();
patient.addIdentifier().setValue("PRP1660");
bundle.addEntry().setResource(patient);
final Organization org = new Organization();
org.setName("FOO");
patient.getManagingOrganization().setResource(org);
final FhirContext ctx = FhirContext.forR4();
ctx.setDefaultTypeForProfile(ExtendedPatient.HTTP_FOO_PROFILES_PROFILE, ExtendedPatient.class);
ctx.getRestfulClientFactory().setHttpClient(myHttpClient);
ctx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
String msg = ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(bundle);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
// httpResponse = new BasicHttpResponse(statusline, catalog, locale)
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
ITestClient client = ctx.newRestfulClient(ITestClient.class, "http://foo");
List<IBaseResource> response = client.getPatientByDobWithGenericResourceReturnType(new DateParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, "2011-01-02"));
assertEquals("http://foo/Patient?birthdate=ge2011-01-02", capt.getValue().getURI().toString());
ExtendedPatient patientResp = (ExtendedPatient) response.get(0);
assertEquals("PRP1660", patientResp.getIdentifier().get(0).getValue());
}
@Test
public void testSearchWithGenericReturnType2() throws Exception {
final Bundle bundle = new Bundle();
final ExtendedPatient patient = new ExtendedPatient();
patient.addIdentifier().setValue("PRP1660");
bundle.addEntry().setResource(patient);
final Organization org = new Organization();
org.setName("FOO");
patient.getManagingOrganization().setResource(org);
final FhirContext ctx = FhirContext.forR4();
ctx.setDefaultTypeForProfile(ExtendedPatient.HTTP_FOO_PROFILES_PROFILE, ExtendedPatient.class);
ctx.getRestfulClientFactory().setHttpClient(myHttpClient);
ctx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
String msg = ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(bundle);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
// httpResponse = new BasicHttpResponse(statusline, catalog, locale)
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
ITestClient client = ctx.newRestfulClient(ITestClient.class, "http://foo");
List<IAnyResource> response = client.getPatientByDobWithGenericResourceReturnType2(new DateParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, "2011-01-02"));
assertEquals("http://foo/Patient?birthdate=ge2011-01-02", capt.getValue().getURI().toString());
ExtendedPatient patientResp = (ExtendedPatient) response.get(0);
assertEquals("PRP1660", patientResp.getIdentifier().get(0).getValue());
}
@BeforeClass
public static void beforeClass() {
ourCtx = FhirContext.forR4();
}
}

View File

@ -0,0 +1,38 @@
package ca.uhn.fhir.rest.client;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.StringType;
import ca.uhn.fhir.model.api.annotation.*;
@ResourceDef(name = "Patient", profile = ExtendedPatient.HTTP_FOO_PROFILES_PROFILE)
public class ExtendedPatient extends Patient {
static final String HTTP_FOO_PROFILES_PROFILE = "http://foo/profiles/Profile";
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* Each extension is defined in a field. Any valid HAPI Data Type
* can be used for the field type. Note that the [name=""] attribute
* in the @Child annotation needs to match the name for the bean accessor
* and mutator methods.
*/
@Child(name = "petName")
@Extension(url = "http://example.com/dontuse#petname", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The name of the patient's favourite pet")
private StringType myPetName;
public StringType getPetName() {
if (myPetName == null) {
myPetName = new StringType();
}
return myPetName;
}
public void setPetName(StringType thePetName) {
myPetName = thePetName;
}
}

View File

@ -0,0 +1,22 @@
package ca.uhn.fhir.rest.client;
import java.util.List;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.Patient;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.client.api.IBasicClient;
import ca.uhn.fhir.rest.param.DateParam;
public interface ITestClient extends IBasicClient {
@Search(type=ExtendedPatient.class)
public List<IBaseResource> getPatientByDobWithGenericResourceReturnType(@RequiredParam(name=Patient.SP_BIRTHDATE) DateParam theBirthDate);
@Search(type=ExtendedPatient.class)
public List<IAnyResource> getPatientByDobWithGenericResourceReturnType2(@RequiredParam(name=Patient.SP_BIRTHDATE) DateParam theBirthDate);
}

View File

@ -0,0 +1,86 @@
package ca.uhn.fhir.rest.client;
import java.util.ArrayList;
import java.util.List;
import org.hl7.fhir.r4.model.*;
import ca.uhn.fhir.model.api.annotation.*;
import ca.uhn.fhir.model.api.annotation.Extension;
import ca.uhn.fhir.util.ElementUtil;
@ResourceDef(name="Patient", profile="http://example.com/StructureDefinition/patient_with_extensions")
public class MyPatientWithExtensions extends DomainResource {
private static final long serialVersionUID = 1L;
@Extension(url = "http://example.com/ext/date", definedLocally = false, isModifier = true)
@Child(name = "modExt")
private DateType myDateExt;
@Extension(url = "http://example.com/ext/string", definedLocally = false, isModifier = false)
@Child(name = "extAtt")
private StringType myStringExt;
@Child(name = "name", type = {HumanName.class}, min=0, max=Child.MAX_UNLIMITED, modifier=false, summary=true)
@Description(shortDefinition="A name associated with the patient", formalDefinition="A name associated with the individual." )
private List<HumanName> myName;
public List<HumanName> getName() {
if (myName == null) {
myName = new ArrayList<HumanName>();
}
return myName;
}
public void setName(List<HumanName> theName) {
myName = theName;
}
public DateType getDateExt() {
if (myDateExt == null) {
myDateExt = new DateType();
}
return myDateExt;
}
public StringType getStringExt() {
if (myStringExt == null) {
myStringExt = new StringType();
}
return myStringExt;
}
@Override
public boolean isEmpty() {
return super.isEmpty() && ElementUtil.isEmpty(myStringExt, myDateExt);
}
public void setDateExt(DateType theDateExt) {
myDateExt = theDateExt;
}
public void setStringExt(StringType theStringExt) {
myStringExt = theStringExt;
}
@Override
public DomainResource copy() {
return null;
}
@Override
public ResourceType getResourceType() {
return ResourceType.Patient;
}
public HumanName addName() {
HumanName retVal = new HumanName();
getName().add(retVal);
return retVal;
}
}

View File

@ -0,0 +1,186 @@
package ca.uhn.fhir.rest.client;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.util.List;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.ReaderInputStream;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicStatusLine;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.*;
import org.junit.*;
import org.mockito.ArgumentCaptor;
import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.annotation.*;
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.api.IRestfulClient;
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
import ca.uhn.fhir.rest.client.impl.BaseClient;
import ca.uhn.fhir.util.TestUtil;
public class NonGenericClientR4Test {
private static FhirContext ourCtx;
private HttpClient myHttpClient;
private HttpResponse myHttpResponse;
@Before
public void before() {
myHttpClient = mock(HttpClient.class, new ReturnsDeepStubs());
ourCtx.getRestfulClientFactory().setHttpClient(myHttpClient);
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
myHttpResponse = mock(HttpResponse.class, new ReturnsDeepStubs());
System.setProperty(BaseClient.HAPI_CLIENT_KEEPRESPONSES, "true");
}
private String extractBodyAsString(ArgumentCaptor<HttpUriRequest> capt, int theIdx) throws IOException {
String body = IOUtils.toString(((HttpEntityEnclosingRequestBase) capt.getAllValues().get(theIdx)).getEntity().getContent(), "UTF-8");
return body;
}
@Test
public void testValidateCustomTypeFromClientSearch() throws Exception {
IParser p = ourCtx.newXmlParser();
Bundle b = new Bundle();
MyPatientWithExtensions patient = new MyPatientWithExtensions();
patient.setId("123");
patient.getText().setDivAsString("OK!");
b.addEntry().setResource(patient);
final String respString = p.encodeResourceToString(b);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
IClientWithCustomType client = ourCtx.newRestfulClient(IClientWithCustomType.class, "http://example.com/fhir");
List<MyPatientWithExtensions> list = client.search();
assertEquals(1, list.size());
assertEquals(MyPatientWithExtensions.class, list.get(0).getClass());
}
@Test
public void testValidateCustomTypeFromClientRead() throws Exception {
IParser p = ourCtx.newXmlParser();
MyPatientWithExtensions patient = new MyPatientWithExtensions();
patient.setId("123");
patient.getText().setDivAsString("OK!");
final String respString = p.encodeResourceToString(patient);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
IClientWithCustomType client = ourCtx.newRestfulClient(IClientWithCustomType.class, "http://example.com/fhir");
MyPatientWithExtensions read = client.read(new IdType("1"));
assertEquals("<div xmlns=\"http://www.w3.org/1999/xhtml\">OK!</div>", read.getText().getDivAsString());
}
@Test
public void testValidateResourceOnly() throws Exception {
IParser p = ourCtx.newXmlParser();
OperationOutcome conf = new OperationOutcome();
conf.getText().setDivAsString("OK!");
final String respString = p.encodeResourceToString(conf);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
IClient client = ourCtx.newRestfulClient(IClient.class, "http://example.com/fhir");
Patient patient = new Patient();
patient.addName().setFamily("FAM");
int idx = 0;
MethodOutcome outcome = client.validate(patient, null, null);
String resp = ourCtx.newXmlParser().encodeResourceToString(outcome.getOperationOutcome());
assertEquals("<OperationOutcome xmlns=\"http://hl7.org/fhir\"><text><div xmlns=\"http://www.w3.org/1999/xhtml\">OK!</div></text></OperationOutcome>", resp);
assertEquals("http://example.com/fhir/$validate", capt.getAllValues().get(idx).getURI().toString());
String request = extractBodyAsString(capt,idx);
assertEquals("<Parameters xmlns=\"http://hl7.org/fhir\"><parameter><name value=\"resource\"/><resource><Patient xmlns=\"http://hl7.org/fhir\"><name><family value=\"FAM\"/></name></Patient></resource></parameter></Parameters>", request);
idx = 1;
outcome = client.validate(patient, ValidationModeEnum.CREATE, "http://foo");
resp = ourCtx.newXmlParser().encodeResourceToString(outcome.getOperationOutcome());
assertEquals("<OperationOutcome xmlns=\"http://hl7.org/fhir\"><text><div xmlns=\"http://www.w3.org/1999/xhtml\">OK!</div></text></OperationOutcome>", resp);
assertEquals("http://example.com/fhir/$validate", capt.getAllValues().get(idx).getURI().toString());
request = extractBodyAsString(capt,idx);
assertEquals("<Parameters xmlns=\"http://hl7.org/fhir\"><parameter><name value=\"resource\"/><resource><Patient xmlns=\"http://hl7.org/fhir\"><name><family value=\"FAM\"/></name></Patient></resource></parameter><parameter><name value=\"mode\"/><valueString value=\"create\"/></parameter><parameter><name value=\"profile\"/><valueString value=\"http://foo\"/></parameter></Parameters>", request);
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@BeforeClass
public static void beforeClass() {
ourCtx = FhirContext.forR4();
}
private interface IClient extends IRestfulClient {
@Validate
MethodOutcome validate(@ResourceParam IBaseResource theResource, @Validate.Mode ValidationModeEnum theMode, @Validate.Profile String theProfile);
}
private interface IClientWithCustomType extends IRestfulClient {
@Search
List<MyPatientWithExtensions> search();
@Read
MyPatientWithExtensions read(@IdParam IdType theId);
}
}

View File

@ -0,0 +1,188 @@
package ca.uhn.fhir.rest.client;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.InputStream;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.util.List;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.ReaderInputStream;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.*;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicStatusLine;
import org.hl7.fhir.r4.model.Parameters;
import org.hl7.fhir.r4.model.StringType;
import org.junit.*;
import org.mockito.ArgumentCaptor;
import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import com.google.common.base.Charsets;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.client.api.*;
import ca.uhn.fhir.rest.param.*;
import ca.uhn.fhir.util.TestUtil;
public class OperationClientR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationClientR4Test.class);
private FhirContext ourCtx;
private HttpClient ourHttpClient;
private HttpResponse ourHttpResponse;
private IOpClient ourAnnClient;
private ArgumentCaptor<HttpUriRequest> capt;
private IGenericClient ourGenClient;
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@Before
public void before() throws Exception {
ourCtx = FhirContext.forR4();
ourHttpClient = mock(HttpClient.class, new ReturnsDeepStubs());
ourCtx.getRestfulClientFactory().setHttpClient(ourHttpClient);
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
ourHttpResponse = mock(HttpResponse.class, new ReturnsDeepStubs());
Parameters outParams = new Parameters();
outParams.addParameter().setName("FOO");
final String retVal = ourCtx.newXmlParser().encodeResourceToString(outParams);
capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(ourHttpClient.execute(capt.capture())).thenReturn(ourHttpResponse);
when(ourHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(ourHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(ourHttpResponse.getEntity().getContent()).thenAnswer(new Answer<InputStream>() {
@Override
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(retVal), Charset.forName("UTF-8"));
}
});
ourAnnClient = ourCtx.newRestfulClient(IOpClient.class, "http://foo");
ourGenClient = ourCtx.newRestfulGenericClient("http://foo");
}
@Test
public void testNonRepeatingGenericUsingParameters() throws Exception {
ourGenClient
.operation()
.onServer()
.named("nonrepeating")
.withSearchParameter(Parameters.class, "valstr", new StringParam("str"))
.andSearchParameter("valtok", new TokenParam("sys2", "val2"))
.execute();
Parameters response = ourAnnClient.nonrepeating(new StringParam("str"), new TokenParam("sys", "val"));
assertEquals("FOO", response.getParameter().get(0).getName());
HttpPost value = (HttpPost) capt.getAllValues().get(0);
String requestBody = IOUtils.toString(((HttpPost) value).getEntity().getContent(), Charsets.UTF_8);
IOUtils.closeQuietly(((HttpPost) value).getEntity().getContent());
ourLog.info(requestBody);
Parameters request = ourCtx.newXmlParser().parseResource(Parameters.class, requestBody);
assertEquals("http://foo/$nonrepeating", value.getURI().toASCIIString());
assertEquals(2, request.getParameter().size());
assertEquals("valstr", request.getParameter().get(0).getName());
assertEquals("str", ((StringType) request.getParameter().get(0).getValue()).getValue());
assertEquals("valtok", request.getParameter().get(1).getName());
assertEquals("sys2|val2", ((StringType) request.getParameter().get(1).getValue()).getValue());
}
@Test
public void testNonRepeatingGenericUsingUrl() throws Exception {
ourGenClient
.operation()
.onServer()
.named("nonrepeating")
.withSearchParameter(Parameters.class, "valstr", new StringParam("str"))
.andSearchParameter("valtok", new TokenParam("sys2", "val2"))
.useHttpGet()
.execute();
Parameters response = ourAnnClient.nonrepeating(new StringParam("str"), new TokenParam("sys", "val"));
assertEquals("FOO", response.getParameter().get(0).getName());
HttpGet value = (HttpGet) capt.getAllValues().get(0);
assertEquals("http://foo/$nonrepeating?valstr=str&valtok=sys2%7Cval2", value.getURI().toASCIIString());
}
@Test
public void testNonRepeatingUsingParameters() throws Exception {
Parameters response = ourAnnClient.nonrepeating(new StringParam("str"), new TokenParam("sys", "val"));
assertEquals("FOO", response.getParameter().get(0).getName());
HttpPost value = (HttpPost) capt.getAllValues().get(0);
String requestBody = IOUtils.toString(((HttpPost) value).getEntity().getContent(), Charsets.UTF_8);
IOUtils.closeQuietly(((HttpPost) value).getEntity().getContent());
ourLog.info(requestBody);
Parameters request = ourCtx.newXmlParser().parseResource(Parameters.class, requestBody);
assertEquals("http://foo/$nonrepeating", value.getURI().toASCIIString());
assertEquals(2, request.getParameter().size());
assertEquals("valstr", request.getParameter().get(0).getName());
assertEquals("str", ((StringType) request.getParameter().get(0).getValue()).getValue());
assertEquals("valtok", request.getParameter().get(1).getName());
assertEquals("sys|val", ((StringType) request.getParameter().get(1).getValue()).getValue());
}
public interface IOpClient extends IBasicClient {
@Operation(name = "$andlist", idempotent = true)
public Parameters andlist(
//@formatter:off
@OperationParam(name="valstr", max=10) StringAndListParam theValStr,
@OperationParam(name="valtok", max=10) TokenAndListParam theValTok
//@formatter:on
);
@Operation(name = "$andlist-withnomax", idempotent = true)
public Parameters andlistWithNoMax(
//@formatter:off
@OperationParam(name="valstr") StringAndListParam theValStr,
@OperationParam(name="valtok") TokenAndListParam theValTok
//@formatter:on
);
@Operation(name = "$nonrepeating", idempotent = true)
public Parameters nonrepeating(
//@formatter:off
@OperationParam(name="valstr") StringParam theValStr,
@OperationParam(name="valtok") TokenParam theValTok
//@formatter:on
);
@Operation(name = "$orlist", idempotent = true)
public Parameters orlist(
//@formatter:off
@OperationParam(name="valstr", max=10) List<StringOrListParam> theValStr,
@OperationParam(name="valtok", max=10) List<TokenOrListParam> theValTok
//@formatter:on
);
@Operation(name = "$orlist-withnomax", idempotent = true)
public Parameters orlistWithNoMax(
//@formatter:off
@OperationParam(name="valstr") List<StringOrListParam> theValStr,
@OperationParam(name="valtok") List<TokenOrListParam> theValTok
//@formatter:on
);
}
}

View File

@ -0,0 +1,131 @@
package ca.uhn.fhir.rest.client;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.io.StringReader;
import java.nio.charset.Charset;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.ReaderInputStream;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicStatusLine;
import org.hl7.fhir.r4.model.*;
import org.junit.*;
import org.mockito.ArgumentCaptor;
import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.PatchTypeEnum;
import ca.uhn.fhir.rest.client.api.*;
import ca.uhn.fhir.util.TestUtil;
public class PatchClientR4Test {
public interface IClientType extends IRestfulClient {
@Patch(type=Patient.class)
MethodOutcome patch(@IdParam IdType theId, @ResourceParam String theBody, PatchTypeEnum thePatchType);
}
private static FhirContext ourCtx;
private HttpClient myHttpClient;
private HttpResponse myHttpResponse;
@Before
public void before() {
myHttpClient = mock(HttpClient.class, new ReturnsDeepStubs());
ourCtx.getRestfulClientFactory().setHttpClient(myHttpClient);
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
myHttpResponse = mock(HttpResponse.class, new ReturnsDeepStubs());
}
@Test
public void testJsonPatchAnnotation() throws Exception {
ArgumentCaptor<HttpUriRequest> capt = prepareResponse();
IClientType client = ourCtx.newRestfulClient(IClientType.class, "http://example.com/fhir");
Patient pt = new Patient();
pt.getText().setDivAsString("A PATIENT");
MethodOutcome outcome = client.patch(new IdType("Patient/123"), "{}", PatchTypeEnum.JSON_PATCH);
assertEquals("PATCH", capt.getAllValues().get(0).getMethod());
assertEquals("http://example.com/fhir/Patient/123", capt.getAllValues().get(0).getURI().toASCIIString());
assertEquals(Constants.CT_JSON_PATCH, capt.getAllValues().get(0).getFirstHeader("content-type").getValue().replaceAll(";.*", ""));
assertEquals("{}", extractBodyAsString(capt));
assertEquals("<div xmlns=\"http://www.w3.org/1999/xhtml\">OK</div>", ((OperationOutcome) outcome.getOperationOutcome()).getText().getDivAsString());
}
@Test
@Ignore
public void testJsonPatchFluent() throws Exception {
ArgumentCaptor<HttpUriRequest> capt = prepareResponse();
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
Patient pt = new Patient();
pt.getText().setDivAsString("A PATIENT");
// MethodOutcome outcome = client.patch().resource("").
// patch(new IdType("Patient/123"), "{}", PatchTypeEnum.JSON_PATCH);
// assertEquals("PATCH", capt.getAllValues().get(0).getMethod());
// assertEquals("http://example.com/fhir/Patient/123", capt.getAllValues().get(0).getURI().toASCIIString());
// assertEquals(Constants.CT_JSON_PATCH, capt.getAllValues().get(0).getFirstHeader("content-type").getValue().replaceAll(";.*", ""));
// assertEquals("{}", extractBodyAsString(capt));
// assertEquals("<div xmlns=\"http://www.w3.org/1999/xhtml\">OK</div>", ((OperationOutcome) outcome.getOperationOutcome()).getText().getDivAsString());
}
private String extractBodyAsString(ArgumentCaptor<HttpUriRequest> capt) throws IOException {
String body = IOUtils.toString(((HttpEntityEnclosingRequestBase) capt.getAllValues().get(0)).getEntity().getContent(), "UTF-8");
return body;
}
private ArgumentCaptor<HttpUriRequest> prepareResponse() throws IOException, ClientProtocolException {
final IParser p = ourCtx.newXmlParser();
final OperationOutcome resp1 = new OperationOutcome();
resp1.getText().setDivAsString("OK");
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML_NEW + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(p.encodeResourceToString(resp1)), Charset.forName("UTF-8"));
}
});
return capt;
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@BeforeClass
public static void beforeClass() {
ourCtx = FhirContext.forR4();
}
}

View File

@ -0,0 +1,234 @@
package ca.uhn.fhir.rest.client;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.InputStream;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.util.*;
import org.apache.commons.io.input.ReaderInputStream;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicStatusLine;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
import org.junit.*;
import org.mockito.ArgumentCaptor;
import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.annotation.Count;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.SortOrderEnum;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.client.api.IRestfulClient;
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.util.TestUtil;
import ca.uhn.fhir.util.UrlUtil;
public class SearchClientR4Test {
private FhirContext ourCtx;
private HttpClient ourHttpClient;
private HttpResponse ourHttpResponse;
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@Before
public void before() {
ourCtx = FhirContext.forR4();
ourHttpClient = mock(HttpClient.class, new ReturnsDeepStubs());
ourCtx.getRestfulClientFactory().setHttpClient(ourHttpClient);
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
ourHttpResponse = mock(HttpResponse.class, new ReturnsDeepStubs());
}
/**
* See #299
*/
@Test
public void testListResponseWithSearchExtension() throws Exception {
final String response = createBundleWithSearchExtension();
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(ourHttpClient.execute(capt.capture())).thenReturn(ourHttpResponse);
when(ourHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(ourHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(ourHttpResponse.getEntity().getContent()).thenAnswer(new Answer<InputStream>() {
@Override
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(response), Charset.forName("UTF-8"));
}
});
ILocationClient client = ourCtx.newRestfulClient(ILocationClient.class, "http://localhost:8081/hapi-fhir/fhir");
List<Location> matches = client.getMatches(new StringParam("smith"), 100);
assertEquals(1, matches.size());
assertEquals("Sample Clinic", matches.get(0).getName());
HttpGet value = (HttpGet) capt.getValue();
assertEquals("http://localhost:8081/hapi-fhir/fhir/Location?_query=match&name=smith&_count=100", value.getURI().toString());
}
/**
* See #371
*/
@Test
public void testSortForDstu3() throws Exception {
final String response = createBundleWithSearchExtension();
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(ourHttpClient.execute(capt.capture())).thenReturn(ourHttpResponse);
when(ourHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(ourHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(ourHttpResponse.getEntity().getContent()).thenAnswer(new Answer<InputStream>() {
@Override
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(response), Charset.forName("UTF-8"));
}
});
ILocationClient client = ourCtx.newRestfulClient(ILocationClient.class, "http://localhost/fhir");
int idx = 0;
client.search(new SortSpec("param1", SortOrderEnum.ASC));
assertEquals("http://localhost/fhir/Bundle?_sort=param1", ((HttpGet) capt.getAllValues().get(idx++)).getURI().toString());
client.search(new SortSpec("param1", SortOrderEnum.ASC).setChain(new SortSpec("param2", SortOrderEnum.DESC)));
assertEquals("http://localhost/fhir/Bundle?_sort=param1%2C-param2", ((HttpGet) capt.getAllValues().get(idx++)).getURI().toString());
}
@Test
public void testSearchWithPrimitiveTypes() throws Exception {
TimeZone tz = TimeZone.getDefault();
try {
TimeZone.setDefault(TimeZone.getTimeZone("America/Toronto"));
Date date = new Date(23898235986L);
Calendar cal = new GregorianCalendar();
cal.setTime(date);
;
final String response = createBundleWithSearchExtension();
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(ourHttpClient.execute(capt.capture())).thenReturn(ourHttpResponse);
when(ourHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(ourHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(ourHttpResponse.getEntity().getContent()).thenAnswer(new Answer<InputStream>() {
@Override
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(response), Charset.forName("UTF-8"));
}
});
ILocationClient client = ourCtx.newRestfulClient(ILocationClient.class, "http://localhost/fhir");
int idx = 0;
client.search("STRING1", new StringType("STRING2"), date, cal);
assertEquals("http://localhost/fhir/Bundle?stringParam=STRING1&stringTypeParam=STRING2&dateParam=1970-10-04T10:23:55.986-04:00&calParam=1970-10-04T10:23:55.986-04:00",
UrlUtil.unescape(((HttpGet) capt.getAllValues().get(idx++)).getURI().toString()));
client.search(null, null, null, null);
assertEquals("http://localhost/fhir/Bundle",
UrlUtil.unescape(((HttpGet) capt.getAllValues().get(idx++)).getURI().toString()));
} finally {
TimeZone.setDefault(tz);
}
}
/**
* See #299
*/
@Test
public void testBundleResponseWithSearchExtension() throws Exception {
final String response = createBundleWithSearchExtension();
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(ourHttpClient.execute(capt.capture())).thenReturn(ourHttpResponse);
when(ourHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(ourHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(ourHttpResponse.getEntity().getContent()).thenAnswer(new Answer<InputStream>() {
@Override
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(response), Charset.forName("UTF-8"));
}
});
ILocationClient client = ourCtx.newRestfulClient(ILocationClient.class, "http://localhost:8081/hapi-fhir/fhir");
Bundle matches = client.getMatchesReturnBundle(new StringParam("smith"), 100);
assertEquals(1, matches.getEntry().size());
BundleEntryComponent entry = matches.getEntry().get(0);
assertEquals("Sample Clinic", ((Location) entry.getResource()).getName());
List<Extension> ext = entry.getSearch().getExtensionsByUrl("http://hl7.org/fhir/StructureDefinition/algorithmic-match");
assertEquals(1, ext.size());
HttpGet value = (HttpGet) capt.getValue();
assertEquals("http://localhost:8081/hapi-fhir/fhir/Location?_query=match&name=smith&_count=100", value.getURI().toString());
}
private String createBundleWithSearchExtension() {
//@formatter:off
final String response = "<Bundle xmlns=\"http://hl7.org/fhir\">"
+ "<id value=\"f61f6ddc-95e8-4ef9-a4cd-17c79bbb74f3\"></id>"
+ "<meta><lastUpdated value=\"2016-02-19T12:04:02.616-05:00\"></lastUpdated></meta>"
+ "<type value=\"searchset\"></type>"
+ "<link><relation value=\"self\"></relation><url value=\"http://localhost:8081/hapi-fhir/fhir/Location?name=Sample+Clinic&amp;_query=match\"></url></link>"
+ "<entry>"
+ "<resource>"
+ "<Location xmlns=\"http://hl7.org/fhir\">"
+ "<id value=\"1\"></id>"
+ "<name value=\"Sample Clinic\"></name>"
+ "</Location>"
+ "</resource>"
+ "<search>"
+ "<extension url=\"http://hl7.org/fhir/StructureDefinition/algorithmic-match\">"
+ "<valueCode value=\"probable\"></valueCode>"
+ "</extension>"
+ "<score value=\"0.8000000000000000444089209850062616169452667236328125\">"
+ "</score>"
+ "</search>"
+ "</entry>"
+ "</Bundle>";
//@formatter:on
return response;
}
public interface ILocationClient extends IRestfulClient {
@Search(queryName = "match")
public List<Location> getMatches(final @RequiredParam(name = Location.SP_NAME) StringParam name, final @Count Integer count);
@Search(queryName = "match", type = Location.class)
public Bundle getMatchesReturnBundle(final @RequiredParam(name = Location.SP_NAME) StringParam name, final @Count Integer count);
@Search
public Bundle search(@Sort SortSpec theSort);
@Search
public Bundle search(@OptionalParam(name = "stringParam") String theString, @OptionalParam(name = "stringTypeParam") StringType theStringDt, @OptionalParam(name = "dateParam") Date theDate,
@OptionalParam(name = "calParam") Calendar theCal);
}
}

View File

@ -0,0 +1,168 @@
package ca.uhn.fhir.rest.server;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.*;
import org.junit.*;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.annotation.Create;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
public class CreateBinaryR4Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forR4();
private static Binary ourLastBinary;
private static byte[] ourLastBinaryBytes;
private static String ourLastBinaryString;
private static int ourPort;
private static Server ourServer;
@Before
public void before() {
ourLastBinary = null;
ourLastBinaryBytes = null;
ourLastBinaryString = null;
}
@Test
public void testRawBytesBinaryContentType() throws Exception {
HttpPost post = new HttpPost("http://localhost:" + ourPort + "/Binary");
post.setEntity(new ByteArrayEntity(new byte[] { 0, 1, 2, 3, 4 }));
post.addHeader("Content-Type", "application/foo");
CloseableHttpResponse status = ourClient.execute(post);
try {
assertEquals("application/foo", ourLastBinary.getContentType());
assertArrayEquals(new byte[] { 0, 1, 2, 3, 4 }, ourLastBinary.getContent());
assertArrayEquals(new byte[] { 0, 1, 2, 3, 4 }, ourLastBinaryBytes);
} finally {
IOUtils.closeQuietly(status);
}
}
/**
* Technically the client shouldn't be doing it this way, but we'll be accepting
*/
@Test
public void testRawBytesFhirContentType() throws Exception {
Binary b = new Binary();
b.setContentType("application/foo");
b.setContent(new byte[] { 0, 1, 2, 3, 4 });
String encoded = ourCtx.newJsonParser().encodeResourceToString(b);
HttpPost post = new HttpPost("http://localhost:" + ourPort + "/Binary");
post.setEntity(new StringEntity(encoded));
post.addHeader("Content-Type", Constants.CT_FHIR_JSON);
CloseableHttpResponse status = ourClient.execute(post);
try {
assertEquals("application/foo", ourLastBinary.getContentType());
assertArrayEquals(new byte[] { 0, 1, 2, 3, 4 }, ourLastBinary.getContent());
} finally {
IOUtils.closeQuietly(status);
}
}
@Test
public void testRawBytesFhirContentTypeContainingFhir() throws Exception {
Patient p = new Patient();
p.getText().setDivAsString("A PATIENT");
Binary b = new Binary();
b.setContentType("application/xml+fhir");
b.setContent(ourCtx.newXmlParser().encodeResourceToString(p).getBytes("UTF-8"));
String encoded = ourCtx.newJsonParser().encodeResourceToString(b);
HttpPost post = new HttpPost("http://localhost:" + ourPort + "/Binary");
post.setEntity(new StringEntity(encoded));
post.addHeader("Content-Type", Constants.CT_FHIR_JSON);
CloseableHttpResponse status = ourClient.execute(post);
try {
assertEquals("application/xml+fhir", ourLastBinary.getContentType());
assertArrayEquals(b.getContent(), ourLastBinary.getContent());
assertEquals(encoded, ourLastBinaryString);
assertArrayEquals(encoded.getBytes("UTF-8"), ourLastBinaryBytes);
} finally {
IOUtils.closeQuietly(status);
}
}
@Test
public void testRawBytesNoContentType() throws Exception {
HttpPost post = new HttpPost("http://localhost:" + ourPort + "/Binary");
post.setEntity(new ByteArrayEntity(new byte[] { 0, 1, 2, 3, 4 }));
CloseableHttpResponse status = ourClient.execute(post);
try {
assertNull(ourLastBinary.getContentType());
assertArrayEquals(new byte[] { 0, 1, 2, 3, 4 }, ourLastBinary.getContent());
} finally {
IOUtils.closeQuietly(status);
}
}
@AfterClass
public static void afterClassClearContext() throws Exception {
ourServer.stop();
TestUtil.clearAllStaticFieldsForUnitTest();
}
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
BinaryProvider binaryProvider = new BinaryProvider();
ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer(ourCtx);
servlet.setResourceProviders(binaryProvider);
ServletHolder servletHolder = new ServletHolder(servlet);
proxyHandler.addServletWithMapping(servletHolder, "/*");
ourServer.setHandler(proxyHandler);
ourServer.start();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourClient = builder.build();
}
public static class BinaryProvider implements IResourceProvider {
@Create()
public MethodOutcome createBinary(@ResourceParam Binary theBinary, @ResourceParam String theBinaryString, @ResourceParam byte[] theBinaryBytes) {
ourLastBinary = theBinary;
ourLastBinaryString = theBinaryString;
ourLastBinaryBytes = theBinaryBytes;
return new MethodOutcome(new IdType("Binary/001/_history/002"));
}
@Override
public Class<? extends IBaseResource> getResourceType() {
return Binary.class;
}
}
}

View File

@ -0,0 +1,269 @@
package ca.uhn.fhir.rest.server;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.stringContainsInOrder;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.OperationOutcome.OperationOutcomeIssueComponent;
import org.junit.*;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.client.MyPatientWithExtensions;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
public class CreateR4Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forR4();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CreateR4Test.class);
private static int ourPort;
private static Server ourServer;
public static IBaseOperationOutcome ourReturnOo;
@Before
public void before() {
ourReturnOo = null;
}
/**
* #472
*/
@Test
public void testCreateReturnsLocationHeader() throws Exception {
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
httpPost.setEntity(new StringEntity("{\"resourceType\":\"Patient\", \"status\":\"active\"}", ContentType.parse("application/fhir+json; charset=utf-8")));
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(201, status.getStatusLine().getStatusCode());
assertEquals(1, status.getHeaders("Location").length);
assertEquals(0, status.getHeaders("Content-Location").length);
assertEquals("http://localhost:" + ourPort + "/Patient/1", status.getFirstHeader("Location").getValue());
}
@Test
public void testCreateReturnsOperationOutcome() throws Exception {
ourReturnOo = new OperationOutcome().addIssue(new OperationOutcomeIssueComponent().setDiagnostics("DIAG"));
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
httpPost.setEntity(new StringEntity("{\"resourceType\":\"Patient\", \"status\":\"active\"}", ContentType.parse("application/fhir+json; charset=utf-8")));
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(201, status.getStatusLine().getStatusCode());
assertThat(responseContent, containsString("DIAG"));
}
/**
* #342
*/
@Test
public void testCreateWithInvalidContent() throws Exception {
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
httpPost.setEntity(new StringEntity("FOO", ContentType.parse("application/xml+fhir; charset=utf-8")));
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(400, status.getStatusLine().getStatusCode());
assertThat(responseContent, containsString("<OperationOutcome xmlns=\"http://hl7.org/fhir\"><issue><severity value=\"error\"/><code value=\"processing\"/><diagnostics value=\""));
assertThat(responseContent, containsString("Failed to parse request body as XML resource. Error was: com.ctc.wstx.exc.WstxUnexpectedCharException: Unexpected character 'F'"));
}
@Test
public void testCreateWithIncorrectContent1() throws Exception {
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
httpPost.setEntity(new StringEntity("{\"foo\":\"bar\"}", ContentType.parse("application/xml+fhir; charset=utf-8")));
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(400, status.getStatusLine().getStatusCode());
assertThat(responseContent, containsString("<OperationOutcome xmlns=\"http://hl7.org/fhir\"><issue><severity value=\"error\"/><code value=\"processing\"/><diagnostics value=\""));
assertThat(responseContent, containsString("Failed to parse request body as XML resource."));
}
@Test
public void testCreateWithIncorrectContent2() throws Exception {
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
httpPost.setEntity(new StringEntity("{\"foo\":\"bar\"}", ContentType.parse("application/fhir+xml; charset=utf-8")));
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(400, status.getStatusLine().getStatusCode());
assertThat(responseContent, containsString("<OperationOutcome xmlns=\"http://hl7.org/fhir\"><issue><severity value=\"error\"/><code value=\"processing\"/><diagnostics value=\""));
assertThat(responseContent, containsString("Failed to parse request body as XML resource."));
}
@Test
public void testCreateWithIncorrectContent3() throws Exception {
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
httpPost.setEntity(new StringEntity("{\"foo\":\"bar\"}", ContentType.parse("application/fhir+json; charset=utf-8")));
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(400, status.getStatusLine().getStatusCode());
assertThat(responseContent, containsString("Failed to parse request body as JSON resource."));
}
@Test
public void testSearch() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=xml&_pretty=true");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
//@formatter:off
assertThat(responseContent, stringContainsInOrder(
"<Patient xmlns=\"http://hl7.org/fhir\">",
"<id value=\"0\"/>",
"<meta>",
"<profile value=\"http://example.com/StructureDefinition/patient_with_extensions\"/>",
"</meta>",
"<modifierExtension url=\"http://example.com/ext/date\">",
"<valueDate value=\"2011-01-01\"/>",
"</modifierExtension>",
"</Patient>"));
//@formatter:on
assertThat(responseContent, not(containsString("http://hl7.org/fhir/")));
}
@AfterClass
public static void afterClassClearContext() throws Exception {
ourServer.stop();
TestUtil.clearAllStaticFieldsForUnitTest();
}
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
PatientProvider patientProvider = new PatientProvider();
ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer(ourCtx);
servlet.setResourceProviders(patientProvider);
ServletHolder servletHolder = new ServletHolder(servlet);
proxyHandler.addServletWithMapping(servletHolder, "/*");
ourServer.setHandler(proxyHandler);
ourServer.start();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourClient = builder.build();
}
public static class PatientProvider implements IResourceProvider {
@Create()
public MethodOutcome create(@ResourceParam Patient theIdParam) {
return new MethodOutcome(new IdType("Patient", "1"), true).setOperationOutcome(ourReturnOo);
}
@Override
public Class<Patient> getResourceType() {
return Patient.class;
}
@Read()
public MyPatientWithExtensions read(@IdParam IdType theIdParam) {
MyPatientWithExtensions p0 = new MyPatientWithExtensions();
p0.setId(theIdParam);
p0.setDateExt(new DateType("2011-01-01"));
return p0;
}
@Search
public List<IBaseResource> search() {
ArrayList<IBaseResource> retVal = new ArrayList<IBaseResource>();
MyPatientWithExtensions p0 = new MyPatientWithExtensions();
p0.setId(new IdType("Patient/0"));
p0.setDateExt(new DateType("2011-01-01"));
retVal.add(p0);
Patient p1 = new Patient();
p1.setId(new IdType("Patient/1"));
p1.addName().setFamily("The Family");
retVal.add(p1);
return retVal;
}
}
}

View File

@ -0,0 +1,171 @@
package ca.uhn.fhir.rest.server;
import static org.junit.Assert.assertEquals;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.r4.model.*;
import org.junit.*;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
public class CustomTypeServerR4 {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forR4();
private static String ourLastConditionalUrl;
private static IdType ourLastId;
private static IdType ourLastIdParam;
private static boolean ourLastRequestWasSearch;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CustomTypeServerR4.class);
private static int ourPort;
private static Server ourServer;
@Before
public void before() {
ourLastId = null;
ourLastConditionalUrl = null;
ourLastIdParam = null;
ourLastRequestWasSearch = false;
}
@Test
public void testCreateWithIdInBody() throws Exception {
Patient patient = new Patient();
patient.setId("2");
patient.addIdentifier().setValue("002");
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
// httpPost.addHeader(Constants.HEADER_IF_NONE_EXIST, "Patient?identifier=system%7C001");
httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(201, status.getStatusLine().getStatusCode());
}
@Test
public void testCreateWithIdInUrl() throws Exception {
Patient patient = new Patient();
patient.addIdentifier().setValue("002");
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/2");
// httpPost.addHeader(Constants.HEADER_IF_NONE_EXIST, "Patient?identifier=system%7C001");
httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(400, status.getStatusLine().getStatusCode());
OperationOutcome oo = ourCtx.newXmlParser().parseResource(OperationOutcome.class, responseContent);
assertEquals("Can not create resource with ID \"2\", ID must not be supplied on a create (POST) operation (use an HTTP PUT / update operation if you wish to supply an ID)", oo.getIssue().get(0).getDiagnostics());
}
@Test
public void testCreateWithIdInUrlForConditional() throws Exception {
Patient patient = new Patient();
patient.addIdentifier().setValue("002");
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/2");
httpPost.addHeader(Constants.HEADER_IF_NONE_EXIST, "Patient?identifier=system%7C001");
httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(400, status.getStatusLine().getStatusCode());
OperationOutcome oo = ourCtx.newXmlParser().parseResource(OperationOutcome.class, responseContent);
assertEquals("Can not create resource with ID \"2\", ID must not be supplied on a create (POST) operation (use an HTTP PUT / update operation if you wish to supply an ID)", oo.getIssue().get(0).getDiagnostics());
}
@AfterClass
public static void afterClassClearContext() throws Exception {
ourServer.stop();
TestUtil.clearAllStaticFieldsForUnitTest();
}
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
PatientProvider patientProvider = new PatientProvider();
ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer(ourCtx);
servlet.setResourceProviders(patientProvider);
ServletHolder servletHolder = new ServletHolder(servlet);
proxyHandler.addServletWithMapping(servletHolder, "/*");
ourServer.setHandler(proxyHandler);
ourServer.start();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourClient = builder.build();
}
public static class PatientProvider implements IResourceProvider {
@Create()
public MethodOutcome createPatient(@ResourceParam Patient thePatient, @ConditionalUrlParam String theConditional, @IdParam IdType theIdParam) {
ourLastConditionalUrl = theConditional;
ourLastId = thePatient.getIdElement();
ourLastIdParam = theIdParam;
return new MethodOutcome(new IdType("Patient/001/_history/002"));
}
@Override
public Class<Patient> getResourceType() {
return Patient.class;
}
@Search
public List<IResource> search(@OptionalParam(name = "foo") StringDt theString) {
ourLastRequestWasSearch = true;
return new ArrayList<IResource>();
}
}
}

View File

@ -0,0 +1,230 @@
package ca.uhn.fhir.rest.server;
import static org.junit.Assert.assertEquals;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.r4.model.Patient;
import org.junit.*;
import com.google.common.base.Charsets;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
public class DateRangeParamSearchR4Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forR4();
private static DateRangeParam ourLastDateRange;
private static int ourPort;
private static Server ourServer;
private static SimpleDateFormat ourFmt;
private static String ourBaseUrl;
@Before
public void before() {
ourLastDateRange = null;
}
@Test
public void testSearchForMultipleUnqualifiedDate() throws Exception {
String baseUrl = "http://localhost:" + ourPort + "/Patient?" + Patient.SP_BIRTHDATE + "=";
HttpGet httpGet = new HttpGet(baseUrl + "2012-01-01&" + Patient.SP_BIRTHDATE + "=2012-02-03");
HttpResponse status = ourClient.execute(httpGet);
IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals(400, status.getStatusLine().getStatusCode());
}
@Test
public void testSearchForOneUnqualifiedDate() throws Exception {
HttpGet httpGet = new HttpGet(ourBaseUrl + "?birthdate=2012-01-01");
HttpResponse status = ourClient.execute(httpGet);
IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals("2012-01-01", ourLastDateRange.getLowerBound().getValueAsString());
assertEquals("2012-01-01", ourLastDateRange.getUpperBound().getValueAsString());
assertEquals(parse("2012-01-01 00:00:00.0000"), ourLastDateRange.getLowerBoundAsInstant());
assertEquals(parseM1("2012-01-02 00:00:00.0000"), ourLastDateRange.getUpperBoundAsInstant());
assertEquals(ParamPrefixEnum.EQUAL, ourLastDateRange.getLowerBound().getPrefix());
assertEquals(ParamPrefixEnum.EQUAL, ourLastDateRange.getUpperBound().getPrefix());
}
@Test
public void testSearchForOneQualifiedDateEq() throws Exception {
HttpGet httpGet = new HttpGet(ourBaseUrl + "?birthdate=eq2012-01-01");
HttpResponse status = ourClient.execute(httpGet);
IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals("2012-01-01", ourLastDateRange.getLowerBound().getValueAsString());
assertEquals("2012-01-01", ourLastDateRange.getUpperBound().getValueAsString());
assertEquals(parse("2012-01-01 00:00:00.0000"), ourLastDateRange.getLowerBoundAsInstant());
assertEquals(parseM1("2012-01-02 00:00:00.0000"), ourLastDateRange.getUpperBoundAsInstant());
assertEquals(ParamPrefixEnum.EQUAL, ourLastDateRange.getLowerBound().getPrefix());
assertEquals(ParamPrefixEnum.EQUAL, ourLastDateRange.getUpperBound().getPrefix());
}
@Test
public void testSearchForOneQualifiedDateGt() throws Exception {
HttpGet httpGet = new HttpGet(ourBaseUrl + "?birthdate=gt2012-01-01");
HttpResponse status = ourClient.execute(httpGet);
IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals("2012-01-01", ourLastDateRange.getLowerBound().getValueAsString());
assertEquals(null, ourLastDateRange.getUpperBound());
assertEquals(parse("2012-01-02 00:00:00.0000"), ourLastDateRange.getLowerBoundAsInstant());
assertEquals(null, ourLastDateRange.getUpperBoundAsInstant());
assertEquals(ParamPrefixEnum.GREATERTHAN, ourLastDateRange.getLowerBound().getPrefix());
assertEquals(null, ourLastDateRange.getUpperBound());
}
@Test
public void testSearchForOneQualifiedDateLt() throws Exception {
HttpGet httpGet = new HttpGet(ourBaseUrl + "?birthdate=lt2012-01-01");
HttpResponse status = ourClient.execute(httpGet);
IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals(null, ourLastDateRange.getLowerBound());
assertEquals("2012-01-01", ourLastDateRange.getUpperBound().getValueAsString());
assertEquals(null, ourLastDateRange.getLowerBoundAsInstant());
assertEquals(parseM1("2012-01-01 00:00:00.0000"), ourLastDateRange.getUpperBoundAsInstant());
assertEquals(null, ourLastDateRange.getLowerBound());
assertEquals(ParamPrefixEnum.LESSTHAN, ourLastDateRange.getUpperBound().getPrefix());
}
@Test
public void testSearchForOneQualifiedDateGe() throws Exception {
HttpGet httpGet = new HttpGet(ourBaseUrl + "?birthdate=ge2012-01-01");
HttpResponse status = ourClient.execute(httpGet);
IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals("2012-01-01", ourLastDateRange.getLowerBound().getValueAsString());
assertEquals(null, ourLastDateRange.getUpperBound());
assertEquals(parse("2012-01-01 00:00:00.0000"), ourLastDateRange.getLowerBoundAsInstant());
assertEquals(null, ourLastDateRange.getUpperBoundAsInstant());
assertEquals(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, ourLastDateRange.getLowerBound().getPrefix());
assertEquals(null, ourLastDateRange.getUpperBound());
}
@Test
public void testSearchForOneQualifiedDateLe() throws Exception {
HttpGet httpGet = new HttpGet(ourBaseUrl + "?birthdate=le2012-01-01");
HttpResponse status = ourClient.execute(httpGet);
IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals(null, ourLastDateRange.getLowerBound());
assertEquals("2012-01-01", ourLastDateRange.getUpperBound().getValueAsString());
assertEquals(null, ourLastDateRange.getLowerBoundAsInstant());
assertEquals(parseM1("2012-01-02 00:00:00.0000"), ourLastDateRange.getUpperBoundAsInstant());
assertEquals(null, ourLastDateRange.getLowerBound());
assertEquals(ParamPrefixEnum.LESSTHAN_OR_EQUALS, ourLastDateRange.getUpperBound().getPrefix());
}
public static Date parse(String theString) throws ParseException {
return ourFmt.parse(theString);
}
public static Date parseM1(String theString) throws ParseException {
return new Date(ourFmt.parse(theString).getTime() - 1L);
}
static {
ourFmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSS");
}
@AfterClass
public static void afterClassClearContext() throws Exception {
ourServer.stop();
TestUtil.clearAllStaticFieldsForUnitTest();
}
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();
ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer(ourCtx);
servlet.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
servlet.setResourceProviders(patientProvider);
ServletHolder servletHolder = new ServletHolder(servlet);
proxyHandler.addServletWithMapping(servletHolder, "/*");
ourServer.setHandler(proxyHandler);
ourServer.start();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourClient = builder.build();
ourBaseUrl = "http://localhost:" + ourPort + "/Patient";
}
public static class DummyPatientResourceProvider implements IResourceProvider {
@Override
public Class<Patient> getResourceType() {
return Patient.class;
}
@Search()
public List<Patient> search(@RequiredParam(name=Patient.SP_BIRTHDATE) DateRangeParam theDateRange) {
ourLastDateRange = theDateRange;
ArrayList<Patient> retVal = new ArrayList<Patient>();
Patient patient = new Patient();
patient.setId("1");
patient.addIdentifier().setSystem("system").setValue("hello");
retVal.add(patient);
return retVal;
}
}
}

View File

@ -0,0 +1,128 @@
package ca.uhn.fhir.rest.server;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.concurrent.TimeUnit;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Patient;
import org.junit.*;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
public class DeleteConditionalR4Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forR4();
private static IGenericClient ourHapiClient;
private static String ourLastConditionalUrl;
private static IdType ourLastIdParam;
private static boolean ourLastRequestWasDelete;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DeleteConditionalR4Test.class);
private static int ourPort;
private static Server ourServer;
@Before
public void before() {
ourLastConditionalUrl = null;
ourLastIdParam = null;
ourLastRequestWasDelete = false;
}
@Test
public void testSearchStillWorks() throws Exception {
Patient patient = new Patient();
patient.addIdentifier().setValue("002");
// HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_pretty=true");
//
// HttpResponse status = ourClient.execute(httpGet);
//
// String responseContent = IOUtils.toString(status.getEntity().getContent());
// IOUtils.closeQuietly(status.getEntity().getContent());
//
// ourLog.info("Response was:\n{}", responseContent);
//@formatter:off
ourHapiClient
.delete()
.resourceConditionalByType(Patient.class)
.where(Patient.IDENTIFIER.exactly().systemAndIdentifier("SOMESYS","SOMEID"))
.execute();
//@formatter:on
assertTrue(ourLastRequestWasDelete);
assertEquals(null, ourLastIdParam);
assertEquals("Patient?identifier=SOMESYS%7CSOMEID", ourLastConditionalUrl);
}
@AfterClass
public static void afterClassClearContext() throws Exception {
ourServer.stop();
TestUtil.clearAllStaticFieldsForUnitTest();
}
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
PatientProvider patientProvider = new PatientProvider();
ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer(ourCtx);
servlet.setResourceProviders(patientProvider);
ServletHolder servletHolder = new ServletHolder(servlet);
proxyHandler.addServletWithMapping(servletHolder, "/*");
ourServer.setHandler(proxyHandler);
ourServer.start();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourClient = builder.build();
ourCtx.getRestfulClientFactory().setSocketTimeout(500 * 1000);
ourHapiClient = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/");
ourHapiClient.registerInterceptor(new LoggingInterceptor());
}
public static class PatientProvider implements IResourceProvider {
@Delete()
public MethodOutcome deletePatient(@IdParam IdType theIdParam, @ConditionalUrlParam String theConditional) {
ourLastRequestWasDelete = true;
ourLastConditionalUrl = theConditional;
ourLastIdParam = theIdParam;
return new MethodOutcome(new IdType("Patient/001/_history/002"));
}
@Override
public Class<? extends IBaseResource> getResourceType() {
return Patient.class;
}
}
}

View File

@ -0,0 +1,768 @@
package ca.uhn.fhir.rest.server;
import static org.hamcrest.Matchers.containsInRelativeOrder;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.*;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestOperationComponent;
import org.hl7.fhir.r4.model.OperationDefinition.OperationParameterUse;
import org.junit.*;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
public class OperationServerR4Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx;
private static IdType ourLastId;
private static String ourLastMethod;
private static StringType ourLastParam1;
private static Patient ourLastParam2;
private static List<StringType> ourLastParam3;
private static Money ourLastParamMoney1;
private static UnsignedIntType ourLastParamUnsignedInt1;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationServerR4Test.class);
private static int ourPort;
private static Server ourServer;
private IGenericClient myFhirClient;
@Before
public void before() {
ourLastParam1 = null;
ourLastParam2 = null;
ourLastParam3 = null;
ourLastParamUnsignedInt1 = null;
ourLastParamMoney1 = null;
ourLastId = null;
ourLastMethod = "";
myFhirClient = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort);
}
@Test
public void testConformance() throws Exception {
LoggingInterceptor loggingInterceptor = new LoggingInterceptor();
loggingInterceptor.setLogResponseBody(true);
myFhirClient.registerInterceptor(loggingInterceptor);
CapabilityStatement p = myFhirClient.fetchConformance().ofType(CapabilityStatement.class).prettyPrint().execute();
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(p));
List<CapabilityStatementRestOperationComponent> ops = p.getRest().get(0).getOperation();
assertThat(ops.size(), greaterThan(1));
List<String> opNames = toOpNames(ops);
assertThat(opNames, containsInRelativeOrder("OP_TYPE"));
// OperationDefinition def = (OperationDefinition) ops.get(opNames.indexOf("OP_TYPE")).getDefinition().getResource();
OperationDefinition def = myFhirClient.read().resource(OperationDefinition.class).withId(ops.get(opNames.indexOf("OP_TYPE")).getDefinition().getReferenceElement()).execute();
assertEquals("OP_TYPE", def.getCode());
}
/**
* See #380
*/
@Test
public void testOperationDefinition() {
OperationDefinition def = myFhirClient.read().resource(OperationDefinition.class).withId("OperationDefinition/Patient--OP_TYPE").execute();
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(def));
// @OperationParam(name="PARAM1") StringType theParam1,
// @OperationParam(name="PARAM2") Patient theParam2,
// @OperationParam(name="PARAM3", min=2, max=5) List<StringType> theParam3,
// @OperationParam(name="PARAM4", min=1) List<StringType> theParam4,
assertEquals(4, def.getParameter().size());
assertEquals("PARAM1", def.getParameter().get(0).getName());
assertEquals(OperationParameterUse.IN, def.getParameter().get(0).getUse());
assertEquals(0, def.getParameter().get(0).getMin());
assertEquals("1", def.getParameter().get(0).getMax());
assertEquals("PARAM2", def.getParameter().get(1).getName());
assertEquals(OperationParameterUse.IN, def.getParameter().get(1).getUse());
assertEquals(0, def.getParameter().get(1).getMin());
assertEquals("1", def.getParameter().get(1).getMax());
assertEquals("PARAM3", def.getParameter().get(2).getName());
assertEquals(OperationParameterUse.IN, def.getParameter().get(2).getUse());
assertEquals(2, def.getParameter().get(2).getMin());
assertEquals("5", def.getParameter().get(2).getMax());
assertEquals("PARAM4", def.getParameter().get(3).getName());
assertEquals(OperationParameterUse.IN, def.getParameter().get(3).getUse());
assertEquals(1, def.getParameter().get(3).getMin());
assertEquals("*", def.getParameter().get(3).getMax());
}
private List<String> toOpNames(List<CapabilityStatementRestOperationComponent> theOps) {
ArrayList<String> retVal = new ArrayList<String>();
for (CapabilityStatementRestOperationComponent next : theOps) {
retVal.add(next.getName());
}
return retVal;
}
@Test
public void testInstanceEverythingGet() throws Exception {
// Try with a GET
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/$everything");
CloseableHttpResponse status = ourClient.execute(httpGet);
assertEquals(200, status.getStatusLine().getStatusCode());
String response = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals("instance $everything", ourLastMethod);
assertThat(response, startsWith("<Bundle"));
assertEquals("Patient/123", ourLastId.toUnqualifiedVersionless().getValue());
}
@Test
public void testInstanceEverythingHapiClient() throws Exception {
ourCtx.newRestfulGenericClient("http://localhost:" + ourPort).operation().onInstance(new IdType("Patient/123")).named("$everything").withParameters(new Parameters()).execute();
assertEquals("instance $everything", ourLastMethod);
assertEquals("Patient/123", ourLastId.toUnqualifiedVersionless().getValue());
}
@Test
public void testInstanceEverythingPost() throws Exception {
String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(new Parameters());
// Try with a POST
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/123/$everything");
httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
assertEquals(200, status.getStatusLine().getStatusCode());
String response = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals("instance $everything", ourLastMethod);
assertThat(response, startsWith("<Bundle"));
assertEquals("Patient/123", ourLastId.toUnqualifiedVersionless().getValue());
}
@Test
public void testOperationCantUseGetIfItIsntIdempotent() throws Exception {
HttpGet httpPost = new HttpGet("http://localhost:" + ourPort + "/Patient/123/$OP_INSTANCE");
HttpResponse status = ourClient.execute(httpPost);
assertEquals(Constants.STATUS_HTTP_405_METHOD_NOT_ALLOWED, status.getStatusLine().getStatusCode());
String response = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals("POST", status.getFirstHeader(Constants.HEADER_ALLOW).getValue());
assertThat(response, containsString("HTTP Method GET is not allowed"));
}
@Test
public void testOperationWrongParameterType() throws Exception {
Parameters p = new Parameters();
p.addParameter().setName("PARAM1").setValue(new IntegerType(123));
String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p);
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/123/$OP_INSTANCE");
httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
CloseableHttpResponse status = ourClient.execute(httpPost);
try {
String response = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
assertThat(response, containsString("Request has parameter PARAM1 of type IntegerType but method expects type StringType"));
ourLog.info(response);
} finally {
IOUtils.closeQuietly(status);
}
}
@Test
public void testOperationOnInstance() throws Exception {
Parameters p = new Parameters();
p.addParameter().setName("PARAM1").setValue(new StringType("PARAM1val"));
p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true));
String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p);
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/123/$OP_INSTANCE");
httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
assertEquals(200, status.getStatusLine().getStatusCode());
String response = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals("PARAM1val", ourLastParam1.getValue());
assertEquals(true, ourLastParam2.getActive());
assertEquals("123", ourLastId.getIdPart());
assertEquals("$OP_INSTANCE", ourLastMethod);
Parameters resp = ourCtx.newXmlParser().parseResource(Parameters.class, response);
assertEquals("RET1", resp.getParameter().get(0).getName());
/*
* Against type should fail
*/
httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_INSTANCE");
httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
status = ourClient.execute(httpPost);
response = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info(response);
assertEquals(400, status.getStatusLine().getStatusCode());
}
@Test
public void testOperationOnInstanceAndType_Instance() throws Exception {
Parameters p = new Parameters();
p.addParameter().setName("PARAM1").setValue(new StringType("PARAM1val"));
p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true));
String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p);
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/123/$OP_INSTANCE_OR_TYPE");
httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
assertEquals(200, status.getStatusLine().getStatusCode());
String response = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals("PARAM1val", ourLastParam1.getValue());
assertEquals(true, ourLastParam2.getActive());
assertEquals("123", ourLastId.getIdPart());
assertEquals("$OP_INSTANCE_OR_TYPE", ourLastMethod);
Parameters resp = ourCtx.newXmlParser().parseResource(Parameters.class, response);
assertEquals("RET1", resp.getParameter().get(0).getName());
}
@Test
public void testOperationOnInstanceAndType_Type() throws Exception {
Parameters p = new Parameters();
p.addParameter().setName("PARAM1").setValue(new StringType("PARAM1val"));
p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true));
String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p);
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_INSTANCE_OR_TYPE");
httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
CloseableHttpResponse status = ourClient.execute(httpPost);
assertEquals(200, status.getStatusLine().getStatusCode());
String response = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals("PARAM1val", ourLastParam1.getValue());
assertEquals(true, ourLastParam2.getActive());
assertEquals(null, ourLastId);
assertEquals("$OP_INSTANCE_OR_TYPE", ourLastMethod);
Parameters resp = ourCtx.newXmlParser().parseResource(Parameters.class, response);
assertEquals("RET1", resp.getParameter().get(0).getName());
}
@Test
public void testOperationOnServer() throws Exception {
Parameters p = new Parameters();
p.addParameter().setName("PARAM1").setValue(new StringType("PARAM1val"));
p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true));
String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p);
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/$OP_SERVER");
httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
assertEquals(200, status.getStatusLine().getStatusCode());
String response = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals("PARAM1val", ourLastParam1.getValue());
assertEquals(true, ourLastParam2.getActive());
assertEquals("$OP_SERVER", ourLastMethod);
Parameters resp = ourCtx.newXmlParser().parseResource(Parameters.class, response);
assertEquals("RET1", resp.getParameter().get(0).getName());
}
@Test
public void testOperationOnType() throws Exception {
Parameters p = new Parameters();
p.addParameter().setName("PARAM1").setValue(new StringType("PARAM1val"));
p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true));
String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p);
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_TYPE");
httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
assertEquals(200, status.getStatusLine().getStatusCode());
String response = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals("PARAM1val", ourLastParam1.getValue());
assertEquals(true, ourLastParam2.getActive());
assertEquals("$OP_TYPE", ourLastMethod);
Parameters resp = ourCtx.newXmlParser().parseResource(Parameters.class, response);
assertEquals("RET1", resp.getParameter().get(0).getName());
}
@Test
public void testOperationOnTypeReturnBundle() throws Exception {
Parameters p = new Parameters();
p.addParameter().setName("PARAM1").setValue(new StringType("PARAM1val"));
p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true));
String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p);
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_TYPE_RET_BUNDLE");
httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
assertEquals(200, status.getStatusLine().getStatusCode());
String response = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals("PARAM1val", ourLastParam1.getValue());
assertEquals(true, ourLastParam2.getActive());
assertEquals("$OP_TYPE_RET_BUNDLE", ourLastMethod);
Bundle resp = ourCtx.newXmlParser().parseResource(Bundle.class, response);
assertEquals("100", resp.getEntryFirstRep().getResponse().getStatus());
}
@Test
public void testOperationWithBundleProviderResponse() throws Exception {
HttpGet httpPost = new HttpGet("http://localhost:" + ourPort + "/$OP_INSTANCE_BUNDLE_PROVIDER?_pretty=true");
HttpResponse status = ourClient.execute(httpPost);
assertEquals(200, status.getStatusLine().getStatusCode());
String response = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info(response);
ourCtx.newXmlParser().parseResource(Bundle.class, response);
}
@Test
public void testOperationWithGetUsingParams() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$OP_TYPE?PARAM1=PARAM1val");
HttpResponse status = ourClient.execute(httpGet);
assertEquals(200, status.getStatusLine().getStatusCode());
String response = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals("PARAM1val", ourLastParam1.getValue());
assertNull(ourLastParam2);
assertEquals("$OP_TYPE", ourLastMethod);
Parameters resp = ourCtx.newXmlParser().parseResource(Parameters.class, response);
assertEquals("RET1", resp.getParameter().get(0).getName());
}
@Test
public void testOperationWithGetUsingParamsFailsWithNonPrimitive() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$OP_TYPE?PARAM1=PARAM1val&PARAM2=foo");
HttpResponse status = ourClient.execute(httpGet);
assertEquals(405, status.getStatusLine().getStatusCode());
String response = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals("POST", status.getFirstHeader(Constants.HEADER_ALLOW).getValue());
assertThat(response, containsString("Can not invoke operation $OP_TYPE using HTTP GET because parameter PARAM2 is not a primitive datatype"));
}
@Test
public void testOperationWithListParam() throws Exception {
Parameters p = new Parameters();
p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true));
p.addParameter().setName("PARAM3").setValue(new StringType("PARAM3val1"));
p.addParameter().setName("PARAM3").setValue(new StringType("PARAM3val2"));
String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p);
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/$OP_SERVER_LIST_PARAM");
httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
assertEquals(200, status.getStatusLine().getStatusCode());
String response = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals("$OP_SERVER_LIST_PARAM", ourLastMethod);
assertEquals(true, ourLastParam2.getActive());
assertEquals(null, ourLastParam1);
assertEquals(2, ourLastParam3.size());
assertEquals("PARAM3val1", ourLastParam3.get(0).getValue());
assertEquals("PARAM3val2", ourLastParam3.get(1).getValue());
Parameters resp = ourCtx.newXmlParser().parseResource(Parameters.class, response);
assertEquals("RET1", resp.getParameter().get(0).getName());
}
@Test
public void testOperationWithProfileDatatypeParams() throws Exception {
Parameters p = new Parameters();
p.addParameter().setName("PARAM1").setValue(new IntegerType("123"));
String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p);
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_PROFILE_DT");
httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
assertEquals(200, status.getStatusLine().getStatusCode());
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals("$OP_PROFILE_DT", ourLastMethod);
assertEquals("123", ourLastParamUnsignedInt1.getValueAsString());
}
@Test
public void testOperationWithProfileDatatypeParams2() throws Exception {
Parameters p = new Parameters();
Money money = new Money();
money.setCode("CODE");
money.setSystem("SYSTEM");
money.setValue(123L);
p.addParameter().setName("PARAM1").setValue(money);
String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p);
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_PROFILE_DT2");
httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
assertEquals(200, status.getStatusLine().getStatusCode());
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals("$OP_PROFILE_DT2", ourLastMethod);
assertEquals("CODE", ourLastParamMoney1.getCode());
assertEquals("SYSTEM", ourLastParamMoney1.getSystem());
assertEquals("123", ourLastParamMoney1.getValue().toString());
}
@Test
public void testOperationWithProfileDatatypeUrl() throws Exception {
HttpGet httpPost = new HttpGet("http://localhost:" + ourPort + "/Patient/$OP_PROFILE_DT?PARAM1=123");
HttpResponse status = ourClient.execute(httpPost);
assertEquals(200, status.getStatusLine().getStatusCode());
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals("$OP_PROFILE_DT", ourLastMethod);
assertEquals("123", ourLastParamUnsignedInt1.getValueAsString());
}
@Test
public void testOperationWrongParamType() throws Exception {
Parameters p = new Parameters();
p.addParameter().setName("PARAM1").setValue(new IntegerType("123"));
p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true));
String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p);
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_TYPE");
httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
assertEquals(400, status.getStatusLine().getStatusCode());
String response = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info(status.getStatusLine().toString());
ourLog.info(response);
assertThat(response, containsString("Request has parameter PARAM1 of type IntegerType but method expects type StringType"));
}
@Test
public void testReadWithOperations() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123");
HttpResponse status = ourClient.execute(httpGet);
assertEquals(200, status.getStatusLine().getStatusCode());
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals("read", ourLastMethod);
}
@AfterClass
public static void afterClassClearContext() throws Exception {
ourServer.stop();
TestUtil.clearAllStaticFieldsForUnitTest();
}
@BeforeClass
public static void beforeClass() throws Exception {
ourCtx = FhirContext.forR4();
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer(ourCtx);
servlet.setPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(2));
servlet.setFhirContext(ourCtx);
servlet.setResourceProviders(new PatientProvider());
servlet.setPlainProviders(new PlainProvider());
ServletHolder servletHolder = new ServletHolder(servlet);
proxyHandler.addServletWithMapping(servletHolder, "/*");
ourServer.setHandler(proxyHandler);
ourServer.start();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourClient = builder.build();
}
public static void main(String[] theValue) {
Parameters p = new Parameters();
p.addParameter().setName("start").setValue(new DateTimeType("2001-01-02"));
p.addParameter().setName("end").setValue(new DateTimeType("2015-07-10"));
String inParamsStr = FhirContext.forDstu2().newXmlParser().encodeResourceToString(p);
ourLog.info(inParamsStr.replace("\"", "\\\""));
}
public static class PatientProvider implements IResourceProvider {
@Override
public Class<Patient> getResourceType() {
return Patient.class;
}
//@formatter:off
@Operation(name="$OP_INSTANCE")
public Parameters opInstance(
@IdParam IdType theId,
@OperationParam(name="PARAM1") StringType theParam1,
@OperationParam(name="PARAM2") Patient theParam2
) {
//@formatter:on
ourLastMethod = "$OP_INSTANCE";
ourLastId = theId;
ourLastParam1 = theParam1;
ourLastParam2 = theParam2;
Parameters retVal = new Parameters();
retVal.addParameter().setName("RET1").setValue(new StringType("RETVAL1"));
return retVal;
}
//@formatter:off
@Operation(name="$OP_INSTANCE_OR_TYPE")
public Parameters opInstanceOrType(
@IdParam(optional=true) IdType theId,
@OperationParam(name="PARAM1") StringType theParam1,
@OperationParam(name="PARAM2") Patient theParam2
) {
//@formatter:on
ourLastMethod = "$OP_INSTANCE_OR_TYPE";
ourLastId = theId;
ourLastParam1 = theParam1;
ourLastParam2 = theParam2;
Parameters retVal = new Parameters();
retVal.addParameter().setName("RET1").setValue(new StringType("RETVAL1"));
return retVal;
}
//@formatter:off
@Operation(name="$OP_PROFILE_DT2", idempotent=true)
public Bundle opProfileType(
@OperationParam(name="PARAM1") Money theParam1
) {
//@formatter:on
ourLastMethod = "$OP_PROFILE_DT2";
ourLastParamMoney1 = theParam1;
Bundle retVal = new Bundle();
retVal.addEntry().getResponse().setStatus("100");
return retVal;
}
//@formatter:off
@Operation(name="$OP_PROFILE_DT", idempotent=true)
public Bundle opProfileType(
@OperationParam(name="PARAM1") UnsignedIntType theParam1
) {
//@formatter:on
ourLastMethod = "$OP_PROFILE_DT";
ourLastParamUnsignedInt1 = theParam1;
Bundle retVal = new Bundle();
retVal.addEntry().getResponse().setStatus("100");
return retVal;
}
//@formatter:off
@SuppressWarnings("unused")
@Operation(name="$OP_TYPE", idempotent=true)
public Parameters opType(
@OperationParam(name="PARAM1") StringType theParam1,
@OperationParam(name="PARAM2") Patient theParam2,
@OperationParam(name="PARAM3", min=2, max=5) List<StringType> theParam3,
@OperationParam(name="PARAM4", min=1) List<StringType> theParam4
) {
//@formatter:on
ourLastMethod = "$OP_TYPE";
ourLastParam1 = theParam1;
ourLastParam2 = theParam2;
Parameters retVal = new Parameters();
retVal.addParameter().setName("RET1").setValue(new StringType("RETVAL1"));
return retVal;
}
//@formatter:off
@Operation(name="$OP_TYPE_ONLY_STRING", idempotent=true)
public Parameters opTypeOnlyString(
@OperationParam(name="PARAM1") StringType theParam1
) {
//@formatter:on
ourLastMethod = "$OP_TYPE";
ourLastParam1 = theParam1;
Parameters retVal = new Parameters();
retVal.addParameter().setName("RET1").setValue(new StringType("RETVAL1"));
return retVal;
}
//@formatter:off
@Operation(name="$OP_TYPE_RET_BUNDLE")
public Bundle opTypeRetBundle(
@OperationParam(name="PARAM1") StringType theParam1,
@OperationParam(name="PARAM2") Patient theParam2
) {
//@formatter:on
ourLastMethod = "$OP_TYPE_RET_BUNDLE";
ourLastParam1 = theParam1;
ourLastParam2 = theParam2;
Bundle retVal = new Bundle();
retVal.addEntry().getResponse().setStatus("100");
return retVal;
}
@Operation(name = "$everything", idempotent=true)
public Bundle patientEverything(@IdParam IdType thePatientId) {
ourLastMethod = "instance $everything";
ourLastId = thePatientId;
return new Bundle();
}
/**
* Just to make sure this method doesn't "steal" calls
*/
@Read
public Patient read(@IdParam IdType theId) {
ourLastMethod = "read";
Patient retVal = new Patient();
retVal.setId(theId);
return retVal;
}
}
public static class PlainProvider {
//@formatter:off
@Operation(name="$OP_INSTANCE_BUNDLE_PROVIDER", idempotent=true)
public IBundleProvider opInstanceReturnsBundleProvider() {
ourLastMethod = "$OP_INSTANCE_BUNDLE_PROVIDER";
List<IBaseResource> resources = new ArrayList<IBaseResource>();
for (int i =0; i < 100;i++) {
Patient p = new Patient();
p.setId("Patient/" + i);
p.addName().setFamily("Patient " + i);
resources.add(p);
}
return new SimpleBundleProvider(resources);
}
//@formatter:off
@Operation(name="$OP_SERVER")
public Parameters opServer(
@OperationParam(name="PARAM1") StringType theParam1,
@OperationParam(name="PARAM2") Patient theParam2
) {
//@formatter:on
ourLastMethod = "$OP_SERVER";
ourLastParam1 = theParam1;
ourLastParam2 = theParam2;
Parameters retVal = new Parameters();
retVal.addParameter().setName("RET1").setValue(new StringType("RETVAL1"));
return retVal;
}
//@formatter:off
@Operation(name="$OP_SERVER_LIST_PARAM")
public Parameters opServerListParam(
@OperationParam(name="PARAM2") Patient theParam2,
@OperationParam(name="PARAM3") List<StringType> theParam3
) {
//@formatter:on
ourLastMethod = "$OP_SERVER_LIST_PARAM";
ourLastParam2 = theParam2;
ourLastParam3 = theParam3;
Parameters retVal = new Parameters();
retVal.addParameter().setName("RET1").setValue(new StringType("RETVAL1"));
return retVal;
}
}
}

View File

@ -0,0 +1,187 @@
package ca.uhn.fhir.rest.server;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Bundle.BundleLinkComponent;
import org.hl7.fhir.r4.model.Patient;
import org.junit.*;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import com.google.common.collect.Lists;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.TokenAndListParam;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
public class SearchBundleProviderWithNoSizeR4Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forR4();
private static TokenAndListParam ourIdentifiers;
private static IBundleProvider ourLastBundleProvider;
private static String ourLastMethod;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchBundleProviderWithNoSizeR4Test.class);
private static int ourPort;
private static Server ourServer;
@Before
public void before() {
ourLastMethod = null;
ourIdentifiers = null;
}
@Test
public void testBundleProviderReturnsNoSize() throws Exception {
Bundle respBundle;
ourLastBundleProvider = mock(IBundleProvider.class);
when(ourLastBundleProvider.size()).thenReturn(null);
when(ourLastBundleProvider.getResources(any(int.class), any(int.class))).then(new Answer<List<IBaseResource>>() {
@Override
public List<IBaseResource> answer(InvocationOnMock theInvocation) throws Throwable {
int from =(Integer)theInvocation.getArguments()[0];
int to =(Integer)theInvocation.getArguments()[1];
ArrayList<IBaseResource> retVal = Lists.newArrayList();
for (int i = from; i < to; i++) {
Patient p = new Patient();
p.setId(Integer.toString(i));
retVal.add(p);
}
return retVal;
}});
HttpGet httpGet;
CloseableHttpResponse status = null;
BundleLinkComponent linkNext;
try {
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=json");
status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals("searchAll", ourLastMethod);
respBundle = ourCtx.newJsonParser().parseResource(Bundle.class, responseContent);
assertEquals(10, respBundle.getEntry().size());
assertEquals("Patient/0", respBundle.getEntry().get(0).getResource().getIdElement().toUnqualifiedVersionless().getValue());
linkNext = respBundle.getLink("next");
assertNotNull(linkNext);
} finally {
IOUtils.closeQuietly(status.getEntity().getContent());
}
when(ourLastBundleProvider.size()).thenReturn(25);
try {
httpGet = new HttpGet(linkNext.getUrl());
status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals("searchAll", ourLastMethod);
respBundle = ourCtx.newJsonParser().parseResource(Bundle.class, responseContent);
assertEquals(10, respBundle.getEntry().size());
assertEquals("Patient/10", respBundle.getEntry().get(0).getResource().getIdElement().toUnqualifiedVersionless().getValue());
linkNext = respBundle.getLink("next");
assertNotNull(linkNext);
} finally {
IOUtils.closeQuietly(status.getEntity().getContent());
}
try {
httpGet = new HttpGet(linkNext.getUrl());
status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals("searchAll", ourLastMethod);
respBundle = ourCtx.newJsonParser().parseResource(Bundle.class, responseContent);
assertEquals(5, respBundle.getEntry().size());
assertEquals("Patient/20", respBundle.getEntry().get(0).getResource().getIdElement().toUnqualifiedVersionless().getValue());
linkNext = respBundle.getLink("next");
assertNull(linkNext);
} finally {
IOUtils.closeQuietly(status.getEntity().getContent());
}
}
@AfterClass
public static void afterClassClearContext() throws Exception {
ourServer.stop();
TestUtil.clearAllStaticFieldsForUnitTest();
}
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();
ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer(ourCtx);
servlet.setPagingProvider(new FifoMemoryPagingProvider(10));
servlet.setResourceProviders(patientProvider);
ServletHolder servletHolder = new ServletHolder(servlet);
proxyHandler.addServletWithMapping(servletHolder, "/*");
ourServer.setHandler(proxyHandler);
ourServer.start();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourClient = builder.build();
}
public static class DummyPatientResourceProvider implements IResourceProvider {
@Override
public Class<? extends IBaseResource> getResourceType() {
return Patient.class;
}
@Search()
public IBundleProvider searchAll() {
ourLastMethod = "searchAll";
return ourLastBundleProvider;
}
}
}

View File

@ -0,0 +1,119 @@
package ca.uhn.fhir.rest.server;
import static org.junit.Assert.assertEquals;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.HumanName;
import org.hl7.fhir.r4.model.Patient;
import org.junit.*;
import com.google.common.base.Charsets;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.annotation.OptionalParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.param.*;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
public class SearchHasParamR4Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forR4();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchHasParamR4Test.class);
private static int ourPort;
private static Server ourServer;
private static String ourLastMethod;
private static HasAndListParam ourLastParam;
@Before
public void before() {
ourLastMethod = null;
ourLastParam = null;
}
@Test
public void testSearch() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_has:Encounter:patient:type=SURG");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals("search", ourLastMethod);
HasParam param = ourLastParam.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(0);
assertEquals("Encounter", param.getTargetResourceType());
assertEquals("patient", param.getOwningFieldName());
assertEquals("type", param.getParameterName());
assertEquals("SURG", param.getParameterValue());
}
@AfterClass
public static void afterClassClearContext() throws Exception {
ourServer.stop();
TestUtil.clearAllStaticFieldsForUnitTest();
}
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();
ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer(ourCtx);
servlet.setPagingProvider(new FifoMemoryPagingProvider(10));
servlet.setResourceProviders(patientProvider);
ServletHolder servletHolder = new ServletHolder(servlet);
proxyHandler.addServletWithMapping(servletHolder, "/*");
ourServer.setHandler(proxyHandler);
ourServer.start();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourClient = builder.build();
}
public static class DummyPatientResourceProvider implements IResourceProvider {
@Override
public Class<? extends IBaseResource> getResourceType() {
return Patient.class;
}
//@formatter:off
@SuppressWarnings("rawtypes")
@Search()
public List search(
@OptionalParam(name=Patient.SP_IDENTIFIER) TokenParam theIdentifier,
@OptionalParam(name="_has") HasAndListParam theParam
) {
ourLastMethod = "search";
ourLastParam = theParam;
ArrayList<Patient> retVal = new ArrayList<Patient>();
retVal.add((Patient) new Patient().addName(new HumanName().setFamily("FAMILY")).setId("1"));
return retVal;
}
//@formatter:on
}
}

View File

@ -0,0 +1,368 @@
package ca.uhn.fhir.rest.server;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.*;
import org.junit.*;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.api.SearchStyleEnum;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.gclient.StringClientParam;
import ca.uhn.fhir.rest.param.TokenAndListParam;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.util.*;
public class SearchR4Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forR4();
private static TokenAndListParam ourIdentifiers;
private static String ourLastMethod;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchR4Test.class);
private static int ourPort;
private static Server ourServer;
@Before
public void before() {
ourLastMethod = null;
ourIdentifiers = null;
}
@Test
public void testSearchNormal() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar");
CloseableHttpResponse status = ourClient.execute(httpGet);
try {
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals("search", ourLastMethod);
assertEquals("foo", ourIdentifiers.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(0).getSystem());
assertEquals("bar", ourIdentifiers.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(0).getValue());
} finally {
IOUtils.closeQuietly(status.getEntity().getContent());
}
}
@Test
public void testSearchWithInvalidChain() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier.chain=foo%7Cbar");
CloseableHttpResponse status = ourClient.execute(httpGet);
try {
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(responseContent);
assertEquals(400, status.getStatusLine().getStatusCode());
OperationOutcome oo = (OperationOutcome) ourCtx.newJsonParser().parseResource(responseContent);
assertEquals(
"Invalid search parameter \"identifier.chain\". Parameter contains a chain (.chain) and chains are not supported for this parameter (chaining is only allowed on reference parameters)",
oo.getIssueFirstRep().getDiagnostics());
} finally {
IOUtils.closeQuietly(status.getEntity().getContent());
}
}
@Test
public void testPagingPreservesEncodingJson() throws Exception {
HttpGet httpGet;
String linkNext;
Bundle bundle;
// Initial search
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar&_format=json");
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON);
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
assertThat(linkNext, containsString("_format=json"));
// Fetch the next page
httpGet = new HttpGet(linkNext);
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON);
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
assertThat(linkNext, containsString("_format=json"));
// Fetch the next page
httpGet = new HttpGet(linkNext);
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON);
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
assertThat(linkNext, containsString("_format=json"));
// Fetch the next page
httpGet = new HttpGet(linkNext);
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON);
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
assertThat(linkNext, containsString("_format=json"));
}
@Test
public void testPagingPreservesEncodingApplicationJsonFhir() throws Exception {
HttpGet httpGet;
String linkNext;
Bundle bundle;
// Initial search
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar&_format=" + Constants.CT_FHIR_JSON_NEW);
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON);
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
assertThat(linkNext, containsString("_format=" + UrlUtil.escape(Constants.CT_FHIR_JSON_NEW)));
// Fetch the next page
httpGet = new HttpGet(linkNext);
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON);
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
assertThat(linkNext, containsString("_format=" + UrlUtil.escape(Constants.CT_FHIR_JSON_NEW)));
// Fetch the next page
httpGet = new HttpGet(linkNext);
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON);
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
assertThat(linkNext, containsString("_format=" + UrlUtil.escape(Constants.CT_FHIR_JSON_NEW)));
// Fetch the next page
httpGet = new HttpGet(linkNext);
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON);
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
assertThat(linkNext, containsString("_format=" + UrlUtil.escape(Constants.CT_FHIR_JSON_NEW)));
}
@Test
public void testPagingPreservesEncodingXml() throws Exception {
HttpGet httpGet;
String linkNext;
Bundle bundle;
// Initial search
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar&_format=xml");
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.XML);
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
assertThat(linkNext, containsString("_format=xml"));
// Fetch the next page
httpGet = new HttpGet(linkNext);
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.XML);
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
assertThat(linkNext, containsString("_format=xml"));
// Fetch the next page
httpGet = new HttpGet(linkNext);
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.XML);
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
assertThat(linkNext, containsString("_format=xml"));
// Fetch the next page
httpGet = new HttpGet(linkNext);
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.XML);
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
assertThat(linkNext, containsString("_format=xml"));
}
@Test
public void testPagingPreservesEncodingNone() throws Exception {
HttpGet httpGet;
String linkNext;
Bundle bundle;
// Initial search
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar");
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON);
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
assertThat(linkNext, not(containsString("_format")));
// Fetch the next page
httpGet = new HttpGet(linkNext);
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON);
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
assertThat(linkNext, not(containsString("_format")));
// Fetch the next page
httpGet = new HttpGet(linkNext);
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON);
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
assertThat(linkNext, not(containsString("_format")));
// Fetch the next page
httpGet = new HttpGet(linkNext);
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON);
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
assertThat(linkNext, not(containsString("_format")));
}
@Test
public void testPagingPreservesEncodingNoneWithBrowserAcceptHeader() throws Exception {
HttpGet httpGet;
String linkNext;
Bundle bundle;
// Initial search
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar");
httpGet.addHeader(Constants.HEADER_ACCEPT, "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8");
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.XML);
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
assertThat(linkNext, not(containsString("_format")));
// Fetch the next page
httpGet = new HttpGet(linkNext);
httpGet.addHeader(Constants.HEADER_ACCEPT, "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8");
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.XML);
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
assertThat(linkNext, not(containsString("_format")));
// Fetch the next page
httpGet = new HttpGet(linkNext);
httpGet.addHeader(Constants.HEADER_ACCEPT, "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8");
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.XML);
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
assertThat(linkNext, not(containsString("_format")));
// Fetch the next page
httpGet = new HttpGet(linkNext);
httpGet.addHeader(Constants.HEADER_ACCEPT, "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8");
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.XML);
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
assertThat(linkNext, not(containsString("_format")));
}
private Bundle executeAndReturnLinkNext(HttpGet httpGet, EncodingEnum theExpectEncoding) throws IOException, ClientProtocolException {
CloseableHttpResponse status = ourClient.execute(httpGet);
Bundle bundle;
try {
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
EncodingEnum ct = EncodingEnum.forContentType(status.getEntity().getContentType().getValue().replaceAll(";.*", "").trim());
assertEquals(theExpectEncoding, ct);
bundle = ct.newParser(ourCtx).parseResource(Bundle.class, responseContent);
assertEquals(10, bundle.getEntry().size());
String linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
assertNotNull(linkNext);
} finally {
IOUtils.closeQuietly(status.getEntity().getContent());
}
return bundle;
}
@Test
public void testSearchWithPostAndInvalidParameters() throws Exception {
IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort);
LoggingInterceptor interceptor = new LoggingInterceptor();
interceptor.setLogRequestSummary(true);
interceptor.setLogRequestBody(true);
interceptor.setLogRequestHeaders(false);
interceptor.setLogResponseBody(false);
interceptor.setLogResponseHeaders(false);
interceptor.setLogResponseSummary(false);
client.registerInterceptor(interceptor);
try {
client
.search()
.forResource(Patient.class)
.where(new StringClientParam("foo").matches().value("bar"))
.encodedJson()
.prettyPrint()
.usingStyle(SearchStyleEnum.POST)
.returnBundle(org.hl7.fhir.r4.model.Bundle.class)
.execute();
fail();
} catch (InvalidRequestException e) {
assertThat(e.getMessage(), containsString("Invalid request: The FHIR endpoint on this server does not know how to handle POST operation[Patient/_search] with parameters [[_pretty, foo]]"));
}
}
@AfterClass
public static void afterClassClearContext() throws Exception {
ourServer.stop();
TestUtil.clearAllStaticFieldsForUnitTest();
}
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();
ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer(ourCtx);
servlet.setDefaultResponseEncoding(EncodingEnum.JSON);
servlet.setPagingProvider(new FifoMemoryPagingProvider(10));
servlet.setResourceProviders(patientProvider);
ServletHolder servletHolder = new ServletHolder(servlet);
proxyHandler.addServletWithMapping(servletHolder, "/*");
ourServer.setHandler(proxyHandler);
ourServer.start();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourClient = builder.build();
}
public static class DummyPatientResourceProvider implements IResourceProvider {
@Override
public Class<? extends IBaseResource> getResourceType() {
return Patient.class;
}
@SuppressWarnings("rawtypes")
@Search()
public List search(
@RequiredParam(name = Patient.SP_IDENTIFIER) TokenAndListParam theIdentifiers) {
ourLastMethod = "search";
ourIdentifiers = theIdentifiers;
ArrayList<Patient> retVal = new ArrayList<Patient>();
for (int i = 0; i < 200; i++) {
Patient patient = new Patient();
patient.addName(new HumanName().setFamily("FAMILY"));
patient.getIdElement().setValue("Patient/" + i);
retVal.add((Patient) patient);
}
return retVal;
}
}
}

View File

@ -0,0 +1,130 @@
package ca.uhn.fhir.rest.server;
import static org.junit.Assert.assertEquals;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.HumanName;
import org.hl7.fhir.r4.model.Patient;
import org.junit.*;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.annotation.Sort;
import ca.uhn.fhir.rest.api.SortOrderEnum;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
public class SearchSortR4Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forR4();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchSortR4Test.class);
private static int ourPort;
private static Server ourServer;
private static String ourLastMethod;
private static SortSpec ourLastSortSpec;
@Before
public void before() {
ourLastMethod = null;
ourLastSortSpec = null;
}
@Test
public void testSearch() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_sort=param1,-param2,param3,-param4");
CloseableHttpResponse status = ourClient.execute(httpGet);
try {
String responseContent = IOUtils.toString(status.getEntity().getContent());
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals("search", ourLastMethod);
assertEquals("param1", ourLastSortSpec.getParamName());
assertEquals(SortOrderEnum.ASC, ourLastSortSpec.getOrder());
assertEquals("param2", ourLastSortSpec.getChain().getParamName());
assertEquals(SortOrderEnum.DESC, ourLastSortSpec.getChain().getOrder());
assertEquals("param3", ourLastSortSpec.getChain().getChain().getParamName());
assertEquals(SortOrderEnum.ASC, ourLastSortSpec.getChain().getChain().getOrder());
assertEquals("param4", ourLastSortSpec.getChain().getChain().getChain().getParamName());
assertEquals(SortOrderEnum.DESC, ourLastSortSpec.getChain().getChain().getChain().getOrder());
} finally {
IOUtils.closeQuietly(status.getEntity().getContent());
}
}
@AfterClass
public static void afterClassClearContext() throws Exception {
ourServer.stop();
TestUtil.clearAllStaticFieldsForUnitTest();
}
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();
ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer(ourCtx);
servlet.setPagingProvider(new FifoMemoryPagingProvider(10));
servlet.setResourceProviders(patientProvider);
ServletHolder servletHolder = new ServletHolder(servlet);
proxyHandler.addServletWithMapping(servletHolder, "/*");
ourServer.setHandler(proxyHandler);
ourServer.start();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourClient = builder.build();
}
public static class DummyPatientResourceProvider implements IResourceProvider {
@Override
public Class<? extends IBaseResource> getResourceType() {
return Patient.class;
}
//@formatter:off
@SuppressWarnings("rawtypes")
@Search()
public List search(
@Sort SortSpec theSortSpec
) {
ourLastMethod = "search";
ourLastSortSpec = theSortSpec;
ArrayList<Patient> retVal = new ArrayList<Patient>();
for (int i = 1; i < 100; i++) {
retVal.add((Patient) new Patient().addName(new HumanName().setFamily("FAMILY")).setId("" + i));
}
return retVal;
}
//@formatter:on
}
}

View File

@ -0,0 +1,395 @@
package ca.uhn.fhir.rest.server;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.*;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.*;
import org.junit.*;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.client.MyPatientWithExtensions;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
public class ServerMimetypeR4Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forR4();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerMimetypeR4Test.class);
private static int ourPort;
private static Server ourServer;
@Test
public void testConformanceMetadataUsesNewMimetypes() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/metadata");
CloseableHttpResponse status = ourClient.execute(httpGet);
try {
String content = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
CapabilityStatement conf = ourCtx.newXmlParser().parseResource(CapabilityStatement.class, content);
List<String> strings = toStrings(conf.getFormat());
assertThat(strings, contains(Constants.CT_FHIR_XML_NEW, Constants.CT_FHIR_JSON_NEW));
} finally {
status.close();
}
}
private List<String> toStrings(List<CodeType> theFormat) {
ArrayList<String> retVal = new ArrayList<String>();
for (CodeType next : theFormat) {
retVal.add(next.asStringValue());
}
return retVal;
}
@Test
public void testCreateWithXmlLegacyNoAcceptHeader() throws Exception {
Patient p = new Patient();
p.addName().setFamily("FAMILY");
String enc = ourCtx.newXmlParser().encodeResourceToString(p);
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_XML + "; charset=utf-8")));
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(201, status.getStatusLine().getStatusCode());
assertEquals(Constants.CT_FHIR_XML, status.getFirstHeader("content-type").getValue().replaceAll(";.*", ""));
assertEquals("<OperationOutcome xmlns=\"http://hl7.org/fhir\"><issue><diagnostics value=\"FAMILY\"/></issue></OperationOutcome>", responseContent);
}
@Test
public void testHttpTraceNotEnabled() throws Exception {
HttpTrace req = new HttpTrace("http://localhost:" + ourPort + "/Patient");
CloseableHttpResponse status = ourClient.execute(req);
try {
ourLog.info(status.toString());
assertEquals(400, status.getStatusLine().getStatusCode());
} finally {
IOUtils.closeQuietly(status.getEntity().getContent());
}
}
@Test
public void testHttpTrackNotEnabled() throws Exception {
HttpRequestBase req = new HttpRequestBase() {
@Override
public String getMethod() {
return "TRACK";
}};
req.setURI(new URI("http://localhost:" + ourPort + "/Patient"));
CloseableHttpResponse status = ourClient.execute(req);
try {
ourLog.info(status.toString());
assertEquals(400, status.getStatusLine().getStatusCode());
} finally {
IOUtils.closeQuietly(status.getEntity().getContent());
}
}
@Test
public void testCreateWithXmlNewNoAcceptHeader() throws Exception {
Patient p = new Patient();
p.addName().setFamily("FAMILY");
String enc = ourCtx.newXmlParser().encodeResourceToString(p);
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_XML_NEW + "; charset=utf-8")));
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(201, status.getStatusLine().getStatusCode());
assertEquals(Constants.CT_FHIR_XML_NEW, status.getFirstHeader("content-type").getValue().replaceAll(";.*", ""));
assertEquals("<OperationOutcome xmlns=\"http://hl7.org/fhir\"><issue><diagnostics value=\"FAMILY\"/></issue></OperationOutcome>", responseContent);
}
@Test
public void testCreateWithXmlNewWithAcceptHeader() throws Exception {
Patient p = new Patient();
p.addName().setFamily("FAMILY");
String enc = ourCtx.newXmlParser().encodeResourceToString(p);
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_XML + "; charset=utf-8")));
httpPost.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_XML_NEW);
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(201, status.getStatusLine().getStatusCode());
assertEquals(Constants.CT_FHIR_XML_NEW, status.getFirstHeader("content-type").getValue().replaceAll(";.*", ""));
assertEquals("<OperationOutcome xmlns=\"http://hl7.org/fhir\"><issue><diagnostics value=\"FAMILY\"/></issue></OperationOutcome>", responseContent);
}
@Test
public void testCreateWithJsonLegacyNoAcceptHeader() throws Exception {
Patient p = new Patient();
p.addName().setFamily("FAMILY");
String enc = ourCtx.newJsonParser().encodeResourceToString(p);
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_JSON + "; charset=utf-8")));
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(201, status.getStatusLine().getStatusCode());
assertEquals(Constants.CT_FHIR_JSON, status.getFirstHeader("content-type").getValue().replaceAll(";.*", ""));
assertEquals("{\"resourceType\":\"OperationOutcome\",\"issue\":[{\"diagnostics\":\"FAMILY\"}]}", responseContent);
}
@Test
public void testCreateWithJsonNewNoAcceptHeader() throws Exception {
Patient p = new Patient();
p.addName().setFamily("FAMILY");
String enc = ourCtx.newJsonParser().encodeResourceToString(p);
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_JSON_NEW + "; charset=utf-8")));
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(201, status.getStatusLine().getStatusCode());
assertEquals(Constants.CT_FHIR_JSON_NEW, status.getFirstHeader("content-type").getValue().replaceAll(";.*", ""));
assertEquals("{\"resourceType\":\"OperationOutcome\",\"issue\":[{\"diagnostics\":\"FAMILY\"}]}", responseContent);
}
@Test
public void testCreateWithJsonNewWithAcceptHeader() throws Exception {
Patient p = new Patient();
p.addName().setFamily("FAMILY");
String enc = ourCtx.newJsonParser().encodeResourceToString(p);
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_JSON + "; charset=utf-8")));
httpPost.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_JSON_NEW);
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(201, status.getStatusLine().getStatusCode());
assertEquals(Constants.CT_FHIR_JSON_NEW, status.getFirstHeader("content-type").getValue().replaceAll(";.*", ""));
assertEquals("{\"resourceType\":\"OperationOutcome\",\"issue\":[{\"diagnostics\":\"FAMILY\"}]}", responseContent);
}
@Test
public void testSearchWithFormatXmlSimple() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=xml");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertThat(responseContent, containsString("<Patient xmlns=\"http://hl7.org/fhir\">"));
assertThat(responseContent, not(containsString("http://hl7.org/fhir/")));
assertEquals(Constants.CT_FHIR_XML_NEW, status.getFirstHeader("content-type").getValue().replaceAll(";.*", ""));
}
@Test
public void testSearchWithFormatXmlLegacy() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=" + Constants.CT_FHIR_XML);
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertThat(responseContent, containsString("<Patient xmlns=\"http://hl7.org/fhir\">"));
assertThat(responseContent, not(containsString("http://hl7.org/fhir/")));
assertEquals(Constants.CT_FHIR_XML, status.getFirstHeader("content-type").getValue().replaceAll(";.*", ""));
}
@Test
public void testSearchWithFormatXmlNew() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=" + Constants.CT_FHIR_XML_NEW);
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertThat(responseContent, containsString("<Patient xmlns=\"http://hl7.org/fhir\">"));
assertThat(responseContent, not(containsString("http://hl7.org/fhir/")));
assertEquals(Constants.CT_FHIR_XML_NEW, status.getFirstHeader("content-type").getValue().replaceAll(";.*", ""));
}
@Test
public void testSearchWithFormatJsonSimple() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=json");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertThat(responseContent, containsString("\"resourceType\""));
assertEquals(Constants.CT_FHIR_JSON_NEW, status.getFirstHeader("content-type").getValue().replaceAll(";.*", ""));
}
@Test
public void testSearchWithFormatJsonLegacy() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=" + Constants.CT_FHIR_JSON);
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertThat(responseContent, containsString("\"resourceType\""));
assertEquals(Constants.CT_FHIR_JSON, status.getFirstHeader("content-type").getValue().replaceAll(";.*", ""));
}
@Test
public void testSearchWithFormatJsonNew() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=" + Constants.CT_FHIR_JSON_NEW);
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertThat(responseContent, containsString("\"resourceType\""));
assertEquals(Constants.CT_FHIR_JSON_NEW, status.getFirstHeader("content-type").getValue().replaceAll(";.*", ""));
}
@AfterClass
public static void afterClassClearContext() throws Exception {
ourServer.stop();
TestUtil.clearAllStaticFieldsForUnitTest();
}
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
PatientProvider patientProvider = new PatientProvider();
ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer(ourCtx);
servlet.setResourceProviders(patientProvider);
ServletHolder servletHolder = new ServletHolder(servlet);
proxyHandler.addServletWithMapping(servletHolder, "/*");
ourServer.setHandler(proxyHandler);
ourServer.start();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourClient = builder.build();
}
public static class PatientProvider implements IResourceProvider {
@Create()
public MethodOutcome create(@ResourceParam Patient theIdParam) {
OperationOutcome oo = new OperationOutcome();
oo.addIssue().setDiagnostics(theIdParam.getNameFirstRep().getFamily());
return new MethodOutcome(new IdType("Patient", "1"), true).setOperationOutcome(oo);
}
@Override
public Class<Patient> getResourceType() {
return Patient.class;
}
@Read()
public MyPatientWithExtensions read(@IdParam IdType theIdParam) {
MyPatientWithExtensions p0 = new MyPatientWithExtensions();
p0.setId(theIdParam);
p0.setDateExt(new DateType("2011-01-01"));
return p0;
}
@Search
public List<IBaseResource> search() {
ArrayList<IBaseResource> retVal = new ArrayList<IBaseResource>();
MyPatientWithExtensions p0 = new MyPatientWithExtensions();
p0.setId(new IdType("Patient/0"));
p0.setDateExt(new DateType("2011-01-01"));
retVal.add(p0);
Patient p1 = new Patient();
p1.setId(new IdType("Patient/1"));
p1.addName().setFamily("The Family");
retVal.add(p1);
return retVal;
}
}
}

View File

@ -1,11 +0,0 @@
package org.hl7.fhir.r4.test;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class)
@SuiteClasses({ FluentPathTests.class, NarrativeGeneratorTests.class, /*ShexGeneratorTests.class, StructureMapTests.class, */ TurtleTests.class, GraphQLParserTests.class })
public class AllTests {
}

View File

@ -1,135 +0,0 @@
package org.hl7.fhir.r4.test;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.ParserConfigurationException;
import org.hl7.fhir.r4.context.SimpleWorkerContext;
import org.hl7.fhir.r4.formats.XmlParser;
import org.hl7.fhir.r4.model.Base;
import org.hl7.fhir.r4.model.BooleanType;
import org.hl7.fhir.r4.model.ExpressionNode;
import org.hl7.fhir.r4.model.PrimitiveType;
import org.hl7.fhir.r4.model.Resource;
import org.hl7.fhir.r4.test.support.TestingUtilities;
import org.hl7.fhir.r4.utils.FHIRPathEngine;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.xml.XMLUtil;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
import junit.framework.Assert;
@RunWith(Parameterized.class)
public class FluentPathTests {
private static FHIRPathEngine fp;
@Parameters(name = "{index}: file {0}")
public static Iterable<Object[]> data() throws ParserConfigurationException, SAXException, IOException {
Document dom = XMLUtil.parseFileToDom(Utilities.path(TestingUtilities. home(), "tests", "resources", "tests-fhir-r4.xml"));
List<Element> list = new ArrayList<Element>();
List<Element> groups = new ArrayList<Element>();
XMLUtil.getNamedChildren(dom.getDocumentElement(), "group", groups);
for (Element g : groups) {
XMLUtil.getNamedChildren(g, "test", list);
}
List<Object[]> objects = new ArrayList<Object[]>(list.size());
for (Element e : list) {
objects.add(new Object[] { getName(e), e });
}
return objects;
}
private static Object getName(Element e) {
String s = e.getAttribute("name");
if (Utilities.noString(s)) {
Element p = (Element) e.getParentNode();
int ndx = 0;
for (int i = 0; i < p.getChildNodes().getLength(); i++) {
Node c = p.getChildNodes().item(i);
if (c == e)
break;
else if (c instanceof Element)
ndx++;
}
s = p.getAttribute("name")+" - "+Integer.toString(ndx+1);
}
return s;
}
private final Element test;
private final String name;
public FluentPathTests(String name, Element e) {
this.name = name;
this.test = e;
}
@SuppressWarnings("deprecation")
@Test
public void test() throws FileNotFoundException, IOException, FHIRException, org.hl7.fhir.exceptions.FHIRException {
if (TestingUtilities.context == null)
TestingUtilities.context = SimpleWorkerContext.fromPack(Utilities.path(TestingUtilities.home(), "publish", "definitions.xml.zip"));
if (fp == null)
fp = new FHIRPathEngine(TestingUtilities.context);
String input = test.getAttribute("inputfile");
String expression = XMLUtil.getNamedChild(test, "expression").getTextContent();
boolean fail = "true".equals(XMLUtil.getNamedChild(test, "expression").getAttribute("invalid"));
Resource res = null;
List<Base> outcome = new ArrayList<Base>();
ExpressionNode node = fp.parse(expression);
try {
if (Utilities.noString(input))
fp.check(null, null, node);
else {
res = new XmlParser().parse(new FileInputStream(Utilities.path(TestingUtilities.home(), "publish", input)));
fp.check(res, res.getResourceType().toString(), res.getResourceType().toString(), node);
}
outcome = fp.evaluate(res, node);
Assert.assertTrue(String.format("Expected exception parsing %s", expression), !fail);
} catch (Exception e) {
Assert.assertTrue(String.format("Unexpected exception parsing %s: "+e.getMessage(), expression), fail);
}
if ("true".equals(test.getAttribute("predicate"))) {
boolean ok = fp.convertToBoolean(outcome);
outcome.clear();
outcome.add(new BooleanType(ok));
}
if (fp.hasLog())
System.out.println(fp.takeLog());
List<Element> expected = new ArrayList<Element>();
XMLUtil.getNamedChildren(test, "output", expected);
Assert.assertTrue(String.format("Expected %d objects but found %d", expected.size(), outcome.size()), outcome.size() == expected.size());
for (int i = 0; i < Math.min(outcome.size(), expected.size()); i++) {
String tn = expected.get(i).getAttribute("type");
if (!Utilities.noString(tn)) {
Assert.assertTrue(String.format("Outcome %d: Type should be %s but was %s", i, tn, outcome.get(i).fhirType()), tn.equals(outcome.get(i).fhirType()));
}
String v = expected.get(i).getTextContent();
if (!Utilities.noString(v)) {
Assert.assertTrue(String.format("Outcome %d: Value should be a primitive type but was %s", i, outcome.get(i).fhirType()), outcome.get(i) instanceof PrimitiveType);
Assert.assertTrue(String.format("Outcome %d: Value should be %s but was %s", i, v, outcome.get(i).toString()), v.equals(((PrimitiveType)outcome.get(i)).asStringValue()));
}
}
}
}

View File

@ -1,200 +0,0 @@
package org.hl7.fhir.r4.test;
import static org.junit.Assert.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.ParserConfigurationException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r4.context.SimpleWorkerContext;
import org.hl7.fhir.r4.formats.XmlParser;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.DomainResource;
import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.r4.model.Resource;
import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.r4.model.Bundle.BundleLinkComponent;
import org.hl7.fhir.r4.model.Bundle.SearchEntryMode;
import org.hl7.fhir.r4.test.support.TestingUtilities;
import org.hl7.fhir.r4.utils.GraphQLEngine;
import org.hl7.fhir.r4.utils.GraphQLEngine.IGraphQLStorageServices;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.graphql.Argument;
import org.hl7.fhir.utilities.graphql.EGraphEngine;
import org.hl7.fhir.utilities.graphql.EGraphQLException;
import org.hl7.fhir.utilities.graphql.NameValue;
import org.hl7.fhir.utilities.graphql.Package;
import org.hl7.fhir.utilities.graphql.Parser;
import org.hl7.fhir.utilities.xml.XMLUtil;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
@RunWith(Parameterized.class)
public class GraphQLEngineTests implements IGraphQLStorageServices {
@Parameters(name = "{index}: {0}")
public static Iterable<Object[]> data() throws FileNotFoundException, IOException, ParserConfigurationException, SAXException {
Document tests = XMLUtil.parseFileToDom(Utilities.path(TestingUtilities.home(), "tests", "graphql", "manifest.xml"));
Element test = XMLUtil.getFirstChild(tests.getDocumentElement());
List<Object[]> objects = new ArrayList<Object[]>();
while (test != null && test.getNodeName().equals("test")) {
objects.add(new Object[] { test.getAttribute("name"), test.getAttribute("source"), test.getAttribute("output"),
test.getAttribute("context"), test.getAttribute("resource"), test.getAttribute("operation")} );
test = XMLUtil.getNextSibling(test);
}
return objects;
}
private final String name;
private String source;
private String output;
private String context;
private String resource;
private String operation;
public GraphQLEngineTests(String name, String source, String output, String context, String resource, String operation) {
this.name = name;
this.source = source;
this.output = output;
this.context = context;
this.resource = resource;
this.operation = operation;
}
@Test
public void test() throws Exception {
String filename = null;
if (!Utilities.noString(context)) {
String[] parts = context.split("/");
if (parts.length != 3)
throw new Exception("not done yet "+source+" "+output+" "+context);
if (!Utilities.noString(resource))
filename = Utilities.path(TestingUtilities.home(), "publish", resource+".xml");
else
filename = Utilities.path(TestingUtilities.home(), "publish", parts[0].toLowerCase()+"-"+parts[1].toLowerCase()+".xml");
}
if (TestingUtilities.context == null)
TestingUtilities.context = SimpleWorkerContext.fromPack(Utilities.path(TestingUtilities.home(), "publish", "definitions.xml.zip"));
GraphQLEngine gql = new GraphQLEngine(TestingUtilities.context);
gql.setServices(this);
if (!Utilities.noString(filename))
gql.setFocus(new XmlParser().parse(new FileInputStream(filename)));
gql.setGraphQL(Parser.parseFile(Utilities.path(TestingUtilities.home(), "tests", "graphql", source)));
gql.getGraphQL().setOperationName(operation);
gql.getGraphQL().getVariables().add(new Argument("var", new NameValue("true")));
boolean ok = false;
String msg = null;
try {
gql.execute();
ok = true;
} catch (Exception e) {
if (!output.equals("$error"))
e.printStackTrace();
ok = false;
msg = e.getMessage();
}
if (ok) {
Assert.assertTrue("Expected to fail, but didn't", !output.equals("$error"));
StringBuilder str = new StringBuilder();
gql.getOutput().write(str, 0);
TextFile.stringToFile(str.toString(), Utilities.path(TestingUtilities.home(), "tests", "graphql", output+".out"));
msg = TestingUtilities.checkJsonIsSame(Utilities.path(TestingUtilities.home(), "tests", "graphql", output+".out"),Utilities.path(TestingUtilities.home(), "tests", "graphql", output));
Assert.assertTrue(msg, Utilities.noString(msg));
}
else
Assert.assertTrue("Error, but proper output was expected ("+msg+")", output.equals("$error"));
}
@Override
public Resource lookup(Object appInfo, String type, String id) throws FHIRException {
try {
String filename = Utilities.path(TestingUtilities.home(), "publish", type.toLowerCase()+'-'+id.toLowerCase()+".xml");
if (new File(filename).exists())
return new XmlParser().parse(new FileInputStream(filename));
else
return null;
} catch (Exception e) {
throw new FHIRException(e);
}
}
@Override
public ReferenceResolution lookup(Object appInfo, Resource context, Reference reference) throws FHIRException {
try {
if (reference.getReference().startsWith("#")) {
if (!(context instanceof DomainResource))
return null;
for (Resource r : ((DomainResource)context).getContained()) {
if (('#'+r.getId()).equals(reference.getReference())) {
return new ReferenceResolution(context, r);
}
}
} else {
String[] parts = reference.getReference().split("/");
String filename = Utilities.path(TestingUtilities.home(), "publish", parts[0].toLowerCase()+'-'+parts[1].toLowerCase()+".xml");
if (new File(filename).exists())
return new ReferenceResolution(null, new XmlParser().parse(new FileInputStream(filename)));
}
return null;
} catch (Exception e) {
throw new FHIRException(e);
}
}
@Override
public void listResources(Object appInfo, String type, List<Argument> searchParams, List<Resource> matches) throws FHIRException {
try {
if (type.equals("Condition"))
matches.add(new XmlParser().parse(new FileInputStream(Utilities.path(TestingUtilities.home(), "publish", "condition-example.xml"))));
else if (type.equals("Patient")) {
matches.add(new XmlParser().parse(new FileInputStream(Utilities.path(TestingUtilities.home(), "publish", "patient-example.xml"))));
matches.add(new XmlParser().parse(new FileInputStream(Utilities.path(TestingUtilities.home(), "publish", "patient-example-xds.xml"))));
}
} catch (Exception e) {
throw new FHIRException(e);
}
}
@Override
public Bundle search(Object appInfo, String type, List<Argument> searchParams) throws FHIRException {
try {
Bundle bnd = new Bundle();
BundleLinkComponent bl = bnd.addLink();
bl.setRelation("next");
bl.setUrl("http://test.fhir.org/r3/Patient?_format=text/xhtml&search-id=77c97e03-8a6c-415f-a63d-11c80cf73f&&active=true&_sort=_id&search-offset=50&_count=50");
bl = bnd.addLink();
bl.setRelation("self");
bl.setUrl("http://test.fhir.org/r3/Patient?_format=text/xhtml&search-id=77c97e03-8a6c-415f-a63d-11c80cf73f&&active=true&_sort=_id&search-offset=0&_count=50");
BundleEntryComponent be = bnd.addEntry();
be.setFullUrl("http://hl7.org/fhir/Patient/example");
be.setResource(new XmlParser().parse(new FileInputStream(Utilities.path(TestingUtilities.home(), "publish", "patient-example.xml"))));
be = bnd.addEntry();
be.setFullUrl("http://hl7.org/fhir/Patient/example");
be.setResource(new XmlParser().parse(new FileInputStream(Utilities.path(TestingUtilities.home(), "publish", "patient-example-xds.xml"))));
be.getSearch().setScore(0.5);
be.getSearch().setMode(SearchEntryMode.MATCH);
bnd.setTotal(50);
return bnd;
} catch (Exception e) {
throw new FHIRException(e);
}
}
}

View File

@ -1,68 +0,0 @@
package org.hl7.fhir.r4.test;
import static org.junit.Assert.*;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.ParserConfigurationException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r4.test.support.TestingUtilities;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.graphql.EGraphEngine;
import org.hl7.fhir.utilities.graphql.EGraphQLException;
import org.hl7.fhir.utilities.graphql.Package;
import org.hl7.fhir.utilities.graphql.Parser;
import org.hl7.fhir.utilities.xml.XMLUtil;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
@RunWith(Parameterized.class)
public class GraphQLParserTests {
@Parameters(name = "{index}: {0}")
public static Iterable<Object[]> data() throws FileNotFoundException, IOException {
String src = TextFile.fileToString(Utilities.path(TestingUtilities.home(), "tests", "graphql", "parser-tests.gql"));
String[] tests = src.split("###");
int i = 0;
for (String s : tests)
if (!Utilities.noString(s.trim()))
i++;
List<Object[]> objects = new ArrayList<Object[]>(i);
i = 0;
for (String s : tests) {
if (!Utilities.noString(s.trim())) {
int l = s.indexOf('\r');
objects.add(new Object[] { s.substring(0, l), s.substring(l+2).trim()});
}
}
return objects;
}
private final String test;
private final String name;
public GraphQLParserTests(String name, String test) {
this.name = name;
this.test = test;
}
@Test
public void test() throws IOException, EGraphQLException, EGraphEngine {
Package doc = Parser.parse(test);
Assert.assertTrue(doc != null);
}
}

View File

@ -1,52 +0,0 @@
package org.hl7.fhir.r4.test;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import org.hl7.fhir.r4.context.SimpleWorkerContext;
import org.hl7.fhir.r4.formats.XmlParser;
import org.hl7.fhir.r4.model.DomainResource;
import org.hl7.fhir.r4.test.support.TestingUtilities;
import org.hl7.fhir.r4.utils.EOperationOutcome;
import org.hl7.fhir.r4.utils.NarrativeGenerator;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.exceptions.FHIRException;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.xmlpull.v1.XmlPullParserException;
public class NarrativeGeneratorTests {
private NarrativeGenerator gen;
@Before
public void setUp() throws FileNotFoundException, IOException, FHIRException {
if (TestingUtilities.context == null)
TestingUtilities.context = SimpleWorkerContext.fromPack(Utilities.path(TestingUtilities.home(), "publish", "definitions.xml.zip"));
if (gen == null)
gen = new NarrativeGenerator("", null, TestingUtilities.context);
}
@After
public void tearDown() {
}
@Test
public void test() throws FileNotFoundException, IOException, XmlPullParserException, EOperationOutcome, FHIRException {
process(Utilities.path(TestingUtilities.home(), "source", "questionnaireresponse", "questionnaireresponse-example-f201-lifelines.xml"));
}
private void process(String path) throws FileNotFoundException, IOException, XmlPullParserException, EOperationOutcome, FHIRException {
XmlParser p = new XmlParser();
DomainResource r = (DomainResource) p.parse(new FileInputStream(path));
gen.generate(r);
FileOutputStream s = new FileOutputStream(Utilities.path(TestingUtilities.temp(), "gen.xml"));
new XmlParser().compose(s, r, true);
s.close();
}
}

View File

@ -1,940 +0,0 @@
package org.hl7.fhir.r4.test;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.hl7.fhir.r4.conformance.ProfileComparer;
import org.hl7.fhir.r4.conformance.ProfileComparer.ProfileComparison;
import org.hl7.fhir.r4.conformance.ProfileUtilities;
import org.hl7.fhir.r4.context.SimpleWorkerContext;
import org.hl7.fhir.r4.formats.IParser.OutputStyle;
import org.hl7.fhir.r4.formats.XmlParser;
import org.hl7.fhir.r4.model.Base;
import org.hl7.fhir.r4.model.CodeType;
import org.hl7.fhir.r4.model.ElementDefinition;
import org.hl7.fhir.r4.model.ElementDefinition.DiscriminatorType;
import org.hl7.fhir.r4.model.ElementDefinition.SlicingRules;
import org.hl7.fhir.r4.model.Enumerations.BindingStrength;
import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.r4.model.StructureDefinition;
import org.hl7.fhir.r4.model.StructureDefinition.TypeDerivationRule;
import org.hl7.fhir.r4.test.support.TestingUtilities;
import org.hl7.fhir.r4.utils.EOperationOutcome;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.utilities.CSFile;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import static org.junit.Assert.*;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r4.utils.SnomedExpressions;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class ProfileUtilitiesTests {
// private String root;
// private SimpleWorkerContext context;
// private ProfileComparer comp;
//
//// public ProfileUtilitiesTests(String root) {
//// super();
//// this.root = root;
//// }
//
//// public ProfileUtilitiesTests() {
//// super();
//// root = TestingUtilities.home();
////
//// }
//
// public static void main(String[] args) throws EOperationOutcome, Exception {
// // new ProfileUtilitiesTests().execute(args);
// ProfileUtilitiesTests put = new ProfileUtilitiesTests();
// put.root = "C:\\work\\org.hl7.fhir\\build\\publish";
// put.testSnapshotGeneration();
// // StructureDefinition p = (StructureDefinition) new XmlParser().parse(new FileInputStream("C:\\work\\org.hl7.fhir\\build\\publish\\lipid-report-cholesterol.profile.xml"));
// // new ProfileUtilities(context, messages, null).generateSchematrons(new FileOutputStream("c:\\temp\\test.sch"), p);
// }
//
// public void execute(String[] args) throws FileNotFoundException, IOException, FHIRException {
// System.out.println("loading context");
// context = SimpleWorkerContext.fromPack(Utilities.path(root, "validation.zip"));
// comp = new ProfileComparer(context);
//
// compare("patient-daf-dafpatient.profile.xml", "patient-qicore-qicore-patient.profile.xml");
// compare("encounter-daf-dafencounter.profile.xml", "encounter-qicore-qicore-encounter.profile.xml");
// compare("substance-daf-dafsubstance.profile.xml", "substance-qicore-qicore-substance.profile.xml");
// compare("medication-daf-dafmedication.profile.xml", "medication-qicore-qicore-medication.profile.xml");
// compare("procedure-daf-dafprocedure.profile.xml", "procedure-qicore-qicore-procedure.profile.xml");
// compare("familymemberhistory-daf-daffamilymemberhistory.profile.xml", "familymemberhistory-qicore-qicore-familymemberhistory.profile.xml");
// compare("immunization-daf-dafimmunization.profile.xml", "immunization-qicore-qicore-immunization.profile.xml");
// compare("condition-daf-dafcondition.profile.xml", "condition-qicore-qicore-condition.profile.xml");
// compare("allergyintolerance-daf-dafallergyintolerance.profile.xml", "allergyintolerance-qicore-qicore-allergyintolerance.profile.xml");
// compare("medicationadministration-daf-dafmedicationadministration.profile.xml", "medicationadministration-qicore-qicore-medicationadministration.profile.xml");
// compare("medicationdispense-daf-dafmedicationdispense.profile.xml", "medicationdispense-qicore-qicore-medicationdispense.profile.xml");
// compare("medicationprescription-daf-dafmedicationprescription.profile.xml", "medicationprescription-qicore-qicore-medicationprescription.profile.xml");
// compare("medicationstatement-daf-dafmedicationstatement.profile.xml", "medicationstatement-qicore-qicore-medicationstatement.profile.xml");
// compare("observation-daf-smokingstatus-dafsmokingstatus.profile.xml", "observation-qicore-qicore-observation.profile.xml");
// compare("observation-daf-vitalsigns-dafvitalsigns.profile.xml", "observation-qicore-qicore-observation.profile.xml");
//// compare("observation-daf-results-dafresultobs.profile.xml", "observation-qicore-qicore-observation.profile.xml");
//// compare("diagnosticorder-daf-dafdiagnosticorder.profile.xml", "diagnosticorder-qicore-qicore-diagnosticorder.profile.xml");
//// compare("diagnosticreport-daf-dafdiagnosticreport.profile.xml", "diagnosticreport-qicore-qicore-diagnosticreport.profile.xml");
//
// System.out.println("processing output");
// for (ProfileComparison outcome : comp.getComparisons()) {
// if (outcome.getSubset() != null)
// new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream("C:\\temp\\intersection-"+outcome.getId()+".xml"), outcome.getSubset());
// if (outcome.getSuperset() != null)
// new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream("C:\\temp\\union-"+outcome.getId()+".xml"), outcome.getSuperset());
//
// System.out.println("\r\n"+outcome.getId()+": Comparison of "+outcome.getLeft().getUrl()+" and "+outcome.getRight().getUrl());
// for (ValidationMessage vm : outcome.getMessages())
// if (vm.getLevel() == IssueSeverity.INFORMATION)
// System.out.println(vm.summary());
// for (ValidationMessage vm : outcome.getMessages())
// if (vm.getLevel() == IssueSeverity.WARNING)
// System.out.println(vm.summary());
// for (ValidationMessage vm : outcome.getMessages())
// if (vm.getLevel() == IssueSeverity.ERROR)
// System.out.println(vm.summary());
// for (ValidationMessage vm : outcome.getMessages())
// if (vm.getLevel() == IssueSeverity.FATAL)
// System.out.println(vm.summary());
// System.out.println("done. "+Integer.toString(outcome.getMessages().size())+" messages");
// System.out.println("=================================================================");
// }
// }
//
// private void compare(String fn1, String fn2) throws FHIRFormatError, FileNotFoundException, IOException, DefinitionException {
// System.out.println("Compare "+fn1+" to "+fn2);
// System.out.println(" .. load");
// StructureDefinition left = (StructureDefinition) new XmlParser().parse(new FileInputStream(Utilities.path(root, fn1)));
// StructureDefinition right = (StructureDefinition) new XmlParser().parse(new FileInputStream(Utilities.path(root, fn2)));
// System.out.println(" .. compare");
// comp.compareProfiles(left, right);
//
// }
//
// public void testSnapshotGeneration() throws EOperationOutcome, Exception {
// System.out.println("Loading");
// context = SimpleWorkerContext.fromPack(Utilities.path(root, "definitions.xml.zip"));
// System.out.println("Loaded "+Integer.toString(context.totalCount())+" resources");
//
// // simple tests
// testSimple();
// testSimple2();
// testCardinalityChange();
// testDocumentationAppend();
// textTypeNarrowing1();
// textTypeNarrowing2();
// testMapping();
//
// // ok, now we test walking into a new type:
// testTypeWalk();
// // todo: testTypeWalk2();
//
// // slicing tests
// testSlicingSimple();
// testSlicingExtension(false);
// testSlicingExtension(true);
// testSlicingExtensionComplex(true);
// testSlicingExtensionComplex(false);
// testSlicingTask8742();
// System.out.println("Success");
// }
//
// /**
// * This is simple: we just create an empty differential, generate the snapshot, and then insist it must match the base
// *
// * @param context2
// * @
// * @throws EOperationOutcome
// */
@Test
public void testSimple() throws FHIRException, FileNotFoundException, IOException {
if (TestingUtilities.context == null)
TestingUtilities.context = SimpleWorkerContext.fromPack(Utilities.path(TestingUtilities.home(), "publish", "definitions.xml.zip"));
StructureDefinition focus = new StructureDefinition();
StructureDefinition base = TestingUtilities.context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/Patient").copy();
focus.setUrl(Utilities.makeUuidUrn());
focus.setBaseDefinition(base.getUrl());
focus.setType("Patient");
focus.setDerivation(TypeDerivationRule.CONSTRAINT);
List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
new ProfileUtilities(TestingUtilities.context, messages, null).generateSnapshot(base, focus, focus.getUrl(), "Simple Test");
boolean ok = base.getSnapshot().getElement().size() == focus.getSnapshot().getElement().size();
for (int i = 0; i < base.getSnapshot().getElement().size(); i++) {
ElementDefinition b = base.getSnapshot().getElement().get(i);
ElementDefinition f = focus.getSnapshot().getElement().get(i);
if (ok) {
if (!f.hasBase())
ok = false;
else if (!b.getPath().equals(f.getPath()))
ok = false;
else {
b.setBase(null);
f.setBase(null);
ok = Base.compareDeep(b, f, true);
}
}
}
if (!ok) {
compareXml(base, focus);
throw new FHIRException("Snap shot generation simple test failed");
} else
System.out.println("Snap shot generation simple test passed");
}
//
// /**
// * This is simple: we just create an empty differential, generate the snapshot, and then insist it must match the base. for a different resource with recursion
// *
// * @param context2
// * @
// * @throws EOperationOutcome
// */
@Test
public void testSimple2() throws EOperationOutcome, Exception {
if (TestingUtilities.context == null)
TestingUtilities.context = SimpleWorkerContext.fromPack(Utilities.path(TestingUtilities.home(), "publish", "definitions.xml.zip"));
StructureDefinition focus = new StructureDefinition();
StructureDefinition base = TestingUtilities.context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/ValueSet").copy();
focus.setUrl(Utilities.makeUuidUrn());
focus.setBaseDefinition(base.getUrl());
focus.setType(base.getType());
focus.setDerivation(TypeDerivationRule.CONSTRAINT);
List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
new ProfileUtilities(TestingUtilities.context, messages, null).generateSnapshot(base, focus, focus.getUrl(), "Simple Test" );
boolean ok = base.getSnapshot().getElement().size() == focus.getSnapshot().getElement().size();
for (int i = 0; i < base.getSnapshot().getElement().size(); i++) {
if (ok) {
ElementDefinition b = base.getSnapshot().getElement().get(i);
ElementDefinition f = focus.getSnapshot().getElement().get(i);
if (!f.hasBase() || !b.getPath().equals(f.getBase().getPath()))
ok = false;
else {
f.setBase(null);
ok = Base.compareDeep(b, f, true);
}
}
}
if (!ok) {
compareXml(base, focus);
throw new FHIRException("Snap shot generation simple test failed");
} else
System.out.println("Snap shot generation simple test passed");
}
// /**
// * Change one cardinality.
// *
// * @param context2
// * @
// * @throws EOperationOutcome
// */
// private void testCardinalityChange() throws EOperationOutcome, Exception {
// StructureDefinition focus = new StructureDefinition();
// StructureDefinition base = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/Patient").copy();
// focus.setUrl(Utilities.makeUuidUrn());
// focus.setBaseDefinition(base.getUrl());
// focus.setType(base.getType());
// focus.setDerivation(TypeDerivationRule.CONSTRAINT);
// ElementDefinition id = focus.getDifferential().addElement();
// id.setPath("Patient.identifier");
// id.setMin(1);
// List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
// new ProfileUtilities(context, messages, null).generateSnapshot(base, focus, focus.getUrl(), "Simple Test" );
//
// boolean ok = base.getSnapshot().getElement().size() == focus.getSnapshot().getElement().size();
// for (int i = 0; i < base.getSnapshot().getElement().size(); i++) {
// if (ok) {
// ElementDefinition b = base.getSnapshot().getElement().get(i);
// ElementDefinition f = focus.getSnapshot().getElement().get(i);
// if (!f.hasBase() || !b.getPath().equals(f.getBase().getPath()))
// ok = false;
// else {
// f.setBase(null);
// if (f.getPath().equals("Patient.identifier")) {
// ok = f.getMin() == 1;
// if (ok)
// f.setMin(0);
// }
// ok = ok && Base.compareDeep(b, f, true);
// }
// }
// }
//
// if (!ok) {
// compareXml(base, focus);
// throw new FHIRException("Snap shot generation chenge cardinality test failed");
// } else
// System.out.println("Snap shot generation chenge cardinality test passed");
// }
//
// /**
// * check that documentation appending is working
// *
// * @param context2
// * @
// * @throws EOperationOutcome
// */
// private void testDocumentationAppend() throws EOperationOutcome, Exception {
// StructureDefinition focus = new StructureDefinition();
// StructureDefinition base = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/Patient").copy();
// focus.setUrl(Utilities.makeUuidUrn());
// focus.setBaseDefinition(base.getUrl());
// focus.setType(base.getType());
// focus.setDerivation(TypeDerivationRule.CONSTRAINT);
// ElementDefinition id = focus.getDifferential().addElement();
// id.setPath("Patient.identifier");
// id.setDefinition("... some more doco");
// List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
// new ProfileUtilities(context, messages, null).generateSnapshot(base, focus, focus.getUrl(), "Simple Test" );
//
// boolean ok = base.getSnapshot().getElement().size() == focus.getSnapshot().getElement().size();
// for (int i = 0; i < base.getSnapshot().getElement().size(); i++) {
// if (ok) {
// ElementDefinition b = base.getSnapshot().getElement().get(i);
// ElementDefinition f = focus.getSnapshot().getElement().get(i);
// if (!f.hasBase() || !b.getPath().equals(f.getBase().getPath()))
// ok = false;
// else {
// f.setBase(null);
// if (f.getPath().equals("Patient.identifier")) {
// ok = f.getDefinition().length() > b.getDefinition().length();
// if (ok) {
// f.setDefinition(null);
// b.setDefinition(null);
// }
// }
// ok = ok && Base.compareDeep(b, f, true);
// }
// }
// }
//
// if (!ok) {
// compareXml(base, focus);
// throw new FHIRException("Snap shot generation documentation append failed");
// } else
// System.out.println("Snap shot generation documentation append test passed");
// }
//
//
// /**
// * check that narrowing types is working
// * this one doesn't rename the path
// *
// * @param context2
// * @
// * @throws EOperationOutcome
// */
// private void textTypeNarrowing1() throws EOperationOutcome, Exception {
// StructureDefinition focus = new StructureDefinition();
// StructureDefinition base = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/Patient").copy();
// focus.setUrl(Utilities.makeUuidUrn());
// focus.setBaseDefinition(base.getUrl());
// focus.setType(base.getType());
// focus.setDerivation(TypeDerivationRule.CONSTRAINT);
// ElementDefinition id = focus.getDifferential().addElement();
// id.setPath("Patient.deceased[x]");
// id.addType().setCode("dateTime");
// List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
// new ProfileUtilities(context, messages, null).generateSnapshot(base, focus, focus.getUrl(), "Simple Test" );
//
// boolean ok = base.getSnapshot().getElement().size() == focus.getSnapshot().getElement().size();
// for (int i = 0; i < base.getSnapshot().getElement().size(); i++) {
// if (ok) {
// ElementDefinition b = base.getSnapshot().getElement().get(i);
// ElementDefinition f = focus.getSnapshot().getElement().get(i);
// if (!f.hasBase() || !b.getPath().equals(f.getBase().getPath()))
// ok = false;
// else {
// f.setBase(null);
// if (f.getPath().equals("Patient.deceasedDateTime")) {
// ok = f.getType().size() == 1 && f.getType().get(0).getCode().equals("dateTime");
// if (ok) {
// f.getType().clear();
// b.getType().clear();
// f.setPath(b.getPath());
// }
// }
// ok = ok && Base.compareDeep(b, f, true);
// }
// }
// }
//
// if (!ok) {
// compareXml(base, focus);
// throw new FHIRException("Snap shot generation narrow type 1 failed");
// } else
// System.out.println("Snap shot generation narrow type 1 test passed");
// }
//
// /**
// * check that narrowing types is working
// * this one renames the path
// *
// * @param context2
// * @
// * @throws EOperationOutcome
// */
// private void textTypeNarrowing2() throws EOperationOutcome, Exception {
// StructureDefinition focus = new StructureDefinition();
// StructureDefinition base = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/Patient").copy();
// focus.setUrl(Utilities.makeUuidUrn());
// focus.setBaseDefinition(base.getUrl());
// focus.setType(base.getType());
// focus.setDerivation(TypeDerivationRule.CONSTRAINT);
// ElementDefinition id = focus.getDifferential().addElement();
// id.setPath("Patient.deceasedDateTime");
// id.addType().setCode("dateTime");
// List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
// new ProfileUtilities(context, messages, null).generateSnapshot(base, focus, focus.getUrl(), "Simple Test" );
//
// boolean ok = base.getSnapshot().getElement().size() == focus.getSnapshot().getElement().size();
// for (int i = 0; i < base.getSnapshot().getElement().size(); i++) {
// if (ok) {
// ElementDefinition b = base.getSnapshot().getElement().get(i);
// ElementDefinition f = focus.getSnapshot().getElement().get(i);
// if (!f.hasBase() || !b.getPath().equals(f.getBase().getPath()))
// ok = false;
// else {
// f.setBase(null);
// if (f.getPath().equals("Patient.deceasedDateTime")) {
// ok = f.getType().size() == 1 && f.getType().get(0).getCode().equals("dateTime");
// if (ok) {
// f.getType().clear();
// b.getType().clear();
// f.setPath(b.getPath());
// }
// }
// ok = ok && Base.compareDeep(b, f, true);
// }
// }
// }
//
// if (!ok) {
// compareXml(base, focus);
// throw new FHIRException("Snap shot generation narrow type 2 failed");
// } else
// System.out.println("Snap shot generation narrow type 2 test passed");
// }
//
// /**
// * check that mapping resolution is working
// *
// * @param context2
// * @
// * @throws EOperationOutcome
// */
// private void testMapping() throws EOperationOutcome, Exception {
// StructureDefinition focus = new StructureDefinition();
// StructureDefinition base = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/Patient").copy();
// focus.setUrl(Utilities.makeUuidUrn());
// focus.setBaseDefinition(base.getUrl());
// focus.setType(base.getType());
// focus.setDerivation(TypeDerivationRule.CONSTRAINT);
// ElementDefinition id = focus.getDifferential().addElement();
// id.setPath("Patient.identifier");
// id.addMapping().setIdentity("rim").setMap("test");
// List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
// new ProfileUtilities(context, messages, null).generateSnapshot(base, focus, focus.getUrl(), "Simple Test" );
//
// boolean ok = base.getSnapshot().getElement().size() == focus.getSnapshot().getElement().size();
// for (int i = 0; i < base.getSnapshot().getElement().size(); i++) {
// if (ok) {
// ElementDefinition b = base.getSnapshot().getElement().get(i);
// ElementDefinition f = focus.getSnapshot().getElement().get(i);
// if (!f.hasBase() || !b.getPath().equals(f.getBase().getPath()))
// ok = false;
// else {
// f.setBase(null);
// if (f.getPath().equals("Patient.identifier")) {
// ok = f.getMapping().size() > b.getMapping().size();
// if (ok) {
// f.getMapping().clear();
// b.getMapping().clear();
// }
// }
// ok = ok && Base.compareDeep(b, f, true);
// }
// }
// }
//
// if (!ok) {
// compareXml(base, focus);
// throw new FHIRException("Snap shot generation mapping changes failed");
// } else
// System.out.println("Snap shot generation mapping changes test passed");
// }
//
// /**
// * Walking into a type
// *
// * @param context2
// * @
// * @throws EOperationOutcome
// */
// private void testTypeWalk() throws EOperationOutcome, Exception {
// StructureDefinition focus = new StructureDefinition();
// StructureDefinition base = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/Patient").copy();
// focus.setUrl(Utilities.makeUuidUrn());
// focus.setBaseDefinition(base.getUrl());
// focus.setType(base.getType());
// focus.setDerivation(TypeDerivationRule.CONSTRAINT);
// ElementDefinition id = focus.getDifferential().addElement();
// id.setPath("Patient.identifier");
// id.setMustSupport(true);
// id = focus.getDifferential().addElement();
// id.setPath("Patient.identifier.system");
// id.setMustSupport(true);
// List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
// new ProfileUtilities(context, messages, null).generateSnapshot(base, focus, focus.getUrl(), "Simple Test" );
//
// // the derived should be 8 longer
// boolean ok = base.getSnapshot().getElement().size() == focus.getSnapshot().getElement().size() - 8;
// for (int i = 0; i < base.getSnapshot().getElement().size(); i++) {
// if (ok) {
// ElementDefinition b = base.getSnapshot().getElement().get(i);
// ElementDefinition f = focus.getSnapshot().getElement().get(i <= 9 ? i : i + 8);
// if (!f.hasBase() || !b.getPath().equals(f.getBase().getPath()))
// ok = false;
// else {
// f.setBase(null);
// if (f.getPath().equals("Patient.identifier")) {
// ok = f.getMustSupport() && !b.getMustSupport();
// if (ok) {
// f.setMustSupportElement(null);
// }
// }
// ok = Base.compareDeep(b, f, true);
// }
// }
// }
//
// if (!ok) {
// compareXml(base, focus);
// throw new FHIRException("Snap shot generation simple test failed");
// } else
// System.out.println("Snap shot generation simple test passed");
// }
//
// /**
// * Walking into a type, without explicitly doing so
// *
// * note: this currently fails.
// *
// * @param context2
// * @
// * @throws EOperationOutcome
// */
// private void testTypeWalk2() throws EOperationOutcome, Exception {
// StructureDefinition focus = new StructureDefinition();
// StructureDefinition base = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/Patient").copy();
// focus.setUrl(Utilities.makeUuidUrn());
// focus.setBaseDefinition(base.getUrl());
// focus.setType(base.getType());
// focus.setDerivation(TypeDerivationRule.CONSTRAINT);
// ElementDefinition id = focus.getDifferential().addElement();
// id.setPath("Patient.identifier.system");
// id.setMustSupport(true);
// List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
// new ProfileUtilities(context, messages, null).generateSnapshot(base, focus, focus.getUrl(), "Simple Test" );
//
// // the derived should be 8 longer
// boolean ok = base.getSnapshot().getElement().size() == focus.getSnapshot().getElement().size() - 8;
// for (int i = 0; i < base.getSnapshot().getElement().size(); i++) {
// if (ok) {
// ElementDefinition b = base.getSnapshot().getElement().get(i);
// ElementDefinition f = focus.getSnapshot().getElement().get(i <= 9 ? i : i + 8);
// if (!f.hasBase() || !b.getPath().equals(f.getBase().getPath()))
// ok = false;
// else {
// f.setBase(null);
// if (f.getPath().equals("Patient.identifier")) {
// ok = f.getMustSupport() && !b.getMustSupport();
// if (ok) {
// f.setMustSupportElement(null);
// }
// }
// ok = Base.compareDeep(b, f, true);
// }
// }
// }
//
// if (!ok) {
// compareXml(base, focus);
// throw new FHIRException("Snap shot generation simple test failed");
// } else
// System.out.println("Snap shot generation simple test passed");
// }
//
//
// /**
// * we're going to slice Patient.identifier
// */
// private void testSlicingSimple() throws EOperationOutcome, Exception {
//
// StructureDefinition focus = new StructureDefinition();
// StructureDefinition base = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/Patient").copy();
// focus.setUrl(Utilities.makeUuidUrn());
// focus.setBaseDefinition(base.getUrl());
// focus.setType(base.getType());
// focus.setDerivation(TypeDerivationRule.CONSTRAINT);
//
// // set the slice up
// ElementDefinition id = focus.getDifferential().addElement();
// id.setPath("Patient.identifier");
// id.getSlicing().setOrdered(false).setRules(SlicingRules.OPEN).addDiscriminator().setPath("use").setType(DiscriminatorType.VALUE);
//
// // first slice:
// id = focus.getDifferential().addElement();
// id.setPath("Patient.identifier");
// id.setSliceName("name1");
// id = focus.getDifferential().addElement();
// id.setPath("Patient.identifier.use");
// id.setFixed(new CodeType("usual"));
//
// // second slice:
// id = focus.getDifferential().addElement();
// id.setPath("Patient.identifier");
// id.setSliceName("name2");
// id = focus.getDifferential().addElement();
// id.setPath("Patient.identifier.use");
// id.setFixed(new CodeType("official"));
//
//
// List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
// new ProfileUtilities(context, messages, null).generateSnapshot(base, focus, focus.getUrl(), "Simple Test" );
//
// // 18 different: identifier + 8 inner children * 2
// boolean ok = base.getSnapshot().getElement().size() == focus.getSnapshot().getElement().size() - 18;
// for (int i = 0; i < base.getSnapshot().getElement().size(); i++) {
// if (ok) {
// ElementDefinition b = base.getSnapshot().getElement().get(i);
// ElementDefinition f = focus.getSnapshot().getElement().get(i <= 9 ? i : i + 18);
// if (!f.hasBase() || !b.getPath().equals(f.getBase().getPath()))
// ok = false;
// else {
// f.setBase(null);
// if (f.getPath().equals("Patient.identifier")) {
// ok = f.hasSlicing();
// if (ok)
// f.setSlicing(null);
// }
// ok = Base.compareDeep(b, f, true);
// }
// }
// }
// // now, check that the slices we skipped are correct:
// for (int i = 10; i <= 18; i++) {
// if (ok) {
// ElementDefinition d1 = focus.getSnapshot().getElement().get(i);
// ElementDefinition d2 = focus.getSnapshot().getElement().get(i+9);
// if (d1.getPath().equals("Patient.identifier.use")) {
// ok = d1.hasFixed() && d2.hasFixed() && !Base.compareDeep(d1.getFixed(), d2.getFixed(), true);
// if (ok) {
// d1.setFixed(null);
// d2.setFixed(null);
// }
// }
// if (d1.getPath().equals("Patient.identifier")) {
// ok = d1.hasSliceName() && d2.hasSliceName() && !Base.compareDeep(d1.getSliceNameElement(), d2.getSliceNameElement(), true);
// if (ok) {
// d1.setSliceName(null);
// d2.setSliceName(null);
// }
// }
// ok = Base.compareDeep(d1, d2, true);
// }
// }
// // for throughness, we could check against identifier too, but this is not done now.
//
// if (!ok) {
// compareXml(base, focus);
// throw new FHIRException("Snap shot generation slicing failed");
// } else
// System.out.println("Snap shot generation slicing passed");
//
// }
//
// /**
// * we're going to slice Patient.extension and refer to extension by profile
// *
// * implicit: whether to rely on implicit extension slicing
// */
// private void testSlicingExtension(boolean implicit) throws EOperationOutcome, Exception {
//
// StructureDefinition focus = new StructureDefinition();
// StructureDefinition base = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/Patient").copy();
// focus.setUrl(Utilities.makeUuidUrn());
// focus.setBaseDefinition(base.getUrl());
// focus.setType(base.getType());
// focus.setDerivation(TypeDerivationRule.CONSTRAINT);
//
// // set the slice up
// ElementDefinition id;
// if (!implicit) {
// id = focus.getDifferential().addElement();
// id.setPath("Patient.extension");
// id.getSlicing().setOrdered(false).setRules(SlicingRules.OPEN).addDiscriminator().setPath("url").setType(DiscriminatorType.VALUE);
// id.setMax("3");
// }
// // first slice:
// id = focus.getDifferential().addElement();
// id.setPath("Patient.extension");
// id.setSliceName("name1");
// id.addType().setCode("Extension").setProfile("http://hl7.org/fhir/StructureDefinition/patient-birthTime");
// id.setMin(1);
//
// // second slice:
// id = focus.getDifferential().addElement();
// id.setPath("Patient.extension");
// id.setSliceName("name2");
// id.addType().setCode("Extension").setProfile("http://hl7.org/fhir/StructureDefinition/patient-mothersMaidenName");
//
// List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
// ProfileUtilities pu = new ProfileUtilities(context, messages, null);
// pu.generateSnapshot(base, focus, focus.getUrl(), "Simple Test" );
//
// // 2 different: extension slices
// boolean ok = base.getSnapshot().getElement().size() == focus.getSnapshot().getElement().size() - 2;
// for (int i = 0; i < base.getSnapshot().getElement().size(); i++) {
// if (ok) {
// ElementDefinition b = base.getSnapshot().getElement().get(i);
// ElementDefinition f = focus.getSnapshot().getElement().get(i <= 7 ? i : i + 2);
// if (!f.hasBase() || !b.getPath().equals(f.getBase().getPath()))
// ok = false;
// else {
// f.setBase(null);
// if (f.getPath().equals("Patient.extension")) {
// ok = f.hasSlicing() && (implicit || f.getMax().equals("3"));
// if (ok) {
// f.setSlicing(null);
// f.setMaxElement(b.getMaxElement());
// }
// }
// if (!f.getPath().equals("Patient.extension")) // no compare that because the definitions get overwritten
// ok = Base.compareDeep(b, f, true);
// }
// }
// }
// // now, check that the slices we skipped are correct:
// if (ok) {
// ElementDefinition d1 = focus.getSnapshot().getElement().get(8);
// ElementDefinition d2 = focus.getSnapshot().getElement().get(9);
// ok = d1.hasType() && d1.getType().get(0).hasProfile() && d2.hasType() && d2.getType().get(0).hasProfile() && !Base.compareDeep(d1.getType(), d2.getType(), true) &&
// d1.getMin() == 1 && d2.getMin() == 0 && d1.getMax().equals("1") && d2.getMax().equals("1");
// if (ok) {
// d1.getType().clear();
// d2.getType().clear();
// d1.setSliceName("x");
// d2.setSliceName("x");
// d1.setMin(0);
// }
// ok = Base.compareDeep(d1, d2, true);
// // for throughness, we could check against extension too, but this is not done now.
// }
//
// if (!ok) {
// compareXml(base, focus);
// throw new FHIRException("Snap shot generation slicing extensions simple ("+(implicit ? "implicit" : "not implicit")+") failed");
// } else
// System.out.println("Snap shot generation slicing extensions simple ("+(implicit ? "implicit" : "not implicit")+") passed");
// }
//
// /**
// * we're going to slice Patient.extension and refer to extension by profile. one of the extensions is complex, and we're going to walk into
// * it and make it must support
// *
// * implicit: whether to rely on implicit extension slicing
// */
// private void testSlicingExtensionComplex(boolean implicit) throws EOperationOutcome, Exception {
//
// StructureDefinition focus = new StructureDefinition();
// StructureDefinition base = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/Patient").copy();
// focus.setUrl(Utilities.makeUuidUrn());
// focus.setBaseDefinition(base.getUrl());
// focus.setType(base.getType());
// focus.setDerivation(TypeDerivationRule.CONSTRAINT);
//
// // set the slice up
// ElementDefinition id;
// if (!implicit) {
// id = focus.getDifferential().addElement();
// id.setPath("Patient.extension");
// id.getSlicing().setOrdered(false).setRules(SlicingRules.OPEN).addDiscriminator().setPath("url").setType(DiscriminatorType.VALUE);
// }
// // first slice - a simple one to get us going:
// id = focus.getDifferential().addElement();
// id.setPath("Patient.extension");
// id.setSliceName("simple");
// id.addType().setCode("Extension").setProfile("http://hl7.org/fhir/StructureDefinition/patient-birthTime");
//
// // second slice - the complex one
// // we walk into this and fix properties on the inner extensions
// id = focus.getDifferential().addElement();
// id.setPath("Patient.extension");
// id.setSliceName("complex");
// id.addType().setCode("Extension").setProfile("http://hl7.org/fhir/StructureDefinition/patient-nationality");
// if (!implicit) {
// id = focus.getDifferential().addElement();
// id.setPath("Patient.extension.extension");
// id.getSlicing().setOrdered(false).setRules(SlicingRules.OPEN).addDiscriminator().setPath("url").setType(DiscriminatorType.VALUE);
// }
// id = focus.getDifferential().addElement();
// id.setPath("Patient.extension.extension");
// id.setSliceName("code");
// id.setMustSupport(true);
// id.addType().setCode("Extension").setProfile("http://hl7.org/fhir/StructureDefinition/patient-nationality#code");
//
// id = focus.getDifferential().addElement();
// id.setPath("Patient.extension.extension");
// id.setSliceName("period");
// id.addType().setCode("Extension").setProfile("http://hl7.org/fhir/StructureDefinition/patient-nationality#period");
// id.setMax("0"); // prohibit this one....
//
// List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
// new ProfileUtilities(context, messages, null).generateSnapshot(base, focus, focus.getUrl(), "Simple Test" );
//
// // ok, there's going to 1 (simple) + complex: 1 + id + extnesion.slice + extension.code + (4 inside from that) + extension.period + (4 inside from that) + value + url = 16
// boolean ok = base.getSnapshot().getElement().size() == focus.getSnapshot().getElement().size() - 16;
//
// // custom checks
// ok = ok && rule(focus.getSnapshot().getElement().get(7).getPath().equals("Patient.extension"), "element 7 (base) path");
// ok = ok && rule(focus.getSnapshot().getElement().get(7).hasSlicing(), "element 7 slicing");
// ok = ok && rule(focus.getSnapshot().getElement().get(8).getPath().equals("Patient.extension"), "element 8 (1st slice) path");
// ok = ok && rule(focus.getSnapshot().getElement().get(8).getSliceName().equals("simple"), "element 8 (1st slice) name");
// ok = ok && rule(focus.getSnapshot().getElement().get(8).getType().get(0).getProfile().equals("http://hl7.org/fhir/StructureDefinition/patient-birthTime"), "element 9 (2nd slice) profile name");
// ok = ok && rule(focus.getSnapshot().getElement().get(9).getPath().equals("Patient.extension"), "element 9 (2nd slice) path");
// ok = ok && rule(focus.getSnapshot().getElement().get(9).getSliceName().equals("complex"), "element 8 (1st slice) name");
// ok = ok && rule(focus.getSnapshot().getElement().get(9).getType().get(0).getProfile().equals("http://hl7.org/fhir/StructureDefinition/patient-nationality"), "element 9 (2nd slice) profile name");
// ok = ok && rule(focus.getSnapshot().getElement().get(10).getPath().equals("Patient.extension.id"), "element 10 (2nd slice).id path");
// ok = ok && rule(focus.getSnapshot().getElement().get(11).getPath().equals("Patient.extension.extension"), "element 11 (2nd slice).extension path");
// ok = ok && rule(focus.getSnapshot().getElement().get(12).getPath().equals("Patient.extension.extension"), "element 12 (2nd slice).extension path");
// ok = ok && rule(focus.getSnapshot().getElement().get(12).getMustSupport(), "element 12 (2nd slice).extension must support");
// ok = ok && rule(focus.getSnapshot().getElement().get(13).getPath().equals("Patient.extension.extension.id"), "element 13 (2nd slice).extension.id path");
// ok = ok && rule(focus.getSnapshot().getElement().get(14).getPath().equals("Patient.extension.extension.extension"), "element 14 (2nd slice).extension.extension path");
// ok = ok && rule(focus.getSnapshot().getElement().get(15).getPath().equals("Patient.extension.extension.url"), "element 15 (2nd slice).extension.url path");
// ok = ok && rule(focus.getSnapshot().getElement().get(16).getPath().equals("Patient.extension.extension.valueCodeableConcept"), "element 16 (2nd slice).extension.valueCodeableConcept path");
// ok = ok && rule(focus.getSnapshot().getElement().get(17).getPath().equals("Patient.extension.extension"), "element 17 (2nd slice).extension path");
// ok = ok && rule(focus.getSnapshot().getElement().get(17).getMax().equals("0"), "element 17 (2nd slice).extension cardinality");
// ok = ok && rule(focus.getSnapshot().getElement().get(18).getPath().equals("Patient.extension.extension.id"), "element 18 (2nd slice).extension.id path");
// ok = ok && rule(focus.getSnapshot().getElement().get(19).getPath().equals("Patient.extension.extension.extension"), "element 19 (2nd slice).extension.extension path");
// ok = ok && rule(focus.getSnapshot().getElement().get(20).getPath().equals("Patient.extension.extension.url"), "element 20 (2nd slice).extension.url path");
// ok = ok && rule(focus.getSnapshot().getElement().get(21).getPath().equals("Patient.extension.extension.valuePeriod"), "element 21 (2nd slice).extension.valuePeriod path");
// ok = ok && rule(focus.getSnapshot().getElement().get(22).getPath().equals("Patient.extension.url"), "element 22 (2nd slice).url path");
// ok = ok && rule(focus.getSnapshot().getElement().get(23).getPath().equals("Patient.extension.value[x]"), "element 23 (2nd slice).url path");
//
// for (int i = 0; i < base.getSnapshot().getElement().size(); i++) {
// if (ok) {
// ElementDefinition b = base.getSnapshot().getElement().get(i);
// ElementDefinition f = focus.getSnapshot().getElement().get(i <= 7 ? i : i + 16);
// if (!f.hasBase() || !b.getPath().equals(f.getBase().getPath()))
// ok = false;
// else {
// f.setBase(null);
// if (f.getPath().equals("Patient.extension")) {
// ok = f.hasSlicing();
// if (ok)
// f.setSlicing(null);
// }
// if (!f.getPath().equals("Patient.extension")) // no compare that because the definitions get overwritten
// ok = Base.compareDeep(b, f, true);
// }
// }
// }
//
// if (!ok) {
// compareXml(base, focus);
// throw new FHIRException("Snap shot generation slicing extensions complex ("+(implicit ? "implicit" : "not implicit")+") failed");
// } else
// System.out.println("Snap shot generation slicing extensions complex ("+(implicit ? "implicit" : "not implicit")+") passed");
// }
//
// private void testSlicingTask8742() throws EOperationOutcome, Exception {
// StructureDefinition focus = new StructureDefinition();
// StructureDefinition base = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/Organization").copy();
// focus.setUrl(Utilities.makeUuidUrn());
// focus.setBaseDefinition(base.getUrl());
// focus.setType(base.getType());
// focus.setDerivation(TypeDerivationRule.CONSTRAINT);
//
// ElementDefinition id = focus.getDifferential().addElement();
// id.setPath("Organization.address");
// id.setMin(1);
// id.setMax("1");
// id.setMustSupport(true);
//
// id = focus.getDifferential().addElement();
// id.setPath("Organization.address.extension");
// id.setSliceName("USLabCountycodes");
// id.getSlicing().setOrdered(false).setRules(SlicingRules.OPEN).addDiscriminator().setPath("url").setType(DiscriminatorType.VALUE);
// id.setShort("County/Parish FIPS codes");
// id.setDefinition("County/Parish FIPS codes.");
// id.setRequirements("County/Parish Code SHALL use FIPS 6-4 ( INCITS 31:2009).");
// id.setMin(0);
// id.setMax("1");
// id.addType().setCode("Extension").setProfile("http://hl7.org/fhir/StructureDefinition/us-core-county");
// id.setMustSupport(true);
// id.getBinding().setStrength(BindingStrength.REQUIRED).setDescription("FIPS codes for US counties and county equivalent entities.").setValueSet(new Reference().setReference("http://hl7.org/fhir/ValueSet/fips-county"));
// List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
//
// new ProfileUtilities(context, messages, null).generateSnapshot(base, focus, focus.getUrl(), "Simple Test" );
//
// // 14 for address with one sliced extension
// boolean ok = base.getSnapshot().getElement().size() == focus.getSnapshot().getElement().size() - 13;
//
// if (!ok) {
// compareXml(base, focus);
// throw new FHIRException("Snap shot generation test 8742 failed");
// } else
// System.out.println("Snap shot generation test 8742 passed");
// }
//
//
// private boolean rule(boolean ok, String message) {
// if (!ok)
// System.out.println("Test failed: " + message);
// return ok;
// }
//
private void compareXml(StructureDefinition base, StructureDefinition focus) throws FileNotFoundException, IOException {
base.setText(null);
focus.setText(null);
base.setDifferential(null);
// focus.setDifferential(null);
String f1 = Utilities.path("c:", "temp", "base.xml");
String f2 = Utilities.path("c:", "temp", "derived.xml");
new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(f1), base);;
new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(f2), focus);;
String diff = Utilities.path(System.getenv("ProgramFiles(X86)"), "WinMerge", "WinMergeU.exe");
List<String> command = new ArrayList<String>();
command.add("\"" + diff + "\" \"" + f1 + "\" \"" + f2 + "\"");
ProcessBuilder builder = new ProcessBuilder(command);
builder.directory(new CSFile("c:\\temp"));
builder.start();
}
}

View File

@ -1,39 +0,0 @@
package org.hl7.fhir.r4.test;
import static org.junit.Assert.*;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import org.hl7.fhir.r4.context.IWorkerContext;
import org.hl7.fhir.r4.context.SimpleWorkerContext;
import org.hl7.fhir.r4.formats.IParser.OutputStyle;
import org.hl7.fhir.r4.formats.XmlParser;
import org.hl7.fhir.r4.model.DomainResource;
import org.hl7.fhir.r4.model.Resource;
import org.hl7.fhir.r4.test.support.TestingUtilities;
import org.hl7.fhir.r4.utils.EOperationOutcome;
import org.hl7.fhir.r4.utils.NarrativeGenerator;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.exceptions.FHIRException;
import org.junit.Before;
import org.junit.Test;
public class ResourceRoundTripTests {
@Before
public void setUp() throws Exception {
}
@Test
public void test() throws FileNotFoundException, IOException, FHIRException, EOperationOutcome {
if (TestingUtilities.context == null)
TestingUtilities.context = SimpleWorkerContext.fromPack(Utilities.path(TestingUtilities.home(), "publish", "definitions.xml.zip"));
Resource res = new XmlParser().parse(new FileInputStream(Utilities.path(TestingUtilities.home(), "tests", "resources", "unicode.xml")));
new NarrativeGenerator("", "", TestingUtilities.context).generate((DomainResource) res);
new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(TestingUtilities.home(), "tests", "resources","unicode.out.xml")), res);
}
}

View File

@ -1,85 +0,0 @@
package org.hl7.fhir.r4.test;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import org.hl7.fhir.r4.conformance.ShExGenerator;
import org.hl7.fhir.r4.conformance.ShExGenerator.HTMLLinkPolicy;
import org.hl7.fhir.r4.context.SimpleWorkerContext;
import org.hl7.fhir.r4.model.StructureDefinition;
import org.hl7.fhir.r4.test.support.TestingUtilities;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.utilities.TextFile;
import org.junit.Test;
public class ShexGeneratorTests {
private void doTest(String name) throws FileNotFoundException, IOException, FHIRException {
String workingDirectory = "C:\\work\\org.hl7.fhir\\build\\publish"; // FileSystems.getDefault().getPath(System.getProperty("user.dir"), "data").toString();
// String workingDirectory = FileSystems.getDefault().getPath(System.getProperty("user.dir"), "..", "..", "..", "publish").toString();
if (TestingUtilities.context == null) {
// For the time being, put the validation entry in org/hl7/fhir/r4/data
Path path = FileSystems.getDefault().getPath(workingDirectory, "definitions.xml.zip");
TestingUtilities.context = SimpleWorkerContext.fromPack(path.toString());
}
StructureDefinition sd = TestingUtilities.context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/"+name);
if(sd == null) {
throw new FHIRException("StructuredDefinition for " + name + "was null");
}
Path outPath = FileSystems.getDefault().getPath(workingDirectory, name.toLowerCase()+".shex");
TextFile.stringToFile(new ShExGenerator(TestingUtilities.context).generate(HTMLLinkPolicy.NONE, sd), outPath.toString());
}
@Test
public void testId() throws FHIRException, IOException {
doTest("id");
}
@Test
public void testUri() throws FHIRException, IOException {
doTest("uri");
}
@Test
public void testObservation() throws FHIRException, IOException {
doTest("Observation");
}
@Test
public void testRef() throws FHIRException, IOException {
doTest("Reference");
}
@Test
public void testAccount() throws FHIRException, IOException {
doTest("Account");
}
@Test
public void testMedicationOrder() throws FHIRException, IOException {
doTest("MedicationOrder");
}
@Test
public void testAllergyIntolerance() throws FHIRException, IOException {
doTest("AllergyIntolerance");
}
@Test
public void testCoding() throws FHIRException, IOException {
doTest("Coding");
}
@Test
public void testTiming() throws FHIRException, IOException {
doTest("Timing");
}
@Test
public void testSignature() throws FHIRException, IOException {
doTest("Signature");
}
}

View File

@ -1,338 +0,0 @@
package org.hl7.fhir.r4.test;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.xml.parsers.ParserConfigurationException;
import org.hl7.fhir.r4.conformance.ProfileUtilities;
import org.hl7.fhir.r4.context.SimpleWorkerContext;
import org.hl7.fhir.r4.formats.IParser.OutputStyle;
import org.hl7.fhir.r4.formats.XmlParser;
import org.hl7.fhir.r4.model.Base;
import org.hl7.fhir.r4.model.BooleanType;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.ExpressionNode;
import org.hl7.fhir.r4.model.MetadataResource;
import org.hl7.fhir.r4.model.ExpressionNode.CollectionStatus;
import org.hl7.fhir.r4.model.PrimitiveType;
import org.hl7.fhir.r4.model.Resource;
import org.hl7.fhir.r4.model.StructureDefinition;
import org.hl7.fhir.r4.model.TestScript;
import org.hl7.fhir.r4.model.TestScript.SetupActionAssertComponent;
import org.hl7.fhir.r4.model.TestScript.SetupActionComponent;
import org.hl7.fhir.r4.model.TestScript.SetupActionOperationComponent;
import org.hl7.fhir.r4.model.TestScript.TestActionComponent;
import org.hl7.fhir.r4.model.TestScript.TestScriptFixtureComponent;
import org.hl7.fhir.r4.model.TestScript.TestScriptTestComponent;
import org.hl7.fhir.r4.model.TypeDetails;
import org.hl7.fhir.r4.test.support.TestingUtilities;
import org.hl7.fhir.r4.utils.CodingUtilities;
import org.hl7.fhir.r4.utils.FHIRPathEngine;
import org.hl7.fhir.r4.utils.FHIRPathEngine.IEvaluationContext;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.exceptions.PathEngineException;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.xml.XMLUtil;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import junit.framework.Assert;
@RunWith(Parameterized.class)
public class SnapShotGenerationTests {
private static class SnapShotGenerationTestsContext implements IEvaluationContext {
private Map<String, Resource> fixtures;
private Map<String, StructureDefinition> snapshots = new HashMap<String, StructureDefinition>();
public TestScript tests;
public void checkTestsDetails() {
if (!"http://hl7.org/fhir/tests/snapshotgeneration".equals(tests.getUrl()))
throw new Error("Wrong URL on test script");
if (!tests.getSetup().isEmpty())
throw new Error("Setup is not supported");
if (!tests.getTeardown().isEmpty())
throw new Error("Teardown is not supported");
Set<String> ids = new HashSet<String>();
Set<String> urls = new HashSet<String>();
for (Resource r : tests.getContained()) {
if (ids.contains(r.getId()))
throw new Error("Unsupported: duplicate contained resource on fixture id "+r.getId());
ids.add(r.getId());
if (r instanceof MetadataResource) {
MetadataResource md = (MetadataResource) r;
if (urls.contains(md.getUrl()))
throw new Error("Unsupported: duplicate canonical url "+md.getUrl()+" on fixture id "+r.getId());
urls.add(md.getUrl());
}
}
for (TestScriptFixtureComponent r : tests.getFixture()) {
if (ids.contains(r.getId()))
throw new Error("Unsupported: duplicate contained resource or fixture id "+r.getId());
ids.add(r.getId());
}
Set<String> names = new HashSet<String>();
for (TestScriptTestComponent test : tests.getTest()) {
if (names.contains(test.getName()))
throw new Error("Unsupported: duplicate name "+test.getName());
names.add(test.getName());
if (test.getAction().size() < 2)
throw new Error("Unsupported: multiple actions required");
if (!test.getActionFirstRep().hasOperation())
throw new Error("Unsupported: first action must be an operation");
for (int i = 0; i < test.getAction().size(); i++) {
// if (!test.getAction().get(i).hasAssert())
// throw new Error("Unsupported: following actions must be an asserts");
TestActionComponent action = test.getAction().get(i);
if (action.hasOperation()) {
SetupActionOperationComponent op = test.getActionFirstRep().getOperation();
if (!CodingUtilities.matches(op.getType(), "http://hl7.org/fhir/testscript-operation-codes", "snapshot")
&& !CodingUtilities.matches(op.getType(), "http://hl7.org/fhir/testscript-operation-codes", "sortDifferential"))
throw new Error("Unsupported action operation type "+CodingUtilities.present(op.getType()));
if (!"StructureDefinition".equals(op.getResource()))
throw new Error("Unsupported action operation resource "+op.getResource());
if (!op.hasResponseId())
throw new Error("Unsupported action operation: no response id");
if (!op.hasSourceId())
throw new Error("Unsupported action operation: no source id");
if (!hasSource(op.getSourceId()))
throw new Error("Unsupported action operation: source id could not be resolved");
} else if (action.hasAssert()) {
SetupActionAssertComponent a = action.getAssert();
if (!a.hasLabel())
throw new Error("Unsupported: actions must have a label");
if (!a.hasDescription())
throw new Error("Unsupported: actions must have a description");
if (!a.hasExpression())
throw new Error("Unsupported: actions must have an expression");
} else {
throw new Error("Unsupported: Unrecognized action type");
}
}
}
}
private boolean hasSource(String sourceId) {
for (TestScriptFixtureComponent ds : tests.getFixture()) {
if (sourceId.equals(ds.getId()))
return true;
}
for (Resource r : tests.getContained()) {
if (sourceId.equals(r.getId()))
return true;
}
return false;
}
public Resource fetchFixture(String id) {
if (fixtures.containsKey(id))
return fixtures.get(id);
for (TestScriptFixtureComponent ds : tests.getFixture()) {
if (id.equals(ds.getId()))
throw new Error("not done yet");
}
for (Resource r : tests.getContained()) {
if (id.equals(r.getId()))
return r;
}
return null;
}
// FHIRPath methods
@Override
public Base resolveConstant(Object appContext, String name) throws PathEngineException {
return null;
}
@Override
public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException {
return null;
}
@Override
public boolean log(String argument, List<Base> focus) {
System.out.println(argument+": "+fp.convertToString(focus));
return true;
}
@Override
public FunctionDetails resolveFunction(String functionName) {
if ("fixture".equals(functionName))
return new FunctionDetails("Access a fixture defined in the testing context", 0, 1);
return null;
}
@Override
public TypeDetails checkFunction(Object appContext, String functionName, List<TypeDetails> parameters) throws PathEngineException {
if ("fixture".equals(functionName))
return new TypeDetails(CollectionStatus.SINGLETON, TestingUtilities.context.getResourceNamesAsSet());
return null;
}
@Override
public List<Base> executeFunction(Object appContext, String functionName, List<List<Base>> parameters) {
if ("fixture".equals(functionName)) {
String id = fp.convertToString(parameters.get(0));
Resource res = fetchFixture(id);
if (res != null) {
List<Base> list = new ArrayList<Base>();
list.add(res);
return list;
}
}
return null;
}
@Override
public Base resolveReference(Object appContext, String url) {
// TODO Auto-generated method stub
return null;
}
}
private static FHIRPathEngine fp;
@Parameters(name = "{index}: file {0}")
public static Iterable<Object[]> data() throws ParserConfigurationException, IOException, FHIRFormatError {
SnapShotGenerationTestsContext context = new SnapShotGenerationTestsContext();
context.tests = (TestScript) new XmlParser().parse(new FileInputStream(Utilities.path(TestingUtilities.home(), "tests", "resources", "snapshot-generation-tests.xml")));
context.checkTestsDetails();
List<Object[]> objects = new ArrayList<Object[]>(context.tests.getTest().size());
for (TestScriptTestComponent e : context.tests.getTest()) {
objects.add(new Object[] { e.getName(), e, context });
}
return objects;
}
private final TestScriptTestComponent test;
private final String name;
private SnapShotGenerationTestsContext context;
public SnapShotGenerationTests(String name, TestScriptTestComponent e, SnapShotGenerationTestsContext context) {
this.name = name;
this.test = e;
this.context = context;
}
@SuppressWarnings("deprecation")
@Test
public void test() throws FileNotFoundException, IOException, FHIRException, org.hl7.fhir.exceptions.FHIRException {
if (TestingUtilities.context == null)
TestingUtilities.context = SimpleWorkerContext.fromPack(Utilities.path(TestingUtilities.home(), "publish", "definitions.xml.zip"));
if (fp == null)
fp = new FHIRPathEngine(TestingUtilities.context);
fp.setHostServices(context);
resolveFixtures();
for (int i = 0; i < test.getAction().size(); i++) {
TestActionComponent action = test.getAction().get(i);
if (action.hasOperation()) {
SetupActionOperationComponent op = action.getOperation();
Coding opType = op.getType();
if (opType.getSystem().equals("http://hl7.org/fhir/testscript-operation-codes") && opType.getCode().equals("snapshot")) {
StructureDefinition source = (StructureDefinition) context.fetchFixture(op.getSourceId());
StructureDefinition base = getSD(source.getBaseDefinition());
StructureDefinition output = source.copy();
ProfileUtilities pu = new ProfileUtilities(TestingUtilities.context, null, null);
pu.setIds(source, false);
if ("sort=true".equals(op.getParams())) {
List<String> errors = new ArrayList<String>();
pu.sortDifferential(base, output, source.getName(), errors);
if (errors.size() > 0)
throw new FHIRException("Sort failed: "+errors.toString());
}
pu.generateSnapshot(base, output, source.getUrl(), source.getName());
context.fixtures.put(op.getResponseId(), output);
context.snapshots.put(output.getUrl(), output);
new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(System.getProperty("java.io.tmpdir"), op.getResponseId()+".xml")), output);
} else if (opType.getSystem().equals("http://hl7.org/fhir/testscript-operation-codes") && opType.getCode().equals("sortDifferential")) {
StructureDefinition source = (StructureDefinition) context.fetchFixture(op.getSourceId());
StructureDefinition base = getSD(source.getBaseDefinition());
StructureDefinition output = source.copy();
ProfileUtilities pu = new ProfileUtilities(TestingUtilities.context, null, null);
pu.setIds(source, false);
List<String> errors = new ArrayList<String>();
pu.sortDifferential(base, output, output.getUrl(), errors);
if (!errors.isEmpty())
throw new FHIRException(errors.get(0));
context.fixtures.put(op.getResponseId(), output);
context.snapshots.put(output.getUrl(), output);
new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(System.getProperty("java.io.tmpdir"), op.getResponseId()+".xml")), output);
} else {
throw new Error("Unsupported operation: " + opType.getSystem() + " : " + opType.getCode());
}
} else if (action.hasAssert()) {
SetupActionAssertComponent a = action.getAssert();
Assert.assertTrue(a.getLabel()+": "+a.getDescription(), fp.evaluateToBoolean(new StructureDefinition(), new StructureDefinition(), a.getExpression()));
}
}
}
private StructureDefinition getSD(String url) throws DefinitionException, FHIRException {
StructureDefinition sd = TestingUtilities.context.fetchResource(StructureDefinition.class, url);
if (sd == null)
sd = context.snapshots.get(url);
if (sd == null)
sd = findContainedProfile(url);
return sd;
}
private StructureDefinition findContainedProfile(String url) throws DefinitionException, FHIRException {
for (Resource r : context.tests.getContained()) {
if (r instanceof StructureDefinition) {
StructureDefinition sd = (StructureDefinition) r;
if (sd.getUrl().equals(url)) {
StructureDefinition p = sd.copy();
ProfileUtilities pu = new ProfileUtilities(TestingUtilities.context, null, null);
pu.setIds(p, false);
List<String> errors = new ArrayList<String>();
pu.sortDifferential(getSD(p.getBaseDefinition()), p, url, errors);
if (!errors.isEmpty())
throw new FHIRException(errors.get(0));
pu.generateSnapshot(getSD(p.getBaseDefinition()), p, p.getUrl(), p.getName());
return p;
}
}
}
return null;
}
private void resolveFixtures() {
if (context.fixtures == null) {
context.fixtures = new HashMap<String, Resource>();
for (TestScriptFixtureComponent fd : context.tests.getFixture()) {
Resource r = TestingUtilities.context.fetchResource(Resource.class, fd.getResource().getReference());
context.fixtures.put(fd.getId(), r);
}
}
}
}

View File

@ -1,31 +1,8 @@
package ca.uhn.fhir.to;
import static org.apache.commons.lang3.StringUtils.defaultString;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.*;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Header;
import org.apache.http.entity.ContentType;
import org.apache.http.message.BasicHeader;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.dstu3.model.CapabilityStatement.CapabilityStatementRestComponent;
import org.hl7.fhir.dstu3.model.CapabilityStatement.CapabilityStatementRestResourceComponent;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IDomainResource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ui.ModelMap;
import org.thymeleaf.TemplateEngine;
import ca.uhn.fhir.context.*;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.ExtensionDt;
import ca.uhn.fhir.model.api.IResource;
@ -36,6 +13,31 @@ import ca.uhn.fhir.rest.client.api.*;
import ca.uhn.fhir.rest.client.impl.GenericClient;
import ca.uhn.fhir.to.model.HomeRequest;
import ca.uhn.fhir.util.ExtensionConstants;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Header;
import org.apache.http.entity.ContentType;
import org.apache.http.message.BasicHeader;
import org.hl7.fhir.dstu3.model.CapabilityStatement;
import org.hl7.fhir.dstu3.model.CapabilityStatement.CapabilityStatementRestComponent;
import org.hl7.fhir.dstu3.model.CapabilityStatement.CapabilityStatementRestResourceComponent;
import org.hl7.fhir.dstu3.model.DecimalType;
import org.hl7.fhir.dstu3.model.Extension;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IDomainResource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ui.ModelMap;
import org.thymeleaf.TemplateEngine;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.*;
import static org.apache.commons.lang3.StringUtils.defaultString;
public class BaseController {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseController.class);
@ -285,7 +287,7 @@ public class BaseController {
ourLog.warn("Failed to invoke server", e);
if (e != null) {
theModel.put("errorMsg", "Error: " + e.getMessage());
theModel.put("errorMsg", toDisplayError("Error: " + e.getMessage(), e));
}
return returnsResource;
@ -313,8 +315,8 @@ public class BaseController {
try {
conformance = (ca.uhn.fhir.model.dstu2.resource.Conformance) client.conformance();
} catch (Exception e) {
ourLog.warn("Failed to load conformance statement", e);
theModel.put("errorMsg", "Failed to load conformance statement, error was: " + e.toString());
ourLog.warn("Failed to load conformance statement, error was: {}", e.toString());
theModel.put("errorMsg", toDisplayError("Failed to load conformance statement, error was: " + e.toString(), e));
conformance = new ca.uhn.fhir.model.dstu2.resource.Conformance();
}
@ -373,8 +375,8 @@ public class BaseController {
try {
capabilityStatement = client.fetchConformance().ofType(org.hl7.fhir.dstu3.model.CapabilityStatement.class).execute();
} catch (Exception ex) {
ourLog.warn("Failed to load conformance statement", ex);
theModel.put("errorMsg", "Failed to load conformance statement, error was: " + ex.toString());
ourLog.warn("Failed to load conformance statement, error was: {}", ex.toString());
theModel.put("errorMsg", toDisplayError("Failed to load conformance statement, error was: " + ex.toString(), ex));
}
theModel.put("jsonEncodedConf", getContext(theRequest).newJsonParser().encodeResourceToString(capabilityStatement));
@ -602,7 +604,7 @@ public class BaseController {
} catch (Exception e) {
ourLog.error("Failure during processing", e);
theModelMap.put("errorMsg", "Error during processing: " + e.getMessage());
theModelMap.put("errorMsg", toDisplayError("Error during processing: " + e.getMessage(), e));
}
}
@ -679,4 +681,16 @@ public class BaseController {
BUNDLE, NONE, RESOURCE, TAGLIST
}
}
/**
* A hook to be overridden by subclasses. The overriding method can modify the error message
* based on its content and/or the related exception.
*
* @param theErrorMsg The original error message to be displayed to the user.
* @param theException The exception that occurred. May be null.
* @return The modified error message to be displayed to the user.
*/
protected String toDisplayError(String theErrorMsg, Exception theException) {
return theErrorMsg;
}
}

View File

@ -1,6 +1,9 @@
package ca.uhn.fhir.to;
import static org.apache.commons.lang3.StringUtils.*;
import static org.apache.commons.lang3.StringUtils.defaultIfEmpty;
import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.IOException;
import java.io.StringWriter;
@ -63,15 +66,15 @@ public class Controller extends BaseController {
Class<? extends IBaseConformance> type;
switch (getContext(theRequest).getVersion().getVersion()) {
default:
case DSTU1:
type = Conformance.class;
break;
case DSTU2:
type = ca.uhn.fhir.model.dstu2.resource.Conformance.class;
break;
case DSTU3:
type = org.hl7.fhir.dstu3.model.CapabilityStatement.class;
break;
case R4:
type = org.hl7.fhir.r4.model.CapabilityStatement.class;
break;
}
client.fetchConformance().ofType(type).execute();
} catch (Exception e) {
@ -103,13 +106,13 @@ public class Controller extends BaseController {
try {
def = getResourceType(theRequest, theReq);
} catch (ServletException e) {
theModel.put("errorMsg", e.toString());
theModel.put("errorMsg", toDisplayError(e.toString(), e));
return "resource";
}
String id = StringUtils.defaultString(theReq.getParameter("resource-delete-id"));
if (StringUtils.isBlank(id)) {
theModel.put("errorMsg", "No ID specified");
theModel.put("errorMsg", toDisplayError("No ID specified", null));
return "resource";
}
@ -148,7 +151,7 @@ public class Controller extends BaseController {
try {
def = getResourceType(theRequest, theReq);
} catch (ServletException e) {
theModel.put("errorMsg", e.toString());
theModel.put("errorMsg", toDisplayError(e.toString(), e));
return "resource";
}
@ -212,11 +215,11 @@ public class Controller extends BaseController {
if (myConfig.isRefuseToFetchThirdPartyUrls()) {
if (!url.startsWith(theModel.get("base").toString())) {
ourLog.warn(logPrefix(theModel) + "Refusing to load page URL: {}", url);
theModel.put("errorMsg", "Invalid page URL: " + url);
theModel.put("errorMsg", toDisplayError("Invalid page URL: " + url, null));
return "result";
}
}
url = url.replace("&amp;", "&");
ResultType returnsResource = ResultType.BUNDLE;
@ -224,13 +227,9 @@ public class Controller extends BaseController {
long start = System.currentTimeMillis();
try {
ourLog.info(logPrefix(theModel) + "Loading paging URL: {}", url);
if (context.getVersion().getVersion() == FhirVersionEnum.DSTU1) {
client.loadPage().byUrl(url).andReturnDstu1Bundle().execute();
} else {
@SuppressWarnings("unchecked")
Class<? extends IBaseBundle> bundleType = (Class<? extends IBaseBundle>) context.getResourceDefinition("Bundle").getImplementingClass();
client.loadPage().byUrl(url).andReturnBundle(bundleType).execute();
}
@SuppressWarnings("unchecked")
Class<? extends IBaseBundle> bundleType = (Class<? extends IBaseBundle>) context.getResourceDefinition("Bundle").getImplementingClass();
client.loadPage().byUrl(url).andReturnBundle(bundleType).execute();
} catch (Exception e) {
returnsResource = handleClientException(client, e, theModel);
}
@ -254,12 +253,12 @@ public class Controller extends BaseController {
try {
def = getResourceType(theRequest, theReq);
} catch (ServletException e) {
theModel.put("errorMsg", e.toString());
theModel.put("errorMsg", toDisplayError(e.toString(), e));
return "resource";
}
String id = StringUtils.defaultString(theReq.getParameter("id"));
if (StringUtils.isBlank(id)) {
theModel.put("errorMsg", "No ID specified");
theModel.put("errorMsg", toDisplayError("No ID specified", null));
return "resource";
}
ResultType returnsResource = ResultType.RESOURCE;
@ -315,13 +314,16 @@ public class Controller extends BaseController {
case DSTU3:
haveSearchParams = extractSearchParamsDstu3CapabilityStatement(conformance, resourceName, includes, revIncludes, sortParams, haveSearchParams, queryIncludes);
break;
case R4:
haveSearchParams = extractSearchParamsR4CapabilityStatement(conformance, resourceName, includes, revIncludes, sortParams, haveSearchParams, queryIncludes);
break;
default:
throw new IllegalStateException("Unknown FHIR version: " + theRequest.getFhirVersion(myConfig));
}
theModel.put("includes", includes);
theModel.put("revincludes", revIncludes);
theModel.put("queries", Collections.emptyList()); //TODO: remove this, it does nothing
theModel.put("queries", Collections.emptyList()); // TODO: remove this, it does nothing
theModel.put("haveSearchParams", haveSearchParams);
theModel.put("queryIncludes", queryIncludes);
theModel.put("sortParams", sortParams);
@ -362,7 +364,7 @@ public class Controller extends BaseController {
try {
query = search.forResource((Class<? extends IBaseResource>) getResourceType(theRequest, theReq).getImplementingClass());
} catch (ServletException e) {
theModel.put("errorMsg", e.toString());
theModel.put("errorMsg", toDisplayError(e.toString(), e));
return "resource";
}
clientCodeJsonWriter.name("resource");
@ -434,7 +436,7 @@ public class Controller extends BaseController {
String limit = theReq.getParameter("resource-search-limit");
if (isNotBlank(limit)) {
if (!limit.matches("[0-9]+")) {
theModel.put("errorMsg", "Search limit must be a numeric value.");
theModel.put("errorMsg", toDisplayError("Search limit must be a numeric value.", null));
return "resource";
}
int limitInt = Integer.parseInt(limit);
@ -463,9 +465,7 @@ public class Controller extends BaseController {
}
}
if (client.getFhirContext().getVersion().getVersion() != FhirVersionEnum.DSTU1) {
query.returnBundle(client.getFhirContext().getResourceDefinition("Bundle").getImplementingClass());
}
query.returnBundle(client.getFhirContext().getResourceDefinition("Bundle").getImplementingClass());
long start = System.currentTimeMillis();
ResultType returnsResource;
@ -505,12 +505,13 @@ public class Controller extends BaseController {
} else if (body.startsWith("<")) {
// XML content
} else {
theModel.put("errorMsg", "Message body does not appear to be a valid FHIR resource instance document. Body should start with '<' (for XML encoding) or '{' (for JSON encoding).");
theModel.put("errorMsg",
toDisplayError("Message body does not appear to be a valid FHIR resource instance document. Body should start with '<' (for XML encoding) or '{' (for JSON encoding).", null));
return "home";
}
} catch (DataFormatException e) {
ourLog.warn("Failed to parse bundle", e);
theModel.put("errorMsg", "Failed to parse transaction bundle body. Error was: " + e.getMessage());
theModel.put("errorMsg", toDisplayError("Failed to parse transaction bundle body. Error was: " + e.getMessage(), e));
return "home";
}
@ -558,7 +559,7 @@ public class Controller extends BaseController {
String body = validate ? theReq.getParameter("resource-validate-body") : theReq.getParameter("resource-create-body");
if (isBlank(body)) {
theModel.put("errorMsg", "No message body specified");
theModel.put("errorMsg", toDisplayError("No message body specified", null));
return;
}
@ -573,12 +574,13 @@ public class Controller extends BaseController {
resource = getContext(theRequest).newXmlParser().parseResource(type, body);
client.setEncoding(EncodingEnum.XML);
} else {
theModel.put("errorMsg", "Message body does not appear to be a valid FHIR resource instance document. Body should start with '<' (for XML encoding) or '{' (for JSON encoding).");
theModel.put("errorMsg",
toDisplayError("Message body does not appear to be a valid FHIR resource instance document. Body should start with '<' (for XML encoding) or '{' (for JSON encoding).", null));
return;
}
} catch (DataFormatException e) {
ourLog.warn("Failed to parse resource", e);
theModel.put("errorMsg", "Failed to parse message body. Error was: " + e.getMessage());
theModel.put("errorMsg", toDisplayError("Failed to parse message body. Error was: " + e.getMessage(), e));
return;
}
@ -671,11 +673,7 @@ public class Controller extends BaseController {
}
IHistoryTyped<?> hist2;
if (client.getFhirContext().getVersion().getVersion() == FhirVersionEnum.DSTU1) {
hist2 = hist1.andReturnDstu1Bundle();
} else {
hist2 = hist1.andReturnBundle(client.getFhirContext().getResourceDefinition("Bundle").getImplementingClass(IBaseBundle.class));
}
hist2 = hist1.andReturnBundle(client.getFhirContext().getResourceDefinition("Bundle").getImplementingClass(IBaseBundle.class));
if (since != null) {
hist2.since(since);
@ -694,8 +692,8 @@ public class Controller extends BaseController {
}
private boolean extractSearchParamsDstu2(IBaseResource theConformance, String resourceName, TreeSet<String> includes, TreeSet<String> theRevIncludes, TreeSet<String> sortParams, boolean haveSearchParams, List<List<String>> queryIncludes) {
private boolean extractSearchParamsDstu2(IBaseResource theConformance, String resourceName, TreeSet<String> includes, TreeSet<String> theRevIncludes, TreeSet<String> sortParams,
boolean haveSearchParams, List<List<String>> queryIncludes) {
ca.uhn.fhir.model.dstu2.resource.Conformance conformance = (ca.uhn.fhir.model.dstu2.resource.Conformance) theConformance;
for (ca.uhn.fhir.model.dstu2.resource.Conformance.Rest nextRest : conformance.getRest()) {
for (ca.uhn.fhir.model.dstu2.resource.Conformance.RestResource nextRes : nextRest.getResource()) {
@ -731,7 +729,8 @@ public class Controller extends BaseController {
return haveSearchParams;
}
private boolean extractSearchParamsDstu3CapabilityStatement(IBaseResource theConformance, String resourceName, TreeSet<String> includes, TreeSet<String> theRevIncludes, TreeSet<String> sortParams, boolean haveSearchParams, List<List<String>> queryIncludes) {
private boolean extractSearchParamsDstu3CapabilityStatement(IBaseResource theConformance, String resourceName, TreeSet<String> includes, TreeSet<String> theRevIncludes, TreeSet<String> sortParams,
boolean haveSearchParams, List<List<String>> queryIncludes) {
CapabilityStatement conformance = (org.hl7.fhir.dstu3.model.CapabilityStatement) theConformance;
for (CapabilityStatementRestComponent nextRest : conformance.getRest()) {
for (CapabilityStatementRestResourceComponent nextRes : nextRest.getResource()) {
@ -754,11 +753,38 @@ public class Controller extends BaseController {
// scan for revinclude candidates
for (CapabilityStatementRestResourceSearchParamComponent next : nextRes.getSearchParam()) {
if (next.getTypeElement().getValue() == org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.REFERENCE) {
// for (CodeType nextTargetType : next.getTarget()) {
// if (nextTargetType.getValue().equals(resourceName)) {
// theRevIncludes.add(nextRes.getTypeElement().getValue() + ":" + next.getName());
// }
// }
}
}
}
}
}
return haveSearchParams;
}
private boolean extractSearchParamsR4CapabilityStatement(IBaseResource theConformance, String resourceName, TreeSet<String> includes, TreeSet<String> theRevIncludes, TreeSet<String> sortParams,
boolean haveSearchParams, List<List<String>> queryIncludes) {
org.hl7.fhir.r4.model.CapabilityStatement conformance = (org.hl7.fhir.r4.model.CapabilityStatement) theConformance;
for (org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestComponent nextRest : conformance.getRest()) {
for (org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestResourceComponent nextRes : nextRest.getResource()) {
if (nextRes.getTypeElement().getValue().equals(resourceName)) {
for (org.hl7.fhir.r4.model.StringType next : nextRes.getSearchInclude()) {
if (next.isEmpty() == false) {
includes.add(next.getValue());
}
}
for (org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent next : nextRes.getSearchParam()) {
if (next.getTypeElement().getValue() != org.hl7.fhir.r4.model.Enumerations.SearchParamType.COMPOSITE) {
sortParams.add(next.getNameElement().getValue());
}
}
if (nextRes.getSearchParam().size() > 0) {
haveSearchParams = true;
}
} else {
// It's a different resource from the one we're searching, so
// scan for revinclude candidates
for (org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent next : nextRes.getSearchParam()) {
if (next.getTypeElement().getValue() == org.hl7.fhir.r4.model.Enumerations.SearchParamType.REFERENCE) {
}
}
}

Some files were not shown because too many files have changed in this diff Show More