Merge remote-tracking branch 'jamesagnew/master' into cleanup2

This commit is contained in:
sekaijin 2017-02-08 10:52:07 +01:00
commit 8fb20ed661
33 changed files with 969 additions and 464 deletions

View File

@ -1889,7 +1889,11 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
for (Include next : myRevInclude) {
addParam(params, Constants.PARAM_REVINCLUDE, next.getValue());
if (next.isRecurse()) {
addParam(params, Constants.PARAM_REVINCLUDE_RECURSE, next.getValue());
} else {
addParam(params, Constants.PARAM_REVINCLUDE, next.getValue());
}
}
if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU2)) {

View File

@ -40,11 +40,13 @@ public class Constants {
public static final String CT_HTML = "text/html";
public static final String CT_HTML_WITH_UTF8 = "text/html" + CHARSET_UTF8_CTSUFFIX;
public static final String CT_JSON = "application/json";
public static final String CT_JSON_PATCH = "application/json-patch+json";
public static final String CT_OCTET_STREAM = "application/octet-stream";
public static final String CT_TEXT = "text/plain";
public static final String CT_TEXT_WITH_UTF8 = CT_TEXT + CHARSET_UTF8_CTSUFFIX;
public static final String CT_X_FORM_URLENCODED = "application/x-www-form-urlencoded";
public static final String CT_XML = "application/xml";
public static final String CT_XML_PATCH = "application/xml-patch+xml";
public static final String ENCODING_GZIP = "gzip";
public static final String EXTOP_VALIDATE = "$validate";
public static final String EXTOP_VALIDATE_MODE = "mode";
@ -61,10 +63,10 @@ public class Constants {
public static final String FORMATS_HTML_XML = "html/xml";
public static final String HEADER_ACCEPT = "Accept";
public static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding";
public static final String HEADER_ACCEPT_VALUE_JSON_NON_LEGACY = CT_FHIR_JSON_NEW + ";q=1.0, " + CT_FHIR_JSON + ";q=0.9";
public static final String HEADER_ACCEPT_VALUE_XML_NON_LEGACY = CT_FHIR_XML_NEW + ";q=1.0, " + CT_FHIR_XML + ";q=0.9";
public static final String HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY = CT_FHIR_XML + ";q=1.0, " + CT_FHIR_JSON + ";q=1.0";
public static final String HEADER_ACCEPT_VALUE_XML_OR_JSON_NON_LEGACY = CT_FHIR_XML_NEW + ";q=1.0, " + CT_FHIR_JSON_NEW + ";q=1.0, " + HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY.replace("1.0", "0.9");
public static final String HEADER_ACCEPT_VALUE_XML_NON_LEGACY = CT_FHIR_XML_NEW + ";q=1.0, " + CT_FHIR_XML + ";q=0.9";
public static final String HEADER_ACCEPT_VALUE_JSON_NON_LEGACY = CT_FHIR_JSON_NEW + ";q=1.0, " + CT_FHIR_JSON + ";q=0.9";
public static final String HEADER_ALLOW = "Allow";
public static final String HEADER_AUTHORIZATION = "Authorization";
public static final String HEADER_AUTHORIZATION_VALPREFIX_BASIC = "Basic ";
@ -127,10 +129,12 @@ public class Constants {
public static final String PARAM_PAGINGACTION = "_getpages";
public static final String PARAM_PAGINGOFFSET = "_getpagesoffset";
public static final String PARAM_PRETTY = "_pretty";
public static final String PARAM_PRETTY_VALUE_FALSE = "false";
public static final String PARAM_PRETTY_VALUE_TRUE = "true";
public static final String PARAM_PROFILE = "_profile";
public static final String PARAM_QUERY = "_query";
public static final String PARAM_REVINCLUDE = "_revinclude";
public static final String PARAM_REVINCLUDE_RECURSE = PARAM_REVINCLUDE+PARAM_INCLUDE_QUALIFIER_RECURSE;
public static final String PARAM_SEARCH = "_search";
public static final String PARAM_SECURITY = "_security";
public static final String PARAM_SINCE = "_since";
@ -156,11 +160,11 @@ public class Constants {
public static final int STATUS_HTTP_401_CLIENT_UNAUTHORIZED = 401;
public static final int STATUS_HTTP_403_FORBIDDEN = 403;
public static final int STATUS_HTTP_404_NOT_FOUND = 404;
public static final int STATUS_HTTP_405_METHOD_NOT_ALLOWED = 405;
public static final int STATUS_HTTP_409_CONFLICT = 409;
public static final int STATUS_HTTP_410_GONE = 410;
public static final int STATUS_HTTP_412_PRECONDITION_FAILED = 412;
public static final int STATUS_HTTP_422_UNPROCESSABLE_ENTITY = 422;
public static final int STATUS_HTTP_500_INTERNAL_ERROR = 500;
public static final int STATUS_HTTP_501_NOT_IMPLEMENTED = 501;
@ -168,9 +172,6 @@ public class Constants {
public static final String TAG_SUBSETTED_SYSTEM = "http://hl7.org/fhir/v3/ObservationValue";
public static final String URL_TOKEN_HISTORY = "_history";
public static final String URL_TOKEN_METADATA = "metadata";
public static final String CT_JSON_PATCH = "application/json-patch+json";
public static final String CT_XML_PATCH = "application/xml-patch+xml";
public static final String PARAM_PRETTY_VALUE_FALSE = "false";
static {
CHARSET_UTF8 = Charset.forName(CHARSET_NAME_UTF8);

View File

@ -22,6 +22,7 @@ package ca.uhn.fhir.util;
import java.util.Collection;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseDatatype;
import org.hl7.fhir.instance.model.api.IBaseParameters;
@ -84,6 +85,7 @@ public class ParametersUtil {
}
public static IBaseParameters newInstance(FhirContext theContext) {
Validate.notNull(theContext, "theContext must not be null");
return (IBaseParameters) theContext.getResourceDefinition("Parameters").newInstance();
}
}

View File

@ -101,6 +101,7 @@ public class BaseDstu2Config extends BaseConfig {
public ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2 systemProviderDstu2() {
ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2 retVal = new ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2();
retVal.setDao(systemDaoDstu2());
retVal.setContext(fhirContextDstu2());
return retVal;
}

View File

@ -22,29 +22,15 @@ package ca.uhn.fhir.jpa.dao;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;
import javax.annotation.PostConstruct;
import javax.persistence.NoResultException;
import javax.persistence.TypedQuery;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseCoding;
import org.hl7.fhir.instance.model.api.IBaseMetaType;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.instance.model.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.transaction.PlatformTransactionManager;
@ -59,42 +45,21 @@ import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTableDao;
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamUriDao;
import ca.uhn.fhir.jpa.dao.data.IResourceTableDao;
import ca.uhn.fhir.jpa.dao.data.ISearchResultDao;
import ca.uhn.fhir.jpa.entity.BaseHasResource;
import ca.uhn.fhir.jpa.entity.BaseTag;
import ca.uhn.fhir.jpa.entity.ResourceHistoryTable;
import ca.uhn.fhir.jpa.entity.ResourceLink;
import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.entity.TagDefinition;
import ca.uhn.fhir.jpa.entity.TagTypeEnum;
import ca.uhn.fhir.jpa.entity.*;
import ca.uhn.fhir.jpa.interceptor.IJpaServerInterceptor;
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
import ca.uhn.fhir.jpa.util.DeleteConflict;
import ca.uhn.fhir.jpa.util.StopWatch;
import ca.uhn.fhir.jpa.util.jsonpatch.JsonPatchUtils;
import ca.uhn.fhir.jpa.util.xmlpatch.XmlPatchUtils;
import ca.uhn.fhir.model.api.IQueryParameterAnd;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.api.*;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.PatchTypeEnum;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.method.MethodUtil;
import ca.uhn.fhir.rest.method.QualifiedParamList;
import ca.uhn.fhir.rest.method.RequestDetails;
import ca.uhn.fhir.rest.method.RestSearchParameterTypeEnum;
import ca.uhn.fhir.rest.method.SearchMethodBinding;
import ca.uhn.fhir.rest.method.*;
import ca.uhn.fhir.rest.method.SearchMethodBinding.QualifierDetails;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.rest.server.exceptions.*;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
import ca.uhn.fhir.util.FhirTerser;
@ -110,14 +75,14 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
@Autowired
private DaoConfig myDaoConfig;
@Autowired
protected IResourceTableDao myResourceTableDao;
@Autowired
protected PlatformTransactionManager myPlatformTransactionManager;
@Autowired
private IResourceHistoryTableDao myResourceHistoryTableDao;
@Autowired()
protected IResourceIndexedSearchParamUriDao myResourceIndexedSearchParamUriDao;
private String myResourceName;
@Autowired
protected IResourceTableDao myResourceTableDao;
private Class<T> myResourceType;
@Autowired(required = false)
protected IFulltextSearchSvc mySearchDao;
@ -226,7 +191,9 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
T resourceToDelete = toResource(myResourceType, entity, false);
validateOkToDelete(deleteConflicts, entity);
preDelete(resourceToDelete);
// Notify interceptors
if (theRequestDetails != null) {
ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getContext(), theId.getResourceType(), theId);
@ -259,10 +226,18 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
validateDeleteConflictsEmptyOrThrowException(deleteConflicts);
IBaseOperationOutcome oo = OperationOutcomeUtil.newInstance(getContext());
String message = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "successfulDeletes", 1, w.getMillis());
String severity = "information";
String code = "informational";
OperationOutcomeUtil.addIssue(getContext(), oo, severity, message, null, code);
ourLog.info("Processed delete on {} in {}ms", theId.getValue(), w.getMillisAndRestart());
return toMethodOutcome(savedEntity, null);
DaoMethodOutcome retVal = toMethodOutcome(savedEntity, null);
retVal.setOperationOutcome(oo);
return retVal;
}
@Override
public List<ResourceTable> deleteByUrl(String theUrl, List<DeleteConflict> deleteConflicts, RequestDetails theRequestDetails) {
Set<Long> resource = processMatchUrl(theUrl, myResourceType);
@ -301,7 +276,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
return retVal;
}
@Override
public DaoMethodOutcome deleteByUrl(String theUrl, RequestDetails theRequestDetails) {
StopWatch w = new StopWatch();
@ -320,7 +295,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
OperationOutcomeUtil.addIssue(getContext(), oo, severity, message, null, code);
} else {
oo = OperationOutcomeUtil.newInstance(getContext());
String message = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "successfulDeletes", theUrl, deletedResources.size(), w.getMillis());
String message = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "successfulDeletes", deletedResources.size(), w.getMillis());
String severity = "information";
String code = "informational";
OperationOutcomeUtil.addIssue(getContext(), oo, severity, message, null, code);
@ -553,6 +528,38 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
return retVal;
}
@Override
public <MT extends IBaseMetaType> MT metaDeleteOperation(IIdType theResourceId, MT theMetaDel, RequestDetails theRequestDetails) {
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getResourceName(), theResourceId);
notifyInterceptors(RestOperationTypeEnum.META_DELETE, requestDetails);
StopWatch w = new StopWatch();
BaseHasResource entity = readEntity(theResourceId);
if (entity == null) {
throw new ResourceNotFoundException(theResourceId);
}
ResourceTable latestVersion = readEntityLatestVersion(theResourceId);
if (latestVersion.getVersion() != entity.getVersion()) {
doMetaDelete(theMetaDel, entity);
} else {
doMetaDelete(theMetaDel, latestVersion);
// Also update history entry
ResourceHistoryTable history = myResourceHistoryTableDao.findForIdAndVersion(entity.getId(), entity.getVersion());
doMetaDelete(theMetaDel, history);
}
myEntityManager.flush();
ourLog.info("Processed metaDeleteOperation on {} in {}ms", new Object[] { theResourceId.getValue(), w.getMillisAndRestart() });
@SuppressWarnings("unchecked")
MT retVal = (MT) metaGetOperation(theMetaDel.getClass(), theResourceId, theRequestDetails);
return retVal;
}
// @Override
// public IBundleProvider everything(IIdType theId) {
// Search search = new Search();
@ -635,38 +642,6 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
// };
// }
@Override
public <MT extends IBaseMetaType> MT metaDeleteOperation(IIdType theResourceId, MT theMetaDel, RequestDetails theRequestDetails) {
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getResourceName(), theResourceId);
notifyInterceptors(RestOperationTypeEnum.META_DELETE, requestDetails);
StopWatch w = new StopWatch();
BaseHasResource entity = readEntity(theResourceId);
if (entity == null) {
throw new ResourceNotFoundException(theResourceId);
}
ResourceTable latestVersion = readEntityLatestVersion(theResourceId);
if (latestVersion.getVersion() != entity.getVersion()) {
doMetaDelete(theMetaDel, entity);
} else {
doMetaDelete(theMetaDel, latestVersion);
// Also update history entry
ResourceHistoryTable history = myResourceHistoryTableDao.findForIdAndVersion(entity.getId(), entity.getVersion());
doMetaDelete(theMetaDel, history);
}
myEntityManager.flush();
ourLog.info("Processed metaDeleteOperation on {} in {}ms", new Object[] { theResourceId.getValue(), w.getMillisAndRestart() });
@SuppressWarnings("unchecked")
MT retVal = (MT) metaGetOperation(theMetaDel.getClass(), theResourceId, theRequestDetails);
return retVal;
}
@Override
public <MT extends IBaseMetaType> MT metaGetOperation(Class<MT> theType, IIdType theId, RequestDetails theRequestDetails) {
// Notify interceptors
@ -743,6 +718,14 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
}
/**
* Subclasses may override to provide behaviour. Invoked within a delete
* transaction with the resource that is about to be deleted.
*/
protected void preDelete(T theResourceToDelete) {
// nothing by default
}
/**
* May be overridden by subclasses to validate resources prior to storage
*

View File

@ -27,6 +27,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;
import javax.persistence.Query;
import javax.persistence.Tuple;
@ -64,15 +65,17 @@ public abstract class BaseHapiFhirSystemDao<T, MT> extends BaseHapiFhirDao<IBase
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseHapiFhirSystemDao.class);
@Autowired
private PlatformTransactionManager myTxManager;
@Autowired
private IForcedIdDao myForcedIdDao;
private ReentrantLock myReindexLock = new ReentrantLock(false);
@Autowired
private ITermConceptDao myTermConceptDao;
@Autowired
private PlatformTransactionManager myTxManager;
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void deleteAllTagsOnServer(RequestDetails theRequestDetails) {
@ -205,8 +208,14 @@ public abstract class BaseHapiFhirSystemDao<T, MT> extends BaseHapiFhirDao<IBase
@Transactional()
@Override
public int markAllResourcesForReindexing() {
ourLog.info("Marking all resources as needing reindexing");
int retVal = myEntityManager.createQuery("UPDATE " + ResourceTable.class.getSimpleName() + " t SET t.myIndexStatus = null").executeUpdate();
ourLog.info("Marking all concepts as needing reindexing");
retVal += myTermConceptDao.markAllForReindexing();
ourLog.info("Done marking reindexing");
return retVal;
}
@ -266,16 +275,21 @@ public abstract class BaseHapiFhirSystemDao<T, MT> extends BaseHapiFhirDao<IBase
}
});
}
@Override
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public int performReindexingPass(final Integer theCount) {
if (!myReindexLock.tryLock()) {
return 0;
}
try {
return doPerformReindexingPass(theCount);
} catch (ReindexFailureException e) {
ourLog.warn("Reindexing failed for resource {}", e.getResourceId());
markResourceAsIndexingFailed(e.getResourceId());
return -1;
} finally {
myReindexLock.unlock();
}
}

View File

@ -492,7 +492,9 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle, MetaDt> {
InstantDt deletedInstantOrNull = ResourceMetadataKeyEnum.DELETED_AT.get(nextResource);
Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null;
boolean shouldUpdate = !nonUpdatedEntities.contains(nextOutcome.getEntity());
updateEntity(nextResource, nextOutcome.getEntity(), deletedTimestampOrNull, true, shouldUpdate, updateTime);
if (shouldUpdate) {
updateEntity(nextResource, nextOutcome.getEntity(), deletedTimestampOrNull, true, true, updateTime);
}
}
myEntityManager.flush();

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.jpa.dao.data;
import java.util.Collection;
/*
* #%L
* HAPI FHIR JPA Server
@ -32,5 +34,8 @@ public interface ITermConceptParentChildLinkDao extends JpaRepository<TermConcep
@Query("DELETE FROM TermConceptParentChildLink t WHERE t.myCodeSystem.myId = :cs_pid")
@Modifying
void deleteByCodeSystemVersion(@Param("cs_pid") Long thePid);
@Query("SELECT t FROM TermConceptParentChildLink t WHERE t.myChildPid = :child_pid")
Collection<TermConceptParentChildLink> findAllWithChild(@Param("child_pid") Long theConceptPid);
}

View File

@ -41,6 +41,7 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.springframework.beans.factory.annotation.Autowired;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem;
import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemVersionDao;
import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.entity.TermConcept;
@ -187,6 +188,9 @@ public class FhirResourceDaoCodeSystemDstu3 extends FhirResourceDaoDstu3<CodeSys
return retVal;
}
@Autowired
private ITermCodeSystemVersionDao myCsvDao;
@Override
protected ResourceTable updateEntity(IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, Date theUpdateTime) {
@ -198,12 +202,22 @@ public class FhirResourceDaoCodeSystemDstu3 extends FhirResourceDaoDstu3<CodeSys
String codeSystemUrl = cs.getUrl();
if (cs.getContent() == CodeSystemContentMode.COMPLETE || cs.getContent() == null) {
ourLog.info("CodeSystem {} has a status of {}, going to store concepts in terminology tables", retVal.getIdDt().getValue(), cs.getContentElement().getValueAsString());
TermCodeSystemVersion persCs = new TermCodeSystemVersion();
persCs.setResource(retVal);
persCs.setResourceVersionId(retVal.getVersion());
persCs.getConcepts().addAll(toPersistedConcepts(cs.getConcept(), persCs));
Long codeSystemResourcePid = retVal.getId();
TermCodeSystemVersion persCs = myCsvDao.findByCodeSystemResourceAndVersion(codeSystemResourcePid, retVal.getVersion());
if (persCs != null) {
ourLog.info("Code system version already exists in database");
} else {
persCs = new TermCodeSystemVersion();
persCs.setResource(retVal);
persCs.setResourceVersionId(retVal.getVersion());
persCs.getConcepts().addAll(toPersistedConcepts(cs.getConcept(), persCs));
ourLog.info("Code system has {} concepts", persCs.getConcepts().size());
myTerminologySvc.storeNewCodeSystemVersion(codeSystemResourcePid, codeSystemUrl, persCs);
}
myTerminologySvc.storeNewCodeSystemVersion(retVal.getId(), codeSystemUrl, persCs);
}
}

View File

@ -2,6 +2,8 @@ package ca.uhn.fhir.jpa.dao.dstu3;
import static org.apache.commons.lang3.StringUtils.isBlank;
import java.util.List;
/*
* #%L
* HAPI FHIR JPA Server
@ -33,6 +35,7 @@ import ca.uhn.fhir.jpa.dao.BaseSearchParamExtractor;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoSearchParameter;
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.util.DeleteConflict;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
@ -45,13 +48,16 @@ public class FhirResourceDaoSearchParameterDstu3 extends FhirResourceDaoDstu3<Se
private IFhirSystemDao<Bundle, Meta> mySystemDao;
private void markAffectedResources(SearchParameter theResource) {
String xpath = theResource.getXpath();
String resourceType = xpath.substring(0, xpath.indexOf('.'));
ourLog.info("Marking all resources of type {} for reindexing due to updated search parameter with path: {}", xpath);
int updatedCount = myResourceTableDao.markResourcesOfTypeAsRequiringReindexing(resourceType);
ourLog.info("Marked {} resources for reindexing", updatedCount);
if (theResource != null) {
String expression = theResource.getExpression();
String resourceType = expression.substring(0, expression.indexOf('.'));
ourLog.info("Marking all resources of type {} for reindexing due to updated search parameter with path: {}", expression);
int updatedCount = myResourceTableDao.markResourcesOfTypeAsRequiringReindexing(resourceType);
ourLog.info("Marked {} resources for reindexing", updatedCount);
}
}
/**
* This method is called once per minute to perform any required re-indexing. During most passes this will
* just check and find that there are no resources requiring re-indexing. In that case the method just returns
@ -77,13 +83,22 @@ public class FhirResourceDaoSearchParameterDstu3 extends FhirResourceDaoDstu3<Se
}
@Override
protected void preDelete(SearchParameter theResourceToDelete) {
super.preDelete(theResourceToDelete);
markAffectedResources(theResourceToDelete);
}
@Override
protected void postPersist(ResourceTable theEntity, SearchParameter theResource) {
super.postPersist(theEntity, theResource);
markAffectedResources(theResource);
}
@Override
protected void postUpdate(ResourceTable theEntity, SearchParameter theResource) {
super.postUpdate(theEntity, theResource);
markAffectedResources(theResource);
}
@ -92,37 +107,37 @@ public class FhirResourceDaoSearchParameterDstu3 extends FhirResourceDaoDstu3<Se
super.validateResourceForStorage(theResource, theEntityToSave);
if (theResource.getStatus() == null) {
throw new UnprocessableEntityException("Resource.status is missing or invalid: " + theResource.getStatusElement().getValueAsString());
throw new UnprocessableEntityException("SearchParameter.status is missing or invalid: " + theResource.getStatusElement().getValueAsString());
}
String xpath = theResource.getXpath();
if (isBlank(xpath)) {
throw new UnprocessableEntityException("Resource.xpath is missing");
String expression = theResource.getExpression();
if (isBlank(expression)) {
throw new UnprocessableEntityException("SearchParameter.expression is missing");
}
String[] xpathSplit = BaseSearchParamExtractor.SPLIT.split(xpath);
String[] expressionSplit = BaseSearchParamExtractor.SPLIT.split(expression);
String allResourceName = null;
for (String nextPath : xpathSplit) {
for (String nextPath : expressionSplit) {
int dotIdx = nextPath.indexOf('.');
if (dotIdx == -1) {
throw new UnprocessableEntityException("Invalid path value \"" + nextPath + "\". Must start with a resource name");
throw new UnprocessableEntityException("Invalid SearchParameter.expression value \"" + nextPath + "\". Must start with a resource name");
}
String resourceName = nextPath.substring(0, dotIdx);
try {
getContext().getResourceDefinition(resourceName);
} catch (DataFormatException e) {
throw new UnprocessableEntityException("Invalid path value \"" + nextPath + "\": " + e.getMessage());
throw new UnprocessableEntityException("Invalid SearchParameter.expression value \"" + nextPath + "\": " + e.getMessage());
}
if (allResourceName == null) {
allResourceName = resourceName;
} else {
if (!allResourceName.equals(resourceName)) {
throw new UnprocessableEntityException("Invalid path value \"" + nextPath + "\". All paths in a single SearchParameter must match the same resource type");
throw new UnprocessableEntityException("Invalid SearchParameter.expression value \"" + nextPath + "\". All paths in a single SearchParameter must match the same resource type");
}
}
}
}

View File

@ -383,7 +383,8 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
IFhirResourceDao resourceDao = getDaoOrThrowException(res.getClass());
res.setId((String) null);
DaoMethodOutcome outcome;
outcome = resourceDao.create(res, nextReqEntry.getRequest().getIfNoneExist(), false, theRequestDetails);
String matchUrl = nextReqEntry.getRequest().getIfNoneExist();
outcome = resourceDao.create(res, matchUrl, false, theRequestDetails);
handleTransactionCreateOrUpdateOutcome(idSubstitutions, idToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res);
entriesToProcess.put(nextRespEntry, outcome.getEntity());
if (outcome.getCreated() == false) {
@ -489,7 +490,9 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
IPrimitiveType<Date> deletedInstantOrNull = ResourceMetadataKeyEnum.DELETED_AT.get((IAnyResource) nextResource);
Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null;
boolean shouldUpdate = !nonUpdatedEntities.contains(nextOutcome.getEntity());
updateEntity(nextResource, nextOutcome.getEntity(), deletedTimestampOrNull, shouldUpdate, shouldUpdate, updateTime);
if (shouldUpdate) {
updateEntity(nextResource, nextOutcome.getEntity(), deletedTimestampOrNull, shouldUpdate, true, updateTime);
}
}
myEntityManager.flush();

View File

@ -155,7 +155,7 @@ public class SearchParamRegistryDstu3 extends BaseSearchParamRegistry {
private RuntimeSearchParam toRuntimeSp(SearchParameter theNextSp) {
String name = theNextSp.getCode();
String description = theNextSp.getDescription();
String path = theNextSp.getXpath();
String path = theNextSp.getExpression();
RestSearchParameterTypeEnum paramType = null;
RuntimeSearchParamStatusEnum status = null;
switch (theNextSp.getType()) {

View File

@ -233,12 +233,14 @@ public class TermConcept implements Serializable {
@PreUpdate
@PrePersist
public void prePersist() {
Set<Long> parentPids = new HashSet<Long>();
TermConcept entity = this;
parentPids(entity, parentPids);
entity.setParentPids(parentPids);
ourLog.trace("Code {}/{} has parents {}", entity.getId(), entity.getCode(), entity.getParentPidsAsString());
if (myParentPids == null) {
Set<Long> parentPids = new HashSet<Long>();
TermConcept entity = this;
parentPids(entity, parentPids);
entity.setParentPids(parentPids);
ourLog.trace("Code {}/{} has parents {}", entity.getId(), entity.getCode(), entity.getParentPidsAsString());
}
}
public void setCode(String theCode) {
@ -280,6 +282,10 @@ public class TermConcept implements Serializable {
myParentPids = b.toString();
}
public void setParentPids(String theParentPids) {
myParentPids = theParentPids;
}
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).append("code", myCode).append("display", myDisplay).build();

View File

@ -10,7 +10,7 @@ package ca.uhn.fhir.jpa.entity;
* 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,
@ -36,77 +36,38 @@ import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
@Entity
@Table(name="TRM_CONCEPT_PC_LINK")
@Table(name = "TRM_CONCEPT_PC_LINK")
public class TermConceptParentChildLink implements Serializable {
private static final long serialVersionUID = 1L;
@ManyToOne()
@JoinColumn(name="CHILD_PID", nullable=false, referencedColumnName="PID", foreignKey=@ForeignKey(name="FK_TERM_CONCEPTPC_CHILD"))
@JoinColumn(name = "CHILD_PID", nullable = false, referencedColumnName = "PID", foreignKey = @ForeignKey(name = "FK_TERM_CONCEPTPC_CHILD"))
private TermConcept myChild;
@Column(name = "CHILD_PID", insertable = false, updatable = false)
private Long myChildPid;
@ManyToOne()
@JoinColumn(name="CODESYSTEM_PID", nullable=false, foreignKey=@ForeignKey(name="FK_TERM_CONCEPTPC_CS"))
@JoinColumn(name = "CODESYSTEM_PID", nullable = false, foreignKey = @ForeignKey(name = "FK_TERM_CONCEPTPC_CS"))
private TermCodeSystemVersion myCodeSystem;
@ManyToOne(cascade= {})
@JoinColumn(name="PARENT_PID", nullable=false, referencedColumnName="PID", foreignKey=@ForeignKey(name="FK_TERM_CONCEPTPC_PARENT"))
@ManyToOne(cascade = {})
@JoinColumn(name = "PARENT_PID", nullable = false, referencedColumnName = "PID", foreignKey = @ForeignKey(name = "FK_TERM_CONCEPTPC_PARENT"))
private TermConcept myParent;
@Column(name = "PARENT_PID", insertable = false, updatable = false)
private Long myParentPid;
@Id()
@SequenceGenerator(name="SEQ_CONCEPT_PC_PID", sequenceName="SEQ_CONCEPT_PC_PID")
@GeneratedValue(strategy=GenerationType.AUTO, generator="SEQ_CONCEPT_PC_PID")
@Column(name="PID")
@SequenceGenerator(name = "SEQ_CONCEPT_PC_PID", sequenceName = "SEQ_CONCEPT_PC_PID")
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_CONCEPT_PC_PID")
@Column(name = "PID")
private Long myPid;
@Enumerated(EnumType.ORDINAL)
@Column(name="REL_TYPE", length=5, nullable=true)
@Column(name = "REL_TYPE", length = 5, nullable = true)
private RelationshipTypeEnum myRelationshipType;
public TermConcept getChild() {
return myChild;
}
public RelationshipTypeEnum getRelationshipType() {
return myRelationshipType;
}
public TermCodeSystemVersion getCodeSystem() {
return myCodeSystem;
}
public TermConcept getParent() {
return myParent;
}
public void setChild(TermConcept theChild) {
myChild = theChild;
}
public void setCodeSystem(TermCodeSystemVersion theCodeSystem) {
myCodeSystem = theCodeSystem;
}
public void setParent(TermConcept theParent) {
myParent = theParent;
}
public void setRelationshipType(RelationshipTypeEnum theRelationshipType) {
myRelationshipType = theRelationshipType;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((myChild == null) ? 0 : myChild.hashCode());
result = prime * result + ((myCodeSystem == null) ? 0 : myCodeSystem.hashCode());
result = prime * result + ((myParent == null) ? 0 : myParent.hashCode());
result = prime * result + ((myRelationshipType == null) ? 0 : myRelationshipType.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
@ -136,15 +97,64 @@ public class TermConceptParentChildLink implements Serializable {
return true;
}
public enum RelationshipTypeEnum{
// ********************************************
// IF YOU ADD HERE MAKE SURE ORDER IS PRESERVED
ISA
public TermConcept getChild() {
return myChild;
}
public Long getChildPid() {
return myChildPid;
}
public TermCodeSystemVersion getCodeSystem() {
return myCodeSystem;
}
public Long getId() {
return myPid;
}
public TermConcept getParent() {
return myParent;
}
public Long getParentPid() {
return myParentPid;
}
public RelationshipTypeEnum getRelationshipType() {
return myRelationshipType;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((myChild == null) ? 0 : myChild.hashCode());
result = prime * result + ((myCodeSystem == null) ? 0 : myCodeSystem.hashCode());
result = prime * result + ((myParent == null) ? 0 : myParent.hashCode());
result = prime * result + ((myRelationshipType == null) ? 0 : myRelationshipType.hashCode());
return result;
}
public void setChild(TermConcept theChild) {
myChild = theChild;
}
public void setCodeSystem(TermCodeSystemVersion theCodeSystem) {
myCodeSystem = theCodeSystem;
}
public void setParent(TermConcept theParent) {
myParent = theParent;
}
public void setRelationshipType(RelationshipTypeEnum theRelationshipType) {
myRelationshipType = theRelationshipType;
}
public enum RelationshipTypeEnum {
// ********************************************
// IF YOU ADD HERE MAKE SURE ORDER IS PRESERVED
ISA
}
}

View File

@ -23,9 +23,11 @@ package ca.uhn.fhir.jpa.term;
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.concurrent.TimeUnit;
@ -33,6 +35,7 @@ import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
@ -45,7 +48,10 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
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;
@ -66,6 +72,7 @@ import ca.uhn.fhir.util.ObjectUtil;
import ca.uhn.fhir.util.ValidateUtil;
public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
private static boolean ourForceSaveDeferredAlwaysForUnitTest;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseHapiTerminologySvc.class);
private static final Object PLACEHOLDER_OBJECT = new Object();
@ -94,8 +101,11 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
protected EntityManager myEntityManager;
private boolean myProcessDeferred = true;
private long myNextReindexPass;
private boolean myProcessDeferred = true;
@Autowired
private PlatformTransactionManager myTransactionMgr;
private boolean addToSet(Set<TermConcept> theSetToPopulate, TermConcept theConcept) {
boolean retVal = theSetToPopulate.add(theConcept);
@ -108,6 +118,25 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
return retVal;
}
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();
retVal += ensureParentsSaved(nextParent.getParents());
if (nextParent.getId() == null) {
myConceptDao.saveAndFlush(nextParent);
retVal++;
ourLog.debug("Saved parent code {} and got id {}", nextParent.getCode(), nextParent.getId());
}
}
}
return retVal;
}
private void fetchChildren(TermConcept theConcept, Set<TermConcept> theSetToPopulate) {
for (TermConceptParentChildLink nextChildLink : theConcept.getChildren()) {
TermConcept nextChild = nextChildLink.getChild();
@ -175,6 +204,15 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
return retVal;
}
/**
* Subclasses may override
* @param theSystem The code system
* @param theCode The code
*/
protected List<VersionIndependentConcept> findCodesAboveUsingBuiltInSystems(String theSystem, String theCode) {
return Collections.emptyList();
}
@Transactional(propagation = Propagation.REQUIRED)
@Override
public Set<TermConcept> findCodesBelow(Long theCodeSystemResourcePid, Long theCodeSystemVersionPid, String theCode) {
@ -206,7 +244,6 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
ArrayList<VersionIndependentConcept> retVal = toVersionIndependentConcepts(theSystem, codes);
return retVal;
}
/**
* Subclasses may override
* @param theSystem The code system
@ -215,16 +252,7 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
protected List<VersionIndependentConcept> findCodesBelowUsingBuiltInSystems(String theSystem, String theCode) {
return Collections.emptyList();
}
/**
* Subclasses may override
* @param theSystem The code system
* @param theCode The code
*/
protected List<VersionIndependentConcept> findCodesAboveUsingBuiltInSystems(String theSystem, String theCode) {
return Collections.emptyList();
}
private TermCodeSystemVersion findCurrentCodeSystemVersionForSystem(String theCodeSystem) {
TermCodeSystem cs = getCodeSystem(theCodeSystem);
if (cs == null || cs.getCurrentVersion() == null) {
@ -233,11 +261,12 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
TermCodeSystemVersion csv = cs.getCurrentVersion();
return csv;
}
private TermCodeSystem getCodeSystem(String theSystem) {
TermCodeSystem cs = myCodeSystemDao.findByCodeSystemUri(theSystem);
return cs;
}
private void persistChildren(TermConcept theConcept, TermCodeSystemVersion theCodeSystem, IdentityHashMap<TermConcept, Object> theConceptsStack, int theTotalConcepts) {
if (theConceptsStack.put(theConcept, PLACEHOLDER_OBJECT) != null) {
return;
@ -271,15 +300,99 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
}
private void saveConceptLink(TermConceptParentChildLink next) {
if (next.getId() == null) {
myConceptParentChildLinkDao.save(next);
private void populateVersion(TermConcept theNext, TermCodeSystemVersion theCodeSystemVersion) {
if (theNext.getCodeSystem() != null) {
return;
}
theNext.setCodeSystem(theCodeSystemVersion);
for (TermConceptParentChildLink next : theNext.getChildren()) {
populateVersion(next.getChild(), theCodeSystemVersion);
}
}
private ArrayListMultimap<Long, Long> myChildToParentPidCache;
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);
if (parents.contains(-1L)) {
return;
} else if (parents.isEmpty()) {
Collection<TermConceptParentChildLink> parentLinks = myConceptParentChildLinkDao.findAllWithChild(theConceptPid);
if (parentLinks.isEmpty()) {
myChildToParentPidCache.put(theConceptPid, -1L);
return;
} else {
for (TermConceptParentChildLink next : parentLinks) {
myChildToParentPidCache.put(theConceptPid, next.getParentPid());
}
}
}
for (Long nextParent : parents) {
if (theParentsBuilder.length() > 0) {
theParentsBuilder.append(' ');
}
theParentsBuilder.append(nextParent);
createParentsString(theParentsBuilder, nextParent);
}
}
});
}
private int saveConcept(TermConcept theConcept) {
int retVal = 0;
retVal += ensureParentsSaved(theConcept.getParents());
/*
* 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)
*/
if (theConcept.getId() == null) {
retVal += ensureParentsSaved(theConcept.getParents());
}
if (theConcept.getId() == null || theConcept.getIndexStatus() == null) {
retVal++;
theConcept.setIndexStatus(BaseHapiFhirDao.INDEX_STATUS_INDEXED);
@ -289,33 +402,10 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
ourLog.trace("Saved {} and got PID {}", theConcept.getCode(), theConcept.getId());
return retVal;
}
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();
retVal += ensureParentsSaved(nextParent.getParents());
if (nextParent.getId() == null) {
myConceptDao.saveAndFlush(nextParent);
retVal++;
ourLog.debug("Saved parent code {} and got id {}", nextParent.getCode(), nextParent.getId());
}
}
}
return retVal;
}
private void populateVersion(TermConcept theNext, TermCodeSystemVersion theCodeSystemVersion) {
if (theNext.getCodeSystem() != null) {
return;
}
theNext.setCodeSystem(theCodeSystemVersion);
for (TermConceptParentChildLink next : theNext.getChildren()) {
populateVersion(next.getChild(), theCodeSystemVersion);
private void saveConceptLink(TermConceptParentChildLink next) {
if (next.getId() == null) {
myConceptParentChildLinkDao.save(next);
}
}
@ -368,43 +458,7 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
ourLog.info("All deferred concepts and relationships have now been synchronized to the database");
}
}
@Autowired
private PlatformTransactionManager myTransactionMgr;
private void processReindexing() {
if (System.currentTimeMillis() < myNextReindexPass) {
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> resources = myConceptDao.findResourcesRequiringReindexing(new PageRequest(0, maxResult));
if (resources.hasContent() == false) {
myNextReindexPass = System.currentTimeMillis() + DateUtils.MILLIS_PER_MINUTE;
return;
}
ourLog.info("Indexing {} / {} concepts", resources.getContent().size(), resources.getTotalElements());
int count = 0;
StopWatch stopwatch = new StopWatch();
for (TermConcept resourceTable : resources) {
saveConcept(resourceTable);
count++;
}
ourLog.info("Indexed {} / {} concepts in {}ms - Avg {}ms / resource", new Object[] { count, resources.getContent().size(), stopwatch.getMillis(), stopwatch.getMillisPerOperation(count) });
}
});
}
@Override
public void setProcessDeferred(boolean theProcessDeferred) {
myProcessDeferred = theProcessDeferred;
@ -470,7 +524,7 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
totalCodeCount += validateConceptForStorage(next, theCodeSystemVersion, conceptsStack, allConcepts);
}
ourLog.info("Saving version");
ourLog.info("Saving version containing {} concepts", totalCodeCount);
TermCodeSystemVersion codeSystemVersion = myCodeSystemVersionDao.saveAndFlush(theCodeSystemVersion);
@ -503,13 +557,13 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
ourLog.info("Note that some concept saving was deferred - still have {} concepts and {} relationships", myConceptsToSaveLater.size(), myConceptLinksToSaveLater.size());
}
}
@Override
public boolean supportsSystem(String theSystem) {
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) {
@ -547,4 +601,12 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
return retVal;
}
/**
* This method is present only for unit tests, do not call from client code
*/
@VisibleForTesting
public static void setForceSaveDeferredAlwaysForUnitTest(boolean theForceSaveDeferredAlwaysForUnitTest) {
ourForceSaveDeferredAlwaysForUnitTest = theForceSaveDeferredAlwaysForUnitTest;
}
}

View File

@ -1,5 +1,6 @@
package ca.uhn.fhir.jpa.term;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
/*
@ -12,7 +13,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
* 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,
@ -64,6 +65,7 @@ import ca.uhn.fhir.jpa.entity.ResourceTable;
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.util.StopWatch;
import ca.uhn.fhir.rest.method.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
@ -79,7 +81,7 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvc implements I
@Autowired
private IValidationSupport myValidationSupport;
private void addCodeIfNotAlreadyAdded(String system, ValueSetExpansionComponent retVal, Set<String> addedCodes, TermConcept nextConcept) {
if (addedCodes.add(nextConcept.getCode())) {
ValueSetExpansionContainsComponent contains = retVal.addContains();
@ -126,12 +128,12 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvc implements I
for (ConceptDefinitionComponent nextChild : theNext.getConcept()) {
foundCodeInChild |= addTreeIfItContainsCode(theSystemString, nextChild, theCode, theListToPopulate);
}
if (theCode.equals(theNext.getCode()) || foundCodeInChild) {
theListToPopulate.add(new VersionIndependentConcept(theSystemString, theNext.getCode()));
return true;
}
return false;
}
@ -159,17 +161,15 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvc implements I
}
private void addDisplayFilterInexact(QueryBuilder qb, BooleanJunction<?> bool, ConceptSetFilterComponent nextFilter) {
//@formatter:off
Query textQuery = qb
.phrase()
.withSlop(2)
.onField("myDisplay").boostedTo(4.0f)
.andField("myDisplayEdgeNGram").boostedTo(2.0f)
//.andField("myDisplayNGram").boostedTo(1.0f)
//.andField("myDisplayPhonetic").boostedTo(0.5f)
// .andField("myDisplayNGram").boostedTo(1.0f)
// .andField("myDisplayPhonetic").boostedTo(0.5f)
.sentence(nextFilter.getValue().toLowerCase()).createQuery();
bool.must(textQuery);
//@formatter:on
}
@Override
@ -216,26 +216,33 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvc implements I
bool.must(qb.keyword().onField("myCodeSystemVersionPid").matching(csv.getPid()).createQuery());
for (ConceptSetFilterComponent nextFilter : theInclude.getFilter()) {
if (isNotBlank(nextFilter.getValue())) {
if (nextFilter.getProperty().equals("display:exact") && nextFilter.getOp() == FilterOperator.EQUAL) {
addDisplayFilterExact(qb, bool, nextFilter);
} else if (nextFilter.getProperty().equals("display") && nextFilter.getOp() == FilterOperator.EQUAL) {
if (nextFilter.getValue().trim().contains(" ")) {
addDisplayFilterExact(qb, bool, nextFilter);
} else {
addDisplayFilterInexact(qb, bool, nextFilter);
}
} else if ((nextFilter.getProperty().equals("concept") || nextFilter.getProperty().equals("code")) && nextFilter.getOp() == FilterOperator.ISA) {
TermConcept code = super.findCode(system, nextFilter.getValue());
if (code == null) {
throw new InvalidRequestException("Invalid filter criteria - code does not exist: {" + system + "}" + nextFilter.getValue());
}
if (isBlank(nextFilter.getValue()) && nextFilter.getOp() == null && isBlank(nextFilter.getProperty())) {
continue;
}
ourLog.info(" * Filtering on codes with a parent of {}/{}/{}", code.getId(), code.getCode(), code.getDisplay());
bool.must(qb.keyword().onField("myParentPids").matching("" + code.getId()).createQuery());
if (isBlank(nextFilter.getValue()) || nextFilter.getOp() == null || isBlank(nextFilter.getProperty())) {
throw new InvalidRequestException("Invalid filter, must have fields populated: property op value");
}
if (nextFilter.getProperty().equals("display:exact") && nextFilter.getOp() == FilterOperator.EQUAL) {
addDisplayFilterExact(qb, bool, nextFilter);
} else if ("display".equals(nextFilter.getProperty()) && nextFilter.getOp() == FilterOperator.EQUAL) {
if (nextFilter.getValue().trim().contains(" ")) {
addDisplayFilterExact(qb, bool, nextFilter);
} else {
throw new InvalidRequestException("Unknown filter property[" + nextFilter + "] + op[" + nextFilter.getOpElement().getValueAsString() + "]");
addDisplayFilterInexact(qb, bool, nextFilter);
}
} else if ((nextFilter.getProperty().equals("concept") || nextFilter.getProperty().equals("code")) && nextFilter.getOp() == FilterOperator.ISA) {
TermConcept code = super.findCode(system, nextFilter.getValue());
if (code == null) {
throw new InvalidRequestException("Invalid filter criteria - code does not exist: {" + system + "}" + nextFilter.getValue());
}
ourLog.info(" * Filtering on codes with a parent of {}/{}/{}", code.getId(), code.getCode(), code.getDisplay());
bool.must(qb.keyword().onField("myParentPids").matching("" + code.getId()).createQuery());
} else {
throw new InvalidRequestException("Unknown filter property[" + nextFilter + "] + op[" + nextFilter.getOpElement().getValueAsString() + "]");
}
}
@ -243,12 +250,17 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvc implements I
FullTextQuery jpaQuery = em.createFullTextQuery(luceneQuery, TermConcept.class);
jpaQuery.setMaxResults(1000);
StopWatch sw = new StopWatch();
@SuppressWarnings("unchecked")
List<TermConcept> result = jpaQuery.getResultList();
ourLog.info("Expansion completed in {}ms", sw.getMillis());
for (TermConcept nextConcept : result) {
addCodeIfNotAlreadyAdded(system, retVal, addedCodes, nextConcept);
}
retVal.setTotal(jpaQuery.getResultSize());
}
@ -259,7 +271,6 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvc implements I
}
}
return retVal;
}

View File

@ -19,10 +19,12 @@ import static org.mockito.Mockito.verify;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.io.IOUtils;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.AfterClass;
import org.junit.Test;
@ -73,6 +75,43 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirSystemDaoDstu2Test.class);
/**
* Per a message on the mailing list
*/
@Test
public void testTransactionWithPostDoesntUpdate() throws Exception {
// First bundle (name is Joshua)
String input = IOUtils.toString(getClass().getResource("/dstu3-post1.xml"), StandardCharsets.UTF_8);
Bundle request = myFhirCtx.newXmlParser().parseResource(Bundle.class, input);
Bundle response = mySystemDao.transaction(mySrd, request);
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(response));
assertEquals(1, response.getEntry().size());
assertEquals("201 Created", response.getEntry().get(0).getResponse().getStatus());
assertEquals("1", response.getEntry().get(0).getResponse().getEtag());
String id = response.getEntry().get(0).getResponse().getLocation();
// Now the second (name is Adam, shouldn't get used)
input = IOUtils.toString(getClass().getResource("/dstu3-post2.xml"), StandardCharsets.UTF_8);
request = myFhirCtx.newXmlParser().parseResource(Bundle.class, input);
response = mySystemDao.transaction(mySrd, request);
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(response));
assertEquals(1, response.getEntry().size());
assertEquals("200 OK", response.getEntry().get(0).getResponse().getStatus());
assertEquals("1", response.getEntry().get(0).getResponse().getEtag());
String id2 = response.getEntry().get(0).getResponse().getLocation();
assertEquals(id, id2);
Patient patient = myPatientDao.read(new IdType(id), mySrd);
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient));
assertEquals("Joshua", patient.getNameFirstRep().getGivenAsSingleString());
}
@Test
public void testReindexing() {
Patient p = new Patient();

View File

@ -0,0 +1,44 @@
package ca.uhn.fhir.jpa.dao.dstu3;
import static org.junit.Assert.assertNotEquals;
import java.nio.charset.StandardCharsets;
import org.apache.commons.io.IOUtils;
import org.hl7.fhir.dstu3.model.CodeSystem;
import org.junit.AfterClass;
import org.junit.Test;
import ca.uhn.fhir.jpa.term.BaseHapiTerminologySvc;
import ca.uhn.fhir.util.TestUtil;
public class FhirResourceDaoDstu3CodeSystemTest extends BaseJpaDstu3Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu3CodeSystemTest.class);
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
BaseHapiTerminologySvc.setForceSaveDeferredAlwaysForUnitTest(false);
}
@Test
public void testIndexContained() throws Exception {
BaseHapiTerminologySvc.setForceSaveDeferredAlwaysForUnitTest(true);
String input = IOUtils.toString(getClass().getResource("/dstu3_codesystem_complete.json"), StandardCharsets.UTF_8);
CodeSystem cs = myFhirCtx.newJsonParser().parseResource(CodeSystem.class, input);
myCodeSystemDao.create(cs, mySrd);
mySystemDao.markAllResourcesForReindexing();
int outcome = mySystemDao.performReindexingPass(100);
assertNotEquals(-1, outcome); // -1 means there was a failure
myTermSvc.saveDeferred();
}
}

View File

@ -1,8 +1,5 @@
package ca.uhn.fhir.jpa.dao.dstu3;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.junit.Assert.assertThat;
import org.hl7.fhir.dstu3.model.Observation;
import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.dstu3.model.Reference;
@ -11,9 +8,6 @@ import org.junit.AfterClass;
import org.junit.Test;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.param.TokenParamModifier;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.TestUtil;
public class FhirResourceDaoDstu3ContainedTest extends BaseJpaDstu3Test {

View File

@ -1,6 +1,7 @@
package ca.uhn.fhir.jpa.dao.dstu3;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.empty;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
@ -18,6 +19,7 @@ import org.junit.Test;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.util.TestUtil;
@ -30,14 +32,14 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP");
fooSp.setXpath("PatientFoo.gender");
fooSp.setExpression("PatientFoo.gender");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
try {
mySearchParameterDao.create(fooSp, mySrd);
fail();
} catch (UnprocessableEntityException e) {
assertEquals("Invalid path value \"PatientFoo.gender\": Unknown resource name \"PatientFoo\" (this name is not known in FHIR version \"DSTU3\")", e.getMessage());
assertEquals("Invalid SearchParameter.expression value \"PatientFoo.gender\": Unknown resource name \"PatientFoo\" (this name is not known in FHIR version \"DSTU3\")", e.getMessage());
}
}
@ -47,14 +49,14 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP");
fooSp.setXpath("Patient.gender or Observation.code");
fooSp.setExpression("Patient.gender or Observation.code");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
try {
mySearchParameterDao.create(fooSp, mySrd);
fail();
} catch (UnprocessableEntityException e) {
assertEquals("Invalid path value \"Observation.code\". All paths in a single SearchParameter must match the same resource type", e.getMessage());
assertEquals("Invalid SearchParameter.expression value \"Observation.code\". All paths in a single SearchParameter must match the same resource type", e.getMessage());
}
}
@ -70,7 +72,7 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
mySearchParameterDao.create(fooSp, mySrd);
fail();
} catch (UnprocessableEntityException e) {
assertEquals("Resource.xpath is missing", e.getMessage());
assertEquals("SearchParameter.expression is missing", e.getMessage());
}
}
@ -80,14 +82,14 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP");
fooSp.setXpath("gender");
fooSp.setExpression("gender");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
try {
mySearchParameterDao.create(fooSp, mySrd);
fail();
} catch (UnprocessableEntityException e) {
assertEquals("Invalid path value \"gender\". Must start with a resource name", e.getMessage());
assertEquals("Invalid SearchParameter.expression value \"gender\". Must start with a resource name", e.getMessage());
}
}
@ -98,14 +100,14 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP");
fooSp.setXpath("Patient.gender");
fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(null);
try {
mySearchParameterDao.create(fooSp, mySrd);
fail();
} catch (UnprocessableEntityException e) {
assertEquals("Resource.status is missing or invalid: null", e.getMessage());
assertEquals("SearchParameter.status is missing or invalid: null", e.getMessage());
}
}
@ -117,10 +119,10 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP");
fooSp.setXpath("Patient.gender");
fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
mySearchParameterDao.create(fooSp, mySrd);
IIdType spId = mySearchParameterDao.create(fooSp, mySrd).getId().toUnqualifiedVersionless();
mySearchParamRegsitry.forceRefresh();
@ -150,6 +152,17 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
foundResources = toUnqualifiedVersionlessIdValues(results);
assertThat(foundResources, contains(patId.getValue()));
// Delete the param
mySearchParameterDao.delete(spId, mySrd);
mySearchParamRegsitry.forceRefresh();
mySystemDao.performReindexingPass(100);
// Try with custom gender SP
map = new SearchParameterMap();
map.add("foo", new TokenParam(null, "male"));
IBundleProvider res = myPatientDao.search(map);
assertEquals(0, res.size());
}
@Test
@ -159,7 +172,7 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP");
fooSp.setXpath("Patient.gender");
fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.DRAFT);
mySearchParameterDao.create(fooSp, mySrd);

View File

@ -26,7 +26,9 @@ import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem.LookupCodeResult;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
@ -34,6 +36,8 @@ import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
import ca.uhn.fhir.jpa.term.BaseHapiTerminologySvc;
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.param.TokenParamModifier;
@ -52,6 +56,8 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
@After
public void after() {
myDaoConfig.setDeferIndexingForCodesystemsOfSize(new DaoConfig().getDeferIndexingForCodesystemsOfSize());
BaseHapiTerminologySvc.setForceSaveDeferredAlwaysForUnitTest(false);
}
@Before
@ -318,6 +324,71 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
}
}
@Test
public void testExpandWithIsAInExternalValueSet() {
createExternalCsAndLocalVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
include.addFilter().setOp(FilterOperator.ISA).setValue("childAA").setProperty("concept");
ValueSet result = myValueSetDao.expand(vs, null);
logAndValidateValueSet(result);
ArrayList<String> codes = toCodesContains(result.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("childAAA", "childAAB"));
}
@Autowired
private IHapiTerminologySvc myHapiTerminologySvc;
@Test
public void testExpandWithIsAInExternalValueSetReindex() {
BaseHapiTerminologySvc.setForceSaveDeferredAlwaysForUnitTest(true);
createExternalCsAndLocalVs();
mySystemDao.markAllResourcesForReindexing();
mySystemDao.performReindexingPass(100);
mySystemDao.performReindexingPass(100);
myHapiTerminologySvc.saveDeferred();
myHapiTerminologySvc.saveDeferred();
myHapiTerminologySvc.saveDeferred();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
include.addFilter().setOp(FilterOperator.ISA).setValue("childAA").setProperty("concept");
ValueSet result = myValueSetDao.expand(vs, null);
logAndValidateValueSet(result);
ArrayList<String> codes = toCodesContains(result.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("childAAA", "childAAB"));
}
@Test
public void testExpandInvalid() {
createExternalCsAndLocalVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
include.addFilter();
include.addFilter().setOp(FilterOperator.ISA).setValue("childAA");
try {
myValueSetDao.expand(vs, null);
fail();
} catch (InvalidRequestException e) {
assertEquals("Invalid filter, must have fields populated: property op value", e.getMessage());
}
}
@Test
public void testExpandWithSystemAndCodesInExternalValueSet() {
createExternalCsAndLocalVs();

View File

@ -382,6 +382,42 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
}
}
/**
* Per a message on the mailing list
*/
@Test
public void testTransactionWithPostDoesntUpdate() throws Exception {
// First bundle (name is Joshua)
String input = IOUtils.toString(getClass().getResource("/dstu3-post1.xml"), StandardCharsets.UTF_8);
Bundle request = myFhirCtx.newXmlParser().parseResource(Bundle.class, input);
Bundle response = mySystemDao.transaction(mySrd, request);
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(response));
assertEquals(1, response.getEntry().size());
assertEquals("201 Created", response.getEntry().get(0).getResponse().getStatus());
assertEquals("1", response.getEntry().get(0).getResponse().getEtag());
String id = response.getEntry().get(0).getResponse().getLocation();
// Now the second (name is Adam, shouldn't get used)
input = IOUtils.toString(getClass().getResource("/dstu3-post2.xml"), StandardCharsets.UTF_8);
request = myFhirCtx.newXmlParser().parseResource(Bundle.class, input);
response = mySystemDao.transaction(mySrd, request);
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(response));
assertEquals(1, response.getEntry().size());
assertEquals("200 OK", response.getEntry().get(0).getResponse().getStatus());
assertEquals("1", response.getEntry().get(0).getResponse().getEtag());
String id2 = response.getEntry().get(0).getResponse().getLocation();
assertEquals(id, id2);
Patient patient = myPatientDao.read(new IdType(id), mySrd);
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient));
assertEquals("Joshua", patient.getNameFirstRep().getGivenAsSingleString());
}
@Test
public void testTransactionCreateInlineMatchUrlWithOneMatch() {
String methodName = "testTransactionCreateInlineMatchUrlWithOneMatch";

View File

@ -10,6 +10,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
@ -139,7 +140,7 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
get.addHeader("Accept", "application/xml, text/html");
CloseableHttpResponse http = ourHttpClient.execute(get);
try {
String response = IOUtils.toString(http.getEntity().getContent());
String response = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(response);
assertThat(response, (containsString("_format=json")));
assertEquals(200, http.getStatusLine().getStatusCode());
@ -167,7 +168,7 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
get.addHeader("Accept", "application/xml+fhir");
CloseableHttpResponse http = ourHttpClient.execute(get);
try {
String response = IOUtils.toString(http.getEntity().getContent());
String response = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(response);
assertThat(response, not(containsString("_format")));
assertEquals(200, http.getStatusLine().getStatusCode());
@ -216,7 +217,7 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
CloseableHttpResponse http = ourHttpClient.execute(get);
try {
assertEquals(200, http.getStatusLine().getStatusCode());
String output = IOUtils.toString(http.getEntity().getContent());
String output = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(output);
Parameters parameters = ourCtx.newXmlParser().parseResource(Parameters.class, output);
@ -246,7 +247,7 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
CloseableHttpResponse http = ourHttpClient.execute(get);
try {
assertEquals(400, http.getStatusLine().getStatusCode());
String output = IOUtils.toString(http.getEntity().getContent());
String output = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(output);
assertThat(output, containsString("Parameter 'context' must be provided"));
} finally {
@ -257,7 +258,7 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
http = ourHttpClient.execute(get);
try {
assertEquals(400, http.getStatusLine().getStatusCode());
String output = IOUtils.toString(http.getEntity().getContent());
String output = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(output);
assertThat(output, containsString("Parameter 'searchParam' must be provided"));
} finally {
@ -268,7 +269,7 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
http = ourHttpClient.execute(get);
try {
assertEquals(400, http.getStatusLine().getStatusCode());
String output = IOUtils.toString(http.getEntity().getContent());
String output = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(output);
assertThat(output, containsString("Parameter 'text' must be provided"));
} finally {
@ -286,7 +287,7 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
@Test
public void testTransactionFromBundle() throws Exception {
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/transaction_link_patient_eve.xml");
String bundle = IOUtils.toString(bundleRes);
String bundle = IOUtils.toString(bundleRes, StandardCharsets.UTF_8);
String response = ourClient.transaction().withBundle(bundle).prettyPrint().execute();
ourLog.info(response);
}
@ -295,7 +296,7 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
public void testTransactionFromBundle2() throws Exception {
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/transaction_link_patient_eve_temp.xml");
String bundle = IOUtils.toString(bundleRes);
String bundle = IOUtils.toString(bundleRes, StandardCharsets.UTF_8);
String response = ourClient.transaction().withBundle(bundle).prettyPrint().execute();
ourLog.info(response);
@ -311,7 +312,7 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
*/
bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/transaction_link_patient_eve_temp.xml");
bundle = IOUtils.toString(bundleRes);
bundle = IOUtils.toString(bundleRes, StandardCharsets.UTF_8);
response = ourClient.transaction().withBundle(bundle).prettyPrint().execute();
ourLog.info(response);
@ -335,7 +336,7 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
public void testTransactionFromBundle3() throws Exception {
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/grahame-transaction.xml");
String bundle = IOUtils.toString(bundleRes);
String bundle = IOUtils.toString(bundleRes, StandardCharsets.UTF_8);
String response = ourClient.transaction().withBundle(bundle).prettyPrint().execute();
ourLog.info(response);
}
@ -343,7 +344,7 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
@Test
public void testTransactionFromBundle4() throws Exception {
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/simone_bundle.xml");
String bundle = IOUtils.toString(bundleRes);
String bundle = IOUtils.toString(bundleRes, StandardCharsets.UTF_8);
String response = ourClient.transaction().withBundle(bundle).prettyPrint().execute();
ourLog.info(response);
Bundle bundleResp = ourCtx.newXmlParser().parseResource(Bundle.class, response);
@ -358,7 +359,7 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
@Test
public void testTransactionFromBundle5() throws Exception {
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/simone_bundle2.xml");
String bundle = IOUtils.toString(bundleRes);
String bundle = IOUtils.toString(bundleRes, StandardCharsets.UTF_8);
try {
ourClient.transaction().withBundle(bundle).prettyPrint().execute();
fail();
@ -372,7 +373,7 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
@Test
public void testTransactionFromBundle6() throws Exception {
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/simone_bundle3.xml");
String bundle = IOUtils.toString(bundleRes);
String bundle = IOUtils.toString(bundleRes, StandardCharsets.UTF_8);
ourClient.transaction().withBundle(bundle).prettyPrint().execute();
// try {
// fail();
@ -427,5 +428,18 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
assertEquals(0, respSub.getEntry().size());
}
@Test
public void testMarkResourcesForReindexing() throws Exception {
HttpGet get = new HttpGet(ourServerBase + "/$mark-all-resources-for-reindexing");
CloseableHttpResponse http = ourHttpClient.execute(get);
try {
String output = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(output);
assertEquals(200, http.getStatusLine().getStatusCode());
} finally {
IOUtils.closeQuietly(http);;
}
}
}

View File

@ -58,7 +58,7 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv
public void saveCreateSearchParamInvalidWithMissingStatus() throws IOException {
SearchParameter sp = new SearchParameter();
sp.setCode("foo");
sp.setXpath("Patient.gender");
sp.setExpression("Patient.gender");
sp.setXpathUsage(XPathUsageType.NORMAL);
sp.setTitle("Foo Param");
@ -66,7 +66,7 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv
ourClient.create().resource(sp).execute();
fail();
} catch (UnprocessableEntityException e) {
assertEquals("HTTP 422 Unprocessable Entity: Resource.status is missing or invalid: null", e.getMessage());
assertEquals("HTTP 422 Unprocessable Entity: SearchParameter.status is missing or invalid: null", e.getMessage());
}
}
@ -100,7 +100,7 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP");
fooSp.setXpath("Patient.gender");
fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
mySearchParameterDao.create(fooSp, mySrd);
@ -110,7 +110,7 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv
fooSp.setCode("gender");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("Gender");
fooSp.setXpath("Patient.gender");
fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.RETIRED);
mySearchParameterDao.create(fooSp, mySrd);
@ -152,7 +152,7 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP");
fooSp.setXpath("Patient.gender");
fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
mySearchParameterDao.create(fooSp, mySrd);
@ -162,7 +162,7 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv
fooSp.setCode("gender");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("Gender");
fooSp.setXpath("Patient.gender");
fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.RETIRED);
mySearchParameterDao.create(fooSp, mySrd);
@ -206,7 +206,7 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP");
fooSp.setXpath("Patient.gender");
fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
mySearchParameterDao.create(fooSp, mySrd);
@ -232,6 +232,7 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv
.where(new TokenClientParam("foo").exactly().code("male"))
.returnBundle(Bundle.class)
.execute();
foundResources = toUnqualifiedVersionlessIdValues(result);
assertThat(foundResources, contains(patId.getValue()));
@ -256,7 +257,7 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP");
fooSp.setXpath("Patient.gender");
fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
mySearchParameterDao.create(fooSp, mySrd);
@ -276,7 +277,7 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.REFERENCE);
fooSp.setTitle("FOO SP");
fooSp.setXpath("Observation.subject");
fooSp.setExpression("Observation.subject");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
mySearchParameterDao.create(fooSp, mySrd);

View File

@ -149,7 +149,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
@After
public void after() throws Exception {
super.after();
myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete());
myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences());
}
@ -160,15 +160,14 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
myDaoConfig.setAllowMultipleDelete(true);
}
private void checkParamMissing(String paramName) throws IOException, ClientProtocolException {
HttpGet get = new HttpGet(ourServerBase + "/Observation?" + paramName + ":missing=false");
CloseableHttpResponse resp = ourHttpClient.execute(get);
IOUtils.closeQuietly(resp.getEntity().getContent());
assertEquals(200, resp.getStatusLine().getStatusCode());
}
private ArrayList<IBaseResource> genResourcesOfType(Bundle theRes, Class<? extends IBaseResource> theClass) {
ArrayList<IBaseResource> retVal = new ArrayList<IBaseResource>();
for (BundleEntryComponent next : theRes.getEntry()) {
@ -197,7 +196,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
List<Extension> exts = basic.getExtensionsByUrl("http://localhost:1080/hapi-fhir-jpaserver-example/baseDstu2/StructureDefinition/DateID");
assertEquals(1, exts.size());
}
private List<String> searchAndReturnUnqualifiedIdValues(String uri) throws IOException, ClientProtocolException {
List<String> ids;
HttpGet get = new HttpGet(uri);
@ -339,9 +338,9 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
Binary binary = new Binary();
binary.setContent(arr);
binary.setContentType("dansk");
IIdType resource = ourClient.create().resource(binary).execute().getId();
Binary fromDB = ourClient.read().resource(Binary.class).withId(resource.toVersionless()).execute();
assertEquals("1", fromDB.getIdElement().getVersionIdPart());
@ -355,7 +354,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
} finally {
IOUtils.closeQuietly(resp);
}
fromDB = ourClient.read().resource(Binary.class).withId(resource.toVersionless()).execute();
assertEquals("2", fromDB.getIdElement().getVersionIdPart());
@ -370,12 +369,12 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
} finally {
IOUtils.closeQuietly(resp);
}
fromDB = ourClient.read().resource(Binary.class).withId(resource.toVersionless()).execute();
assertEquals("3", fromDB.getIdElement().getVersionIdPart());
// Now an update with the wrong ID in the body
arr[0] = 4;
binary.setId("");
encoded = myFhirCtx.newJsonParser().encodeResourceToString(binary);
@ -387,7 +386,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
} finally {
IOUtils.closeQuietly(resp);
}
fromDB = ourClient.read().resource(Binary.class).withId(resource.toVersionless()).execute();
assertEquals("3", fromDB.getIdElement().getVersionIdPart());
@ -674,7 +673,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
String encoded = myFhirCtx.newXmlParser().encodeResourceToString(response);
ourLog.info(encoded);
assertThat(encoded, containsString(
"<issue><severity value=\"information\"/><code value=\"informational\"/><diagnostics value=\"Successfully deleted Patient?identifier=testDeleteConditionalMultiple resource(s) in 2ms\"/></issue>"));
"<issue><severity value=\"information\"/><code value=\"informational\"/><diagnostics value=\"Successfully deleted 2 resource(s) in "));
try {
ourClient.read().resource("Patient").withId(id1).execute();
fail();
@ -726,7 +725,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
* Test for #345
*/
@Test
public void testDeleteNormal() throws IOException {
public void testDeleteNormal() {
Patient p = new Patient();
p.addName().setFamily("FAM");
IIdType id = ourClient.create().resource(p).execute().getId().toUnqualifiedVersionless();
@ -743,6 +742,17 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
}
}
@Test
public void testDeleteReturnsOperationOutcome() {
Patient p = new Patient();
p.addName().setFamily("FAM");
IIdType id = ourClient.create().resource(p).execute().getId().toUnqualifiedVersionless();
IBaseOperationOutcome resp = ourClient.delete().resourceById(id).execute();
OperationOutcome oo = (OperationOutcome) resp;
assertThat(oo.getIssueFirstRep().getDiagnostics(), startsWith("Successfully deleted 1 resource(s) in "));
}
@Test
public void testDeleteResourceConditional1() throws IOException {
String methodName = "testDeleteResourceConditional1";
@ -768,6 +778,10 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
response = ourHttpClient.execute(delete);
try {
assertEquals(200, response.getStatusLine().getStatusCode());
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(resp);
OperationOutcome oo = myFhirCtx.newXmlParser().parseResource(OperationOutcome.class, resp);
assertThat(oo.getIssueFirstRep().getDiagnostics(), startsWith("Successfully deleted 1 resource(s) in "));
} finally {
response.close();
}
@ -777,6 +791,24 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
try {
ourLog.info(response.toString());
assertEquals(Constants.STATUS_HTTP_410_GONE, response.getStatusLine().getStatusCode());
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(resp);
OperationOutcome oo = myFhirCtx.newXmlParser().parseResource(OperationOutcome.class, resp);
assertThat(oo.getIssueFirstRep().getDiagnostics(), startsWith("Resource was deleted at"));
} finally {
response.close();
}
// Delete should now have no matches
delete = new HttpDelete(ourServerBase + "/Patient?name=" + methodName);
response = ourHttpClient.execute(delete);
try {
assertEquals(200, response.getStatusLine().getStatusCode());
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(resp);
OperationOutcome oo = myFhirCtx.newXmlParser().parseResource(OperationOutcome.class, resp);
assertThat(oo.getIssueFirstRep().getDiagnostics(), startsWith("Unable to find resource matching URL \"Patient?name=testDeleteResourceConditional1\". Deletion failed."));
} finally {
response.close();
}
@ -1781,7 +1813,10 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
ourLog.info("Response: {}", respString);
assertEquals(400, response.getStatusLine().getStatusCode());
OperationOutcome oo = myFhirCtx.newXmlParser().parseResource(OperationOutcome.class, respString);
assertEquals("Can not update resource, resource body must contain an ID element which matches the request URL for update (PUT) operation - Resource body ID of \"AAA\" does not match URL ID of \"" + id.getIdPart() + "\"", oo.getIssue().get(0).getDiagnostics());
assertEquals(
"Can not update resource, resource body must contain an ID element which matches the request URL for update (PUT) operation - Resource body ID of \"AAA\" does not match URL ID of \""
+ id.getIdPart() + "\"",
oo.getIssue().get(0).getDiagnostics());
} finally {
response.close();
}
@ -1809,12 +1844,11 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
@Test
public void testIncludeWithExternalReferences() {
myDaoConfig.setAllowExternalReferences(true);
Patient p = new Patient();
p.getManagingOrganization().setReference("http://example.com/Organization/123");
ourClient.create().resource(p).execute();
Bundle b = ourClient.search().forResource("Patient").include(Patient.INCLUDE_ORGANIZATION).returnBundle(Bundle.class).execute();
assertEquals(1, b.getEntry().size());
}
@ -1834,7 +1868,6 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
}
}
@Test
public void testMetadataSuperParamsAreIncluded() throws IOException {
StructureDefinition p = new StructureDefinition();
@ -1843,12 +1876,12 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
IIdType id = ourClient.create().resource(p).execute().getId().toUnqualifiedVersionless();
Bundle resp = ourClient
.search()
.forResource(StructureDefinition.class)
.where(StructureDefinition.URL.matches().value("http://example.com/foo"))
.returnBundle(Bundle.class)
.execute();
.search()
.forResource(StructureDefinition.class)
.where(StructureDefinition.URL.matches().value("http://example.com/foo"))
.returnBundle(Bundle.class)
.execute();
assertEquals(1, resp.getTotal());
}
@ -2101,9 +2134,10 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
String input = IOUtils.toString(getClass().getResourceAsStream("/two_questionnaires.json"), StandardCharsets.UTF_8);
String respString = ourClient.transaction().withBundle(input).prettyPrint().execute();
ourLog.info(respString);
ourHttpClient.execute(new HttpGet("http://localhost:" + ourPort + "/QuestionnaireResponse?patient=QR3295&questionnaire=profile&_sort:desc=authored&_count=5&_include=QuestionnaireResponse:questionnaire&_include=QuestionnaireResponse:subject"));
// Bundle bundle =
ourHttpClient.execute(new HttpGet("http://localhost:" + ourPort
+ "/QuestionnaireResponse?patient=QR3295&questionnaire=profile&_sort:desc=authored&_count=5&_include=QuestionnaireResponse:questionnaire&_include=QuestionnaireResponse:subject"));
// Bundle bundle =
}
@Test
@ -2284,14 +2318,14 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
}
Bundle found = ourClient
.search()
.forResource(Patient.class)
.where(BaseResource.RES_ID.exactly().systemAndValues(null, id1.getIdPart(), id2.getIdPart()))
.returnBundle(Bundle.class)
.execute();
.search()
.forResource(Patient.class)
.where(BaseResource.RES_ID.exactly().systemAndValues(null, id1.getIdPart(), id2.getIdPart()))
.returnBundle(Bundle.class)
.execute();
assertThat(toUnqualifiedVersionlessIds(found), empty());
found = ourClient
.search()
.forResource(Patient.class)
@ -2319,14 +2353,14 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
.execute();
assertThat(toUnqualifiedVersionlessIds(found), containsInAnyOrder(id1));
found = ourClient
.search()
.forResource(Patient.class)
.where(BaseResource.RES_ID.exactly().codes(Arrays.asList(id1.getIdPart(), id2.getIdPart(), "FOOOOO")))
.and(BaseResource.RES_ID.exactly().code(id1.getIdPart()))
.returnBundle(Bundle.class)
.execute();
.search()
.forResource(Patient.class)
.where(BaseResource.RES_ID.exactly().codes(Arrays.asList(id1.getIdPart(), id2.getIdPart(), "FOOOOO")))
.and(BaseResource.RES_ID.exactly().code(id1.getIdPart()))
.returnBundle(Bundle.class)
.execute();
assertThat(toUnqualifiedVersionlessIds(found), containsInAnyOrder(id1));
@ -3445,7 +3479,9 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
ourLog.info(responseString);
assertEquals(400, response.getStatusLine().getStatusCode());
OperationOutcome oo = myFhirCtx.newXmlParser().parseResource(OperationOutcome.class, responseString);
assertEquals("Can not update resource, resource body must contain an ID element which matches the request URL for update (PUT) operation - Resource body ID of \"333\" does not match URL ID of \"A2\"", oo.getIssue().get(0).getDiagnostics());
assertEquals(
"Can not update resource, resource body must contain an ID element which matches the request URL for update (PUT) operation - Resource body ID of \"333\" does not match URL ID of \"A2\"",
oo.getIssue().get(0).getDiagnostics());
} finally {
response.close();
}

View File

@ -129,7 +129,7 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
CloseableHttpResponse resp = ourHttpClient.execute(req);
try {
String encoded = IOUtils.toString(resp.getEntity().getContent());
String encoded = IOUtils.toString(resp.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(encoded);
assertThat(encoded, containsString("transaction-response"));
@ -162,7 +162,7 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
CloseableHttpResponse resp = ourHttpClient.execute(req);
try {
String encoded = IOUtils.toString(resp.getEntity().getContent());
String encoded = IOUtils.toString(resp.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(encoded);
assertThat(encoded, containsString("transaction-response"));
@ -271,14 +271,14 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
try {
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/questionnaire-sdc-profile-example-ussg-fht.xml");
String bundleStr = IOUtils.toString(bundleRes);
String bundleStr = IOUtils.toString(bundleRes, StandardCharsets.UTF_8);
HttpPost req = new HttpPost(ourServerBase);
req.setEntity(new StringEntity(bundleStr, ContentType.parse(Constants.CT_FHIR_XML + "; charset=utf-8")));
CloseableHttpResponse resp = ourHttpClient.execute(req);
try {
String encoded = IOUtils.toString(resp.getEntity().getContent());
String encoded = IOUtils.toString(resp.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(encoded);
//@formatter:off
@ -314,7 +314,7 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
CloseableHttpResponse http = ourHttpClient.execute(get);
try {
String response = IOUtils.toString(http.getEntity().getContent());
String response = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(response);
assertThat(response, containsString("_format=json"));
assertEquals(200, http.getStatusLine().getStatusCode());
@ -342,7 +342,7 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
get.addHeader("Accept", "application/xml+fhir");
CloseableHttpResponse http = ourHttpClient.execute(get);
try {
String response = IOUtils.toString(http.getEntity().getContent());
String response = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(response);
assertThat(response, not(containsString("_format")));
assertEquals(200, http.getStatusLine().getStatusCode());
@ -373,7 +373,7 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
HttpGet get = new HttpGet(ourServerBase + "/$mark-all-resources-for-reindexing");
CloseableHttpResponse http = ourHttpClient.execute(get);
try {
String output = IOUtils.toString(http.getEntity().getContent());
String output = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(output);
assertEquals(200, http.getStatusLine().getStatusCode());
} finally {
@ -404,7 +404,7 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
CloseableHttpResponse http = ourHttpClient.execute(get);
try {
assertEquals(200, http.getStatusLine().getStatusCode());
String output = IOUtils.toString(http.getEntity().getContent());
String output = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(output);
Parameters parameters = ourCtx.newXmlParser().parseResource(Parameters.class, output);
@ -434,7 +434,7 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
CloseableHttpResponse http = ourHttpClient.execute(get);
try {
assertEquals(400, http.getStatusLine().getStatusCode());
String output = IOUtils.toString(http.getEntity().getContent());
String output = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(output);
assertThat(output, containsString("Parameter 'context' must be provided"));
} finally {
@ -445,7 +445,7 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
http = ourHttpClient.execute(get);
try {
assertEquals(400, http.getStatusLine().getStatusCode());
String output = IOUtils.toString(http.getEntity().getContent());
String output = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(output);
assertThat(output, containsString("Parameter 'searchParam' must be provided"));
} finally {
@ -456,7 +456,7 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
http = ourHttpClient.execute(get);
try {
assertEquals(400, http.getStatusLine().getStatusCode());
String output = IOUtils.toString(http.getEntity().getContent());
String output = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(output);
assertThat(output, containsString("Parameter 'text' must be provided"));
} finally {
@ -500,7 +500,7 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
public void testTransactionFromBundle2() throws Exception {
InputStream bundleRes = SystemProviderDstu3Test.class.getResourceAsStream("/transaction_link_patient_eve_temp.xml");
String bundle = IOUtils.toString(bundleRes);
String bundle = IOUtils.toString(bundleRes, StandardCharsets.UTF_8);
String response = ourClient.transaction().withBundle(bundle).prettyPrint().execute();
ourLog.info(response);
@ -516,7 +516,7 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
*/
bundleRes = SystemProviderDstu3Test.class.getResourceAsStream("/transaction_link_patient_eve_temp.xml");
bundle = IOUtils.toString(bundleRes);
bundle = IOUtils.toString(bundleRes, StandardCharsets.UTF_8);
response = ourClient.transaction().withBundle(bundle).prettyPrint().execute();
ourLog.info(response);
@ -540,7 +540,7 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
public void testTransactionFromBundle3() throws Exception {
InputStream bundleRes = SystemProviderDstu3Test.class.getResourceAsStream("/grahame-transaction.xml");
String bundle = IOUtils.toString(bundleRes);
String bundle = IOUtils.toString(bundleRes, StandardCharsets.UTF_8);
String response = ourClient.transaction().withBundle(bundle).prettyPrint().execute();
ourLog.info(response);
}
@ -548,7 +548,7 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
@Test
public void testTransactionFromBundle4() throws Exception {
InputStream bundleRes = SystemProviderDstu3Test.class.getResourceAsStream("/simone_bundle.xml");
String bundle = IOUtils.toString(bundleRes);
String bundle = IOUtils.toString(bundleRes, StandardCharsets.UTF_8);
String response = ourClient.transaction().withBundle(bundle).prettyPrint().execute();
ourLog.info(response);
Bundle bundleResp = ourCtx.newXmlParser().parseResource(Bundle.class, response);
@ -563,7 +563,7 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
@Test
public void testTransactionFromBundle5() throws Exception {
InputStream bundleRes = SystemProviderDstu3Test.class.getResourceAsStream("/simone_bundle2.xml");
String bundle = IOUtils.toString(bundleRes);
String bundle = IOUtils.toString(bundleRes, StandardCharsets.UTF_8);
try {
ourClient.transaction().withBundle(bundle).prettyPrint().execute();
fail();
@ -577,7 +577,7 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
@Test
public void testTransactionFromBundle6() throws Exception {
InputStream bundleRes = SystemProviderDstu3Test.class.getResourceAsStream("/simone_bundle3.xml");
String bundle = IOUtils.toString(bundleRes);
String bundle = IOUtils.toString(bundleRes, StandardCharsets.UTF_8);
ourClient.transaction().withBundle(bundle).prettyPrint().execute();
// try {
// fail();

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<Bundle xmlns="http://hl7.org/fhir">
<type value="transaction"/>
<entry>
<resource>
<Patient>
<id value="ID613876"/>
<identifier>
<value value="ID613876"/>
</identifier>
<name>
<given value="Joshua"/>
</name>
</Patient>
</resource>
<request>
<method value="POST"/>
<url value="Patient/ID613876"/>
<ifNoneExist value="identifier=ID613876"/>
</request>
</entry>
</Bundle>

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<Bundle xmlns="http://hl7.org/fhir">
<type value="transaction"/>
<entry>
<resource>
<Patient>
<id value="ID613876"/>
<identifier>
<value value="ID613876"/>
</identifier>
<name>
<given value="Adam"/>
</name>
</Patient>
</resource>
<request>
<method value="POST"/>
<url value="Patient/ID613876"/>
<ifNoneExist value="identifier=ID613876"/>
</request>
</entry>
</Bundle>

View File

@ -0,0 +1,27 @@
{
"resourceType": "CodeSystem",
"id": "22472",
"meta": {
"versionId": "3",
"lastUpdated": "2017-01-27T10:53:39.457-05:00"
},
"url": "xvalue://dedalus.eu/mci/CodeSystem/AddressUse",
"name": "AddressUse",
"status": "active",
"publisher": "x1v1-mci",
"date": "2016-04-07",
"description": "AddressUse",
"caseSensitive": true,
"compositional": false,
"versionNeeded": false,
"content": "complete",
"concept": {
"code": "work",
"display": "Work",
"definition": "An office address. First choice for business related contacts during business hours.",
"designation": {
"language": "IT",
"value": "Lavoro"
}
}
}

View File

@ -25,7 +25,9 @@ import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.XMLUnit;
import org.hamcrest.collection.IsEmptyCollection;
@ -76,6 +78,24 @@ public class XmlParserDstu3Test {
ourCtx.setNarrativeGenerator(null);
}
/**
* See #551
*/
@Test
public void testXmlLargeAttribute() {
String largeString = StringUtils.leftPad("", (int) FileUtils.ONE_MB, 'A');
Patient p = new Patient();
p.addName().setFamily(largeString);
String encoded = ourCtx.newXmlParser().encodeResourceToString(p);
p = ourCtx.newXmlParser().parseResource(Patient.class, encoded);
assertEquals(largeString, p.getNameFirstRep().getFamily());
}
/**
* See #544
*/

View File

@ -98,7 +98,7 @@ public class GenericClientDstu3Test {
byte[] body = IOUtils.toByteArray(((HttpEntityEnclosingRequestBase) capt.getAllValues().get(0)).getEntity().getContent());
return body;
}
private String extractBodyAsString(ArgumentCaptor<HttpUriRequest> capt) throws IOException {
String body = IOUtils.toString(((HttpEntityEnclosingRequestBase) capt.getAllValues().get(0)).getEntity().getContent(), "UTF-8");
return body;
@ -120,6 +120,26 @@ public class GenericClientDstu3Test {
return capt;
}
@Test
public void testRevIncludeRecursive() throws ClientProtocolException, IOException {
ArgumentCaptor<HttpUriRequest> capt = prepareClientForSearchResponse();
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
int idx = 0;
client.search()
.forResource(EpisodeOfCare.class)
.where(EpisodeOfCare.PATIENT.hasId("123"))
.revInclude(Encounter.INCLUDE_EPISODEOFCARE)
.revInclude(Observation.INCLUDE_ENCOUNTER.asRecursive())
.returnBundle(Bundle.class)
.execute();
assertEquals("http://example.com/fhir/EpisodeOfCare?patient=123&_revinclude=Encounter%3Aepisodeofcare&_revinclude%3Arecurse=Observation%3Aencounter", capt.getAllValues().get(idx).getURI().toString());
idx++;
}
@Test
public void testPatchJsonByIdString() throws Exception {
OperationOutcome conf = new OperationOutcome();
@ -143,20 +163,21 @@ public class GenericClientDstu3Test {
String patch = "[ { \"op\":\"replace\", \"path\":\"/active\", \"value\":false } ]";
MethodOutcome outcome = client
.patch()
.withBody(patch)
.withId("Patient/123")
.execute();
.patch()
.withBody(patch)
.withId("Patient/123")
.execute();
assertEquals("http://example.com/fhir/Patient/123", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString()));
assertEquals("PATCH", capt.getAllValues().get(0).getRequestLine().getMethod());
assertEquals(patch, extractBodyAsString(capt));
assertEquals(Constants.CT_JSON_PATCH, capt.getAllValues().get(idx).getFirstHeader("Content-Type").getValue().replaceAll(";.*", ""));
idx++;
OperationOutcome oo = (OperationOutcome) outcome.getOperationOutcome();
assertThat(oo.getText().getDivAsString(), containsString("OK!"));
}
@Test
public void testPatchJsonByIdType() throws Exception {
OperationOutcome conf = new OperationOutcome();
@ -180,20 +201,21 @@ public class GenericClientDstu3Test {
String patch = "[ { \"op\":\"replace\", \"path\":\"/active\", \"value\":false } ]";
MethodOutcome outcome = client
.patch()
.withBody(patch)
.withId(new IdType("http://localhost/fhir/Patient/123/_history/234"))
.execute();
.patch()
.withBody(patch)
.withId(new IdType("http://localhost/fhir/Patient/123/_history/234"))
.execute();
assertEquals("http://example.com/fhir/Patient/123", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString()));
assertEquals("PATCH", capt.getAllValues().get(0).getRequestLine().getMethod());
assertEquals(patch, extractBodyAsString(capt));
assertEquals(Constants.CT_JSON_PATCH, capt.getAllValues().get(idx).getFirstHeader("Content-Type").getValue().replaceAll(";.*", ""));
idx++;
OperationOutcome oo = (OperationOutcome) outcome.getOperationOutcome();
assertThat(oo.getText().getDivAsString(), containsString("OK!"));
}
@Test
public void testPatchJsonByConditionalString() throws Exception {
OperationOutcome conf = new OperationOutcome();
@ -217,20 +239,21 @@ public class GenericClientDstu3Test {
String patch = "[ { \"op\":\"replace\", \"path\":\"/active\", \"value\":false } ]";
MethodOutcome outcome = client
.patch()
.withBody(patch)
.conditionalByUrl("Patient?foo=bar")
.execute();
.patch()
.withBody(patch)
.conditionalByUrl("Patient?foo=bar")
.execute();
assertEquals("http://example.com/fhir/Patient?foo=bar", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString()));
assertEquals("PATCH", capt.getAllValues().get(0).getRequestLine().getMethod());
assertEquals(patch, extractBodyAsString(capt));
assertEquals(Constants.CT_JSON_PATCH, capt.getAllValues().get(idx).getFirstHeader("Content-Type").getValue().replaceAll(";.*", ""));
idx++;
OperationOutcome oo = (OperationOutcome) outcome.getOperationOutcome();
assertThat(oo.getText().getDivAsString(), containsString("OK!"));
}
@Test
public void testPatchJsonByConditionalParam() throws Exception {
OperationOutcome conf = new OperationOutcome();
@ -254,21 +277,22 @@ public class GenericClientDstu3Test {
String patch = "[ { \"op\":\"replace\", \"path\":\"/active\", \"value\":false } ]";
MethodOutcome outcome = client
.patch()
.withBody(patch)
.conditional("Patient").where(Patient.NAME.matches().value("TEST"))
.and(Patient.FAMILY.matches().value("TEST2"))
.execute();
.patch()
.withBody(patch)
.conditional("Patient").where(Patient.NAME.matches().value("TEST"))
.and(Patient.FAMILY.matches().value("TEST2"))
.execute();
assertEquals("http://example.com/fhir/Patient?name=TEST&family=TEST2", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString()));
assertEquals("PATCH", capt.getAllValues().get(0).getRequestLine().getMethod());
assertEquals(patch, extractBodyAsString(capt));
assertEquals(Constants.CT_JSON_PATCH, capt.getAllValues().get(idx).getFirstHeader("Content-Type").getValue().replaceAll(";.*", ""));
idx++;
OperationOutcome oo = (OperationOutcome) outcome.getOperationOutcome();
assertThat(oo.getText().getDivAsString(), containsString("OK!"));
}
@Test
public void testPatchJsonByConditionalParamResourceType() throws Exception {
OperationOutcome conf = new OperationOutcome();
@ -292,21 +316,22 @@ public class GenericClientDstu3Test {
String patch = "[ { \"op\":\"replace\", \"path\":\"/active\", \"value\":false } ]";
MethodOutcome outcome = client
.patch()
.withBody(patch)
.conditional(Patient.class).where(Patient.NAME.matches().value("TEST"))
.and(Patient.FAMILY.matches().value("TEST2"))
.execute();
.patch()
.withBody(patch)
.conditional(Patient.class).where(Patient.NAME.matches().value("TEST"))
.and(Patient.FAMILY.matches().value("TEST2"))
.execute();
assertEquals("http://example.com/fhir/Patient?name=TEST&family=TEST2", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString()));
assertEquals("PATCH", capt.getAllValues().get(0).getRequestLine().getMethod());
assertEquals(patch, extractBodyAsString(capt));
assertEquals(Constants.CT_JSON_PATCH, capt.getAllValues().get(idx).getFirstHeader("Content-Type").getValue().replaceAll(";.*", ""));
idx++;
OperationOutcome oo = (OperationOutcome) outcome.getOperationOutcome();
assertThat(oo.getText().getDivAsString(), containsString("OK!"));
}
@Test
public void testPatchXmlByIdString() throws Exception {
OperationOutcome conf = new OperationOutcome();
@ -330,31 +355,31 @@ public class GenericClientDstu3Test {
String patch = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><diff xmlns:fhir=\"http://hl7.org/fhir\"><replace sel=\"fhir:Patient/fhir:active/@value\">false</replace></diff>";
MethodOutcome outcome = client
.patch()
.withBody(patch)
.withId("Patient/123")
.execute();
.patch()
.withBody(patch)
.withId("Patient/123")
.execute();
assertEquals("http://example.com/fhir/Patient/123", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString()));
assertEquals("PATCH", capt.getAllValues().get(0).getRequestLine().getMethod());
assertEquals(patch, extractBodyAsString(capt));
assertEquals(Constants.CT_XML_PATCH, capt.getAllValues().get(idx).getFirstHeader("Content-Type").getValue().replaceAll(";.*", ""));
idx++;
OperationOutcome oo = (OperationOutcome) outcome.getOperationOutcome();
assertThat(oo.getText().getDivAsString(), containsString("OK!"));
}
@Test
public void testPatchInvalid() throws Exception {
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
try {
client
.patch()
.withBody("AA")
.withId("Patient/123")
.execute();
.patch()
.withBody("AA")
.withId("Patient/123")
.execute();
} catch (IllegalArgumentException e) {
assertEquals("Unable to determine encoding of patch", e.getMessage());
}
@ -388,7 +413,7 @@ public class GenericClientDstu3Test {
assertEquals("http://example.com/fhir/Device?_format=json", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString()));
assertEquals("application/fhir+json;q=1.0, application/json+fhir;q=0.9", capt.getAllValues().get(idx).getFirstHeader(Constants.HEADER_ACCEPT).getValue());
idx++;
//@formatter:off
client.setEncoding(EncodingEnum.XML);
client.search()
@ -399,7 +424,7 @@ public class GenericClientDstu3Test {
assertEquals("http://example.com/fhir/Device?_format=xml", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString()));
assertEquals("application/fhir+xml;q=1.0, application/xml+fhir;q=0.9", capt.getAllValues().get(idx).getFirstHeader(Constants.HEADER_ACCEPT).getValue());
idx++;
}
@Test
@ -480,9 +505,7 @@ public class GenericClientDstu3Test {
assertArrayEquals(new byte[] { 0, 1, 2, 3, 4 }, ourCtx.newXmlParser().parseResource(Binary.class, extractBodyAsString(capt)).getContent());
}
@SuppressWarnings("unchecked")
@Test
public void testClientFailures() throws Exception {
@ -592,7 +615,7 @@ public class GenericClientDstu3Test {
assertEquals(myAnswerCount, capt.getAllValues().size());
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("http://foo.com/base/Patient/222/_history/3", capt.getAllValues().get(1).getURI().toASCIIString());
}
@ -1027,7 +1050,7 @@ public class GenericClientDstu3Test {
assertEquals("http://example.com/fhir/Patient", capt.getAllValues().get(idx).getURI().toString());
idx++;
//@formatter:off
client
.search()
@ -1053,22 +1076,22 @@ public class GenericClientDstu3Test {
idx++;
}
@Test
public void testPutDoesntForceAllIdsJson() throws Exception {
IParser p = ourCtx.newJsonParser();
Patient patient = new Patient();
patient.setId("PATIENT1");
patient.addName().setFamily("PATIENT1");
Bundle bundle = new Bundle();
bundle.setId("BUNDLE1");
bundle.addEntry().setResource(patient);
final String encoded = p.encodeResourceToString(bundle);
assertEquals("{\"resourceType\":\"Bundle\",\"id\":\"BUNDLE1\",\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"PATIENT1\",\"name\":[{\"family\":\"PATIENT1\"}]}}]}", encoded);
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"));
@ -1251,7 +1274,7 @@ public class GenericClientDstu3Test {
.returnBundle(Bundle.class)
.execute();
//@formatter:on
assertEquals("http://example.com/fhir/Patient?birthdate=gt"+dateString, capt.getAllValues().get(idx).getURI().toString());
assertEquals("http://example.com/fhir/Patient?birthdate=gt" + dateString, capt.getAllValues().get(idx).getURI().toString());
idx++;
//@formatter:off
@ -1261,7 +1284,7 @@ public class GenericClientDstu3Test {
.returnBundle(Bundle.class)
.execute();
//@formatter:on
assertEquals("http://example.com/fhir/Patient?birthdate=gt"+dateString, capt.getAllValues().get(idx).getURI().toString());
assertEquals("http://example.com/fhir/Patient?birthdate=gt" + dateString, capt.getAllValues().get(idx).getURI().toString());
idx++;
//@formatter:off
@ -1271,7 +1294,7 @@ public class GenericClientDstu3Test {
.returnBundle(Bundle.class)
.execute();
//@formatter:on
assertEquals("http://example.com/fhir/Patient?birthdate=ge"+dateString, capt.getAllValues().get(idx).getURI().toString());
assertEquals("http://example.com/fhir/Patient?birthdate=ge" + dateString, capt.getAllValues().get(idx).getURI().toString());
idx++;
//@formatter:off
@ -1281,7 +1304,7 @@ public class GenericClientDstu3Test {
.returnBundle(Bundle.class)
.execute();
//@formatter:on
assertEquals("http://example.com/fhir/Patient?birthdate=lt"+dateString, capt.getAllValues().get(idx).getURI().toString());
assertEquals("http://example.com/fhir/Patient?birthdate=lt" + dateString, capt.getAllValues().get(idx).getURI().toString());
idx++;
//@formatter:off
@ -1291,7 +1314,7 @@ public class GenericClientDstu3Test {
.returnBundle(Bundle.class)
.execute();
//@formatter:on
assertEquals("http://example.com/fhir/Patient?birthdate=le"+dateString, capt.getAllValues().get(idx).getURI().toString());
assertEquals("http://example.com/fhir/Patient?birthdate=le" + dateString, capt.getAllValues().get(idx).getURI().toString());
idx++;
//@formatter:off
@ -1301,7 +1324,7 @@ public class GenericClientDstu3Test {
.returnBundle(Bundle.class)
.execute();
//@formatter:on
assertEquals("http://example.com/fhir/Patient?birthdate="+dateString, capt.getAllValues().get(idx).getURI().toString());
assertEquals("http://example.com/fhir/Patient?birthdate=" + dateString, capt.getAllValues().get(idx).getURI().toString());
idx++;
//@formatter:off
@ -1639,7 +1662,7 @@ public class GenericClientDstu3Test {
int idx = 0;
Collection<String> values = Arrays.asList("VAL1", "VAL2", "VAL3A,B");
//@formatter:off
client.search()
.forResource("Patient")
@ -1743,7 +1766,6 @@ public class GenericClientDstu3Test {
assertEquals("Unable to determing encoding of request (body does not appear to be valid XML or JSON)", e.getMessage());
}
}
@Test
@ -2012,7 +2034,7 @@ public class GenericClientDstu3Test {
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);
@ -2028,7 +2050,7 @@ public class GenericClientDstu3Test {
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
MyPatientWithExtensions read = client.read().resource(MyPatientWithExtensions.class).withId(new IdType("1")).execute();
assertEquals("<div xmlns=\"http://www.w3.org/1999/xhtml\">OK!</div>", read.getText().getDivAsString());
// Ensure that we haven't overridden the default type for the name
assertFalse(MyPatientWithExtensions.class.isAssignableFrom(Patient.class));
assertFalse(Patient.class.isAssignableFrom(MyPatientWithExtensions.class));
@ -2037,7 +2059,7 @@ public class GenericClientDstu3Test {
IParser parser = ourCtx.newXmlParser();
String encoded = parser.encodeResourceToString(pt);
pt = (Patient) parser.parseResource(encoded);
}
@Test
@ -2045,13 +2067,12 @@ public class GenericClientDstu3Test {
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);
@ -2066,7 +2087,7 @@ public class GenericClientDstu3Test {
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
Bundle bundle = client.search().forResource(MyPatientWithExtensions.class).returnBundle(Bundle.class).execute();
assertEquals(1, bundle.getEntry().size());
assertEquals(MyPatientWithExtensions.class, bundle.getEntry().get(0).getResource().getClass());
}
@ -2082,8 +2103,6 @@ public class GenericClientDstu3Test {
assertEquals(expectedUserAgent(), capt.getAllValues().get(0).getHeaders("User-Agent")[0].getValue());
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
@ -2093,5 +2112,5 @@ public class GenericClientDstu3Test {
public static void beforeClass() {
ourCtx = FhirContext.forDstu3();
}
}

View File

@ -1,8 +1,7 @@
package ca.uhn.fhir.rest.server;
import static org.hamcrest.Matchers.either;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.oneOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
@ -65,7 +64,7 @@ public class SearchDefaultMethodDstu3Test {
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertThat(ourLastMethod, either(equalTo("search01")).or(equalTo("search02")));
assertThat(ourLastMethod, oneOf("search01", "search02", "search03"));
assertEquals(null, ourLastParam1);
assertEquals(null, ourLastParam2);
assertEquals(null, ourLastAdditionalParams);

View File

@ -123,6 +123,21 @@
Clean up dependencies and remove Eclipse project files from git. Thanks to
@sekaijin for the pull request!
</action>
<action type="fix">
When performing a conditional create in a transaction in JPA server,
if a resource already existed matching the conditional expression, the
server did not change the version of the resource but did update the body
with the passed in body. Thanks to Artem Sopin for reporting and providing a test
case for this!
</action>
<action type="fix">
Client revincludes did not include the :recurse modifier. Thanks to
Jenny Meinsma for pointing this out on Zulip!
</action>
<action type="add">
JPA server did not return an OperationOutcome in the response for
a normal delete operation.
</action>
</release>
<release version="2.2" date="2016-12-20">
<action type="add">