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,8 +1889,12 @@ public class GenericClient extends BaseClient implements IGenericClient {
} }
for (Include next : myRevInclude) { for (Include next : myRevInclude) {
if (next.isRecurse()) {
addParam(params, Constants.PARAM_REVINCLUDE_RECURSE, next.getValue());
} else {
addParam(params, Constants.PARAM_REVINCLUDE, next.getValue()); addParam(params, Constants.PARAM_REVINCLUDE, next.getValue());
} }
}
if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU2)) { if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU2)) {
SortSpec rootSs = null; SortSpec rootSs = null;

View File

@ -40,11 +40,13 @@ public class Constants {
public static final String CT_HTML = "text/html"; 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_HTML_WITH_UTF8 = "text/html" + CHARSET_UTF8_CTSUFFIX;
public static final String CT_JSON = "application/json"; 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_OCTET_STREAM = "application/octet-stream";
public static final String CT_TEXT = "text/plain"; 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_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_X_FORM_URLENCODED = "application/x-www-form-urlencoded";
public static final String CT_XML = "application/xml"; 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 ENCODING_GZIP = "gzip";
public static final String EXTOP_VALIDATE = "$validate"; public static final String EXTOP_VALIDATE = "$validate";
public static final String EXTOP_VALIDATE_MODE = "mode"; 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 FORMATS_HTML_XML = "html/xml";
public static final String HEADER_ACCEPT = "Accept"; public static final String HEADER_ACCEPT = "Accept";
public static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding"; 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_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_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_ALLOW = "Allow";
public static final String HEADER_AUTHORIZATION = "Authorization"; public static final String HEADER_AUTHORIZATION = "Authorization";
public static final String HEADER_AUTHORIZATION_VALPREFIX_BASIC = "Basic "; 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_PAGINGACTION = "_getpages";
public static final String PARAM_PAGINGOFFSET = "_getpagesoffset"; public static final String PARAM_PAGINGOFFSET = "_getpagesoffset";
public static final String PARAM_PRETTY = "_pretty"; 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_PRETTY_VALUE_TRUE = "true";
public static final String PARAM_PROFILE = "_profile"; public static final String PARAM_PROFILE = "_profile";
public static final String PARAM_QUERY = "_query"; public static final String PARAM_QUERY = "_query";
public static final String PARAM_REVINCLUDE = "_revinclude"; 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_SEARCH = "_search";
public static final String PARAM_SECURITY = "_security"; public static final String PARAM_SECURITY = "_security";
public static final String PARAM_SINCE = "_since"; 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_401_CLIENT_UNAUTHORIZED = 401;
public static final int STATUS_HTTP_403_FORBIDDEN = 403; 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_404_NOT_FOUND = 404;
public static final int STATUS_HTTP_405_METHOD_NOT_ALLOWED = 405; 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_409_CONFLICT = 409;
public static final int STATUS_HTTP_410_GONE = 410; 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_412_PRECONDITION_FAILED = 412;
public static final int STATUS_HTTP_422_UNPROCESSABLE_ENTITY = 422; 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_500_INTERNAL_ERROR = 500;
public static final int STATUS_HTTP_501_NOT_IMPLEMENTED = 501; 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 TAG_SUBSETTED_SYSTEM = "http://hl7.org/fhir/v3/ObservationValue";
public static final String URL_TOKEN_HISTORY = "_history"; public static final String URL_TOKEN_HISTORY = "_history";
public static final String URL_TOKEN_METADATA = "metadata"; 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 { static {
CHARSET_UTF8 = Charset.forName(CHARSET_NAME_UTF8); CHARSET_UTF8 = Charset.forName(CHARSET_NAME_UTF8);

View File

@ -22,6 +22,7 @@ package ca.uhn.fhir.util;
import java.util.Collection; 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.IBase;
import org.hl7.fhir.instance.model.api.IBaseDatatype; import org.hl7.fhir.instance.model.api.IBaseDatatype;
import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseParameters;
@ -84,6 +85,7 @@ public class ParametersUtil {
} }
public static IBaseParameters newInstance(FhirContext theContext) { public static IBaseParameters newInstance(FhirContext theContext) {
Validate.notNull(theContext, "theContext must not be null");
return (IBaseParameters) theContext.getResourceDefinition("Parameters").newInstance(); 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() { public ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2 systemProviderDstu2() {
ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2 retVal = new ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2(); ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2 retVal = new ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2();
retVal.setDao(systemDaoDstu2()); retVal.setDao(systemDaoDstu2());
retVal.setContext(fhirContextDstu2());
return retVal; return retVal;
} }

View File

@ -22,29 +22,15 @@ package ca.uhn.fhir.jpa.dao;
import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.util.ArrayList; import java.util.*;
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.Map.Entry; import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.persistence.NoResultException; import javax.persistence.NoResultException;
import javax.persistence.TypedQuery; import javax.persistence.TypedQuery;
import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.*;
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.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required; import org.springframework.beans.factory.annotation.Required;
import org.springframework.transaction.PlatformTransactionManager; 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.IResourceIndexedSearchParamUriDao;
import ca.uhn.fhir.jpa.dao.data.IResourceTableDao; import ca.uhn.fhir.jpa.dao.data.IResourceTableDao;
import ca.uhn.fhir.jpa.dao.data.ISearchResultDao; import ca.uhn.fhir.jpa.dao.data.ISearchResultDao;
import ca.uhn.fhir.jpa.entity.BaseHasResource; import ca.uhn.fhir.jpa.entity.*;
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.interceptor.IJpaServerInterceptor; import ca.uhn.fhir.jpa.interceptor.IJpaServerInterceptor;
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc; import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
import ca.uhn.fhir.jpa.util.DeleteConflict; import ca.uhn.fhir.jpa.util.DeleteConflict;
import ca.uhn.fhir.jpa.util.StopWatch; import ca.uhn.fhir.jpa.util.StopWatch;
import ca.uhn.fhir.jpa.util.jsonpatch.JsonPatchUtils; import ca.uhn.fhir.jpa.util.jsonpatch.JsonPatchUtils;
import ca.uhn.fhir.jpa.util.xmlpatch.XmlPatchUtils; import ca.uhn.fhir.jpa.util.xmlpatch.XmlPatchUtils;
import ca.uhn.fhir.model.api.IQueryParameterAnd; import ca.uhn.fhir.model.api.*;
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.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.PatchTypeEnum; import ca.uhn.fhir.rest.api.PatchTypeEnum;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.method.MethodUtil; import ca.uhn.fhir.rest.method.*;
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.SearchMethodBinding.QualifierDetails; import ca.uhn.fhir.rest.method.SearchMethodBinding.QualifierDetails;
import ca.uhn.fhir.rest.server.IBundleProvider; import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.*;
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.interceptor.IServerInterceptor; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
import ca.uhn.fhir.util.FhirTerser; import ca.uhn.fhir.util.FhirTerser;
@ -110,14 +75,14 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
@Autowired @Autowired
private DaoConfig myDaoConfig; private DaoConfig myDaoConfig;
@Autowired @Autowired
protected IResourceTableDao myResourceTableDao;
@Autowired
protected PlatformTransactionManager myPlatformTransactionManager; protected PlatformTransactionManager myPlatformTransactionManager;
@Autowired @Autowired
private IResourceHistoryTableDao myResourceHistoryTableDao; private IResourceHistoryTableDao myResourceHistoryTableDao;
@Autowired() @Autowired()
protected IResourceIndexedSearchParamUriDao myResourceIndexedSearchParamUriDao; protected IResourceIndexedSearchParamUriDao myResourceIndexedSearchParamUriDao;
private String myResourceName; private String myResourceName;
@Autowired
protected IResourceTableDao myResourceTableDao;
private Class<T> myResourceType; private Class<T> myResourceType;
@Autowired(required = false) @Autowired(required = false)
protected IFulltextSearchSvc mySearchDao; protected IFulltextSearchSvc mySearchDao;
@ -227,6 +192,8 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
validateOkToDelete(deleteConflicts, entity); validateOkToDelete(deleteConflicts, entity);
preDelete(resourceToDelete);
// Notify interceptors // Notify interceptors
if (theRequestDetails != null) { if (theRequestDetails != null) {
ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getContext(), theId.getResourceType(), theId); ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getContext(), theId.getResourceType(), theId);
@ -259,8 +226,16 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
validateDeleteConflictsEmptyOrThrowException(deleteConflicts); 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()); 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 @Override
@ -320,7 +295,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
OperationOutcomeUtil.addIssue(getContext(), oo, severity, message, null, code); OperationOutcomeUtil.addIssue(getContext(), oo, severity, message, null, code);
} else { } else {
oo = OperationOutcomeUtil.newInstance(getContext()); 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 severity = "information";
String code = "informational"; String code = "informational";
OperationOutcomeUtil.addIssue(getContext(), oo, severity, message, null, code); OperationOutcomeUtil.addIssue(getContext(), oo, severity, message, null, code);
@ -553,6 +528,38 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
return retVal; 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 // @Override
// public IBundleProvider everything(IIdType theId) { // public IBundleProvider everything(IIdType theId) {
// Search search = new Search(); // 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 @Override
public <MT extends IBaseMetaType> MT metaGetOperation(Class<MT> theType, IIdType theId, RequestDetails theRequestDetails) { public <MT extends IBaseMetaType> MT metaGetOperation(Class<MT> theType, IIdType theId, RequestDetails theRequestDetails) {
// Notify interceptors // 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 * 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.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;
import javax.persistence.Query; import javax.persistence.Query;
import javax.persistence.Tuple; 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); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseHapiFhirSystemDao.class);
@Autowired
private PlatformTransactionManager myTxManager;
@Autowired @Autowired
private IForcedIdDao myForcedIdDao; private IForcedIdDao myForcedIdDao;
private ReentrantLock myReindexLock = new ReentrantLock(false);
@Autowired @Autowired
private ITermConceptDao myTermConceptDao; private ITermConceptDao myTermConceptDao;
@Autowired
private PlatformTransactionManager myTxManager;
@Transactional(propagation = Propagation.REQUIRED) @Transactional(propagation = Propagation.REQUIRED)
@Override @Override
public void deleteAllTagsOnServer(RequestDetails theRequestDetails) { public void deleteAllTagsOnServer(RequestDetails theRequestDetails) {
@ -205,8 +208,14 @@ public abstract class BaseHapiFhirSystemDao<T, MT> extends BaseHapiFhirDao<IBase
@Transactional() @Transactional()
@Override @Override
public int markAllResourcesForReindexing() { 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(); 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(); retVal += myTermConceptDao.markAllForReindexing();
ourLog.info("Done marking reindexing");
return retVal; return retVal;
} }
@ -270,12 +279,17 @@ public abstract class BaseHapiFhirSystemDao<T, MT> extends BaseHapiFhirDao<IBase
@Override @Override
@Transactional(propagation = Propagation.NOT_SUPPORTED) @Transactional(propagation = Propagation.NOT_SUPPORTED)
public int performReindexingPass(final Integer theCount) { public int performReindexingPass(final Integer theCount) {
if (!myReindexLock.tryLock()) {
return 0;
}
try { try {
return doPerformReindexingPass(theCount); return doPerformReindexingPass(theCount);
} catch (ReindexFailureException e) { } catch (ReindexFailureException e) {
ourLog.warn("Reindexing failed for resource {}", e.getResourceId()); ourLog.warn("Reindexing failed for resource {}", e.getResourceId());
markResourceAsIndexingFailed(e.getResourceId()); markResourceAsIndexingFailed(e.getResourceId());
return -1; 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); InstantDt deletedInstantOrNull = ResourceMetadataKeyEnum.DELETED_AT.get(nextResource);
Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null; Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null;
boolean shouldUpdate = !nonUpdatedEntities.contains(nextOutcome.getEntity()); 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(); myEntityManager.flush();

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.jpa.dao.data; package ca.uhn.fhir.jpa.dao.data;
import java.util.Collection;
/* /*
* #%L * #%L
* HAPI FHIR JPA Server * HAPI FHIR JPA Server
@ -33,4 +35,7 @@ public interface ITermConceptParentChildLinkDao extends JpaRepository<TermConcep
@Modifying @Modifying
void deleteByCodeSystemVersion(@Param("cs_pid") Long thePid); 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 org.springframework.beans.factory.annotation.Autowired;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem; 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.ResourceTable;
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.entity.TermConcept;
@ -187,6 +188,9 @@ public class FhirResourceDaoCodeSystemDstu3 extends FhirResourceDaoDstu3<CodeSys
return retVal; return retVal;
} }
@Autowired
private ITermCodeSystemVersionDao myCsvDao;
@Override @Override
protected ResourceTable updateEntity(IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing, protected ResourceTable updateEntity(IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, Date theUpdateTime) { boolean theUpdateVersion, Date theUpdateTime) {
@ -198,12 +202,22 @@ public class FhirResourceDaoCodeSystemDstu3 extends FhirResourceDaoDstu3<CodeSys
String codeSystemUrl = cs.getUrl(); String codeSystemUrl = cs.getUrl();
if (cs.getContent() == CodeSystemContentMode.COMPLETE || cs.getContent() == null) { 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()); ourLog.info("CodeSystem {} has a status of {}, going to store concepts in terminology tables", retVal.getIdDt().getValue(), cs.getContentElement().getValueAsString());
TermCodeSystemVersion persCs = new TermCodeSystemVersion();
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.setResource(retVal);
persCs.setResourceVersionId(retVal.getVersion()); persCs.setResourceVersionId(retVal.getVersion());
persCs.getConcepts().addAll(toPersistedConcepts(cs.getConcept(), persCs)); 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 static org.apache.commons.lang3.StringUtils.isBlank;
import java.util.List;
/* /*
* #%L * #%L
* HAPI FHIR JPA Server * 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.IFhirResourceDaoSearchParameter;
import ca.uhn.fhir.jpa.dao.IFhirSystemDao; import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
import ca.uhn.fhir.jpa.entity.ResourceTable; import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.util.DeleteConflict;
import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
@ -45,12 +48,15 @@ public class FhirResourceDaoSearchParameterDstu3 extends FhirResourceDaoDstu3<Se
private IFhirSystemDao<Bundle, Meta> mySystemDao; private IFhirSystemDao<Bundle, Meta> mySystemDao;
private void markAffectedResources(SearchParameter theResource) { private void markAffectedResources(SearchParameter theResource) {
String xpath = theResource.getXpath(); if (theResource != null) {
String resourceType = xpath.substring(0, xpath.indexOf('.')); String expression = theResource.getExpression();
ourLog.info("Marking all resources of type {} for reindexing due to updated search parameter with path: {}", xpath); 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); int updatedCount = myResourceTableDao.markResourcesOfTypeAsRequiringReindexing(resourceType);
ourLog.info("Marked {} resources for reindexing", updatedCount); 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 * This method is called once per minute to perform any required re-indexing. During most passes this will
@ -77,13 +83,22 @@ public class FhirResourceDaoSearchParameterDstu3 extends FhirResourceDaoDstu3<Se
} }
@Override
protected void preDelete(SearchParameter theResourceToDelete) {
super.preDelete(theResourceToDelete);
markAffectedResources(theResourceToDelete);
}
@Override @Override
protected void postPersist(ResourceTable theEntity, SearchParameter theResource) { protected void postPersist(ResourceTable theEntity, SearchParameter theResource) {
super.postPersist(theEntity, theResource);
markAffectedResources(theResource); markAffectedResources(theResource);
} }
@Override @Override
protected void postUpdate(ResourceTable theEntity, SearchParameter theResource) { protected void postUpdate(ResourceTable theEntity, SearchParameter theResource) {
super.postUpdate(theEntity, theResource);
markAffectedResources(theResource); markAffectedResources(theResource);
} }
@ -92,34 +107,34 @@ public class FhirResourceDaoSearchParameterDstu3 extends FhirResourceDaoDstu3<Se
super.validateResourceForStorage(theResource, theEntityToSave); super.validateResourceForStorage(theResource, theEntityToSave);
if (theResource.getStatus() == null) { 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(); String expression = theResource.getExpression();
if (isBlank(xpath)) { if (isBlank(expression)) {
throw new UnprocessableEntityException("Resource.xpath is missing"); throw new UnprocessableEntityException("SearchParameter.expression is missing");
} }
String[] xpathSplit = BaseSearchParamExtractor.SPLIT.split(xpath); String[] expressionSplit = BaseSearchParamExtractor.SPLIT.split(expression);
String allResourceName = null; String allResourceName = null;
for (String nextPath : xpathSplit) { for (String nextPath : expressionSplit) {
int dotIdx = nextPath.indexOf('.'); int dotIdx = nextPath.indexOf('.');
if (dotIdx == -1) { 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); String resourceName = nextPath.substring(0, dotIdx);
try { try {
getContext().getResourceDefinition(resourceName); getContext().getResourceDefinition(resourceName);
} catch (DataFormatException e) { } 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) { if (allResourceName == null) {
allResourceName = resourceName; allResourceName = resourceName;
} else { } else {
if (!allResourceName.equals(resourceName)) { 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()); IFhirResourceDao resourceDao = getDaoOrThrowException(res.getClass());
res.setId((String) null); res.setId((String) null);
DaoMethodOutcome outcome; 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); handleTransactionCreateOrUpdateOutcome(idSubstitutions, idToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res);
entriesToProcess.put(nextRespEntry, outcome.getEntity()); entriesToProcess.put(nextRespEntry, outcome.getEntity());
if (outcome.getCreated() == false) { if (outcome.getCreated() == false) {
@ -489,7 +490,9 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
IPrimitiveType<Date> deletedInstantOrNull = ResourceMetadataKeyEnum.DELETED_AT.get((IAnyResource) nextResource); IPrimitiveType<Date> deletedInstantOrNull = ResourceMetadataKeyEnum.DELETED_AT.get((IAnyResource) nextResource);
Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null; Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null;
boolean shouldUpdate = !nonUpdatedEntities.contains(nextOutcome.getEntity()); 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(); myEntityManager.flush();

View File

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

View File

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

View File

@ -44,6 +44,9 @@ public class TermConceptParentChildLink implements Serializable {
@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; private TermConcept myChild;
@Column(name = "CHILD_PID", insertable = false, updatable = false)
private Long myChildPid;
@ManyToOne() @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; private TermCodeSystemVersion myCodeSystem;
@ -52,6 +55,9 @@ public class TermConceptParentChildLink implements Serializable {
@JoinColumn(name = "PARENT_PID", nullable = false, referencedColumnName = "PID", foreignKey = @ForeignKey(name = "FK_TERM_CONCEPTPC_PARENT")) @JoinColumn(name = "PARENT_PID", nullable = false, referencedColumnName = "PID", foreignKey = @ForeignKey(name = "FK_TERM_CONCEPTPC_PARENT"))
private TermConcept myParent; private TermConcept myParent;
@Column(name = "PARENT_PID", insertable = false, updatable = false)
private Long myParentPid;
@Id() @Id()
@SequenceGenerator(name = "SEQ_CONCEPT_PC_PID", sequenceName = "SEQ_CONCEPT_PC_PID") @SequenceGenerator(name = "SEQ_CONCEPT_PC_PID", sequenceName = "SEQ_CONCEPT_PC_PID")
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_CONCEPT_PC_PID") @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_CONCEPT_PC_PID")
@ -62,51 +68,6 @@ public class TermConceptParentChildLink implements Serializable {
@Column(name = "REL_TYPE", length = 5, nullable = true) @Column(name = "REL_TYPE", length = 5, nullable = true)
private RelationshipTypeEnum myRelationshipType; 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 @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (this == obj) if (this == obj)
@ -136,15 +97,64 @@ public class TermConceptParentChildLink implements Serializable {
return true; return true;
} }
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 { public enum RelationshipTypeEnum {
// ******************************************** // ********************************************
// IF YOU ADD HERE MAKE SURE ORDER IS PRESERVED // IF YOU ADD HERE MAKE SURE ORDER IS PRESERVED
ISA ISA
} }
public Long getId() {
return myPid;
}
} }

View File

@ -23,9 +23,11 @@ package ca.uhn.fhir.jpa.term;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.IdentityHashMap; import java.util.IdentityHashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -33,6 +35,7 @@ import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext; import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType; import javax.persistence.PersistenceContextType;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.time.DateUtils; import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page; 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.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate; import org.springframework.transaction.support.TransactionTemplate;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Stopwatch; 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.context.FhirContext;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao; import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
@ -66,6 +72,7 @@ import ca.uhn.fhir.util.ObjectUtil;
import ca.uhn.fhir.util.ValidateUtil; import ca.uhn.fhir.util.ValidateUtil;
public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc { 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 org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseHapiTerminologySvc.class);
private static final Object PLACEHOLDER_OBJECT = new Object(); private static final Object PLACEHOLDER_OBJECT = new Object();
@ -94,8 +101,11 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
@PersistenceContext(type = PersistenceContextType.TRANSACTION) @PersistenceContext(type = PersistenceContextType.TRANSACTION)
protected EntityManager myEntityManager; protected EntityManager myEntityManager;
private boolean myProcessDeferred = true;
private long myNextReindexPass; private long myNextReindexPass;
private boolean myProcessDeferred = true;
@Autowired
private PlatformTransactionManager myTransactionMgr;
private boolean addToSet(Set<TermConcept> theSetToPopulate, TermConcept theConcept) { private boolean addToSet(Set<TermConcept> theSetToPopulate, TermConcept theConcept) {
boolean retVal = theSetToPopulate.add(theConcept); boolean retVal = theSetToPopulate.add(theConcept);
@ -108,6 +118,25 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
return retVal; 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) { private void fetchChildren(TermConcept theConcept, Set<TermConcept> theSetToPopulate) {
for (TermConceptParentChildLink nextChildLink : theConcept.getChildren()) { for (TermConceptParentChildLink nextChildLink : theConcept.getChildren()) {
TermConcept nextChild = nextChildLink.getChild(); TermConcept nextChild = nextChildLink.getChild();
@ -175,6 +204,15 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
return retVal; 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) @Transactional(propagation = Propagation.REQUIRED)
@Override @Override
public Set<TermConcept> findCodesBelow(Long theCodeSystemResourcePid, Long theCodeSystemVersionPid, String theCode) { 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); ArrayList<VersionIndependentConcept> retVal = toVersionIndependentConcepts(theSystem, codes);
return retVal; return retVal;
} }
/** /**
* Subclasses may override * Subclasses may override
* @param theSystem The code system * @param theSystem The code system
@ -216,15 +253,6 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
return Collections.emptyList(); 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) { private TermCodeSystemVersion findCurrentCodeSystemVersionForSystem(String theCodeSystem) {
TermCodeSystem cs = getCodeSystem(theCodeSystem); TermCodeSystem cs = getCodeSystem(theCodeSystem);
if (cs == null || cs.getCurrentVersion() == null) { if (cs == null || cs.getCurrentVersion() == null) {
@ -233,6 +261,7 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
TermCodeSystemVersion csv = cs.getCurrentVersion(); TermCodeSystemVersion csv = cs.getCurrentVersion();
return csv; return csv;
} }
private TermCodeSystem getCodeSystem(String theSystem) { private TermCodeSystem getCodeSystem(String theSystem) {
TermCodeSystem cs = myCodeSystemDao.findByCodeSystemUri(theSystem); TermCodeSystem cs = myCodeSystemDao.findByCodeSystemUri(theSystem);
return cs; return cs;
@ -271,15 +300,99 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
} }
private void saveConceptLink(TermConceptParentChildLink next) { private void populateVersion(TermConcept theNext, TermCodeSystemVersion theCodeSystemVersion) {
if (next.getId() == null) { if (theNext.getCodeSystem() != null) {
myConceptParentChildLinkDao.save(next); 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) { private int saveConcept(TermConcept theConcept) {
int retVal = 0; int retVal = 0;
/*
* If the concept has an ID, we're reindexing, so there's no need to
* save parent concepts first (it's way too slow to do that)
*/
if (theConcept.getId() == null) {
retVal += ensureParentsSaved(theConcept.getParents()); retVal += ensureParentsSaved(theConcept.getParents());
}
if (theConcept.getId() == null || theConcept.getIndexStatus() == null) { if (theConcept.getId() == null || theConcept.getIndexStatus() == null) {
retVal++; retVal++;
theConcept.setIndexStatus(BaseHapiFhirDao.INDEX_STATUS_INDEXED); theConcept.setIndexStatus(BaseHapiFhirDao.INDEX_STATUS_INDEXED);
@ -290,32 +403,9 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
return retVal; return retVal;
} }
private int ensureParentsSaved(Collection<TermConceptParentChildLink> theParents) { private void saveConceptLink(TermConceptParentChildLink next) {
ourLog.trace("Checking {} parents", theParents.size()); if (next.getId() == null) {
int retVal = 0; myConceptParentChildLinkDao.save(next);
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);
} }
} }
@ -369,42 +459,6 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
} }
} }
@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 @Override
public void setProcessDeferred(boolean theProcessDeferred) { public void setProcessDeferred(boolean theProcessDeferred) {
myProcessDeferred = theProcessDeferred; myProcessDeferred = theProcessDeferred;
@ -470,7 +524,7 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
totalCodeCount += validateConceptForStorage(next, theCodeSystemVersion, conceptsStack, allConcepts); totalCodeCount += validateConceptForStorage(next, theCodeSystemVersion, conceptsStack, allConcepts);
} }
ourLog.info("Saving version"); ourLog.info("Saving version containing {} concepts", totalCodeCount);
TermCodeSystemVersion codeSystemVersion = myCodeSystemVersionDao.saveAndFlush(theCodeSystemVersion); TermCodeSystemVersion codeSystemVersion = myCodeSystemVersionDao.saveAndFlush(theCodeSystemVersion);
@ -547,4 +601,12 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
return retVal; 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; package ca.uhn.fhir.jpa.term;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank;
/* /*
@ -64,6 +65,7 @@ import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.entity.TermCodeSystem; import ca.uhn.fhir.jpa.entity.TermCodeSystem;
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.entity.TermConcept; 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.method.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
@ -159,7 +161,6 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvc implements I
} }
private void addDisplayFilterInexact(QueryBuilder qb, BooleanJunction<?> bool, ConceptSetFilterComponent nextFilter) { private void addDisplayFilterInexact(QueryBuilder qb, BooleanJunction<?> bool, ConceptSetFilterComponent nextFilter) {
//@formatter:off
Query textQuery = qb Query textQuery = qb
.phrase() .phrase()
.withSlop(2) .withSlop(2)
@ -169,7 +170,6 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvc implements I
// .andField("myDisplayPhonetic").boostedTo(0.5f) // .andField("myDisplayPhonetic").boostedTo(0.5f)
.sentence(nextFilter.getValue().toLowerCase()).createQuery(); .sentence(nextFilter.getValue().toLowerCase()).createQuery();
bool.must(textQuery); bool.must(textQuery);
//@formatter:on
} }
@Override @Override
@ -216,10 +216,18 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvc implements I
bool.must(qb.keyword().onField("myCodeSystemVersionPid").matching(csv.getPid()).createQuery()); bool.must(qb.keyword().onField("myCodeSystemVersionPid").matching(csv.getPid()).createQuery());
for (ConceptSetFilterComponent nextFilter : theInclude.getFilter()) { for (ConceptSetFilterComponent nextFilter : theInclude.getFilter()) {
if (isNotBlank(nextFilter.getValue())) { if (isBlank(nextFilter.getValue()) && nextFilter.getOp() == null && isBlank(nextFilter.getProperty())) {
continue;
}
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) { if (nextFilter.getProperty().equals("display:exact") && nextFilter.getOp() == FilterOperator.EQUAL) {
addDisplayFilterExact(qb, bool, nextFilter); addDisplayFilterExact(qb, bool, nextFilter);
} else if (nextFilter.getProperty().equals("display") && nextFilter.getOp() == FilterOperator.EQUAL) { } else if ("display".equals(nextFilter.getProperty()) && nextFilter.getOp() == FilterOperator.EQUAL) {
if (nextFilter.getValue().trim().contains(" ")) { if (nextFilter.getValue().trim().contains(" ")) {
addDisplayFilterExact(qb, bool, nextFilter); addDisplayFilterExact(qb, bool, nextFilter);
} else { } else {
@ -237,14 +245,18 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvc implements I
throw new InvalidRequestException("Unknown filter property[" + nextFilter + "] + op[" + nextFilter.getOpElement().getValueAsString() + "]"); throw new InvalidRequestException("Unknown filter property[" + nextFilter + "] + op[" + nextFilter.getOpElement().getValueAsString() + "]");
} }
} }
}
Query luceneQuery = bool.createQuery(); Query luceneQuery = bool.createQuery();
FullTextQuery jpaQuery = em.createFullTextQuery(luceneQuery, TermConcept.class); FullTextQuery jpaQuery = em.createFullTextQuery(luceneQuery, TermConcept.class);
jpaQuery.setMaxResults(1000); jpaQuery.setMaxResults(1000);
StopWatch sw = new StopWatch();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
List<TermConcept> result = jpaQuery.getResultList(); List<TermConcept> result = jpaQuery.getResultList();
ourLog.info("Expansion completed in {}ms", sw.getMillis());
for (TermConcept nextConcept : result) { for (TermConcept nextConcept : result) {
addCodeIfNotAlreadyAdded(system, retVal, addedCodes, nextConcept); addCodeIfNotAlreadyAdded(system, retVal, addedCodes, nextConcept);
} }
@ -259,7 +271,6 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvc implements I
} }
} }
return retVal; return retVal;
} }

View File

@ -19,10 +19,12 @@ import static org.mockito.Mockito.verify;
import java.io.InputStream; import java.io.InputStream;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.Test; 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); 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 @Test
public void testReindexing() { public void testReindexing() {
Patient p = new Patient(); 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; 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.Observation;
import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.dstu3.model.Reference; import org.hl7.fhir.dstu3.model.Reference;
@ -11,9 +8,6 @@ import org.junit.AfterClass;
import org.junit.Test; import org.junit.Test;
import ca.uhn.fhir.jpa.dao.SearchParameterMap; 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; import ca.uhn.fhir.util.TestUtil;
public class FhirResourceDaoDstu3ContainedTest extends BaseJpaDstu3Test { public class FhirResourceDaoDstu3ContainedTest extends BaseJpaDstu3Test {

View File

@ -1,6 +1,7 @@
package ca.uhn.fhir.jpa.dao.dstu3; package ca.uhn.fhir.jpa.dao.dstu3;
import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.empty;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat; 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.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.IBundleProvider; 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.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.TestUtil;
@ -30,14 +32,14 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
fooSp.setCode("foo"); fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN); fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP"); fooSp.setTitle("FOO SP");
fooSp.setXpath("PatientFoo.gender"); fooSp.setExpression("PatientFoo.gender");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL); fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE); fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
try { try {
mySearchParameterDao.create(fooSp, mySrd); mySearchParameterDao.create(fooSp, mySrd);
fail(); fail();
} catch (UnprocessableEntityException e) { } 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.setCode("foo");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN); fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP"); 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.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE); fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
try { try {
mySearchParameterDao.create(fooSp, mySrd); mySearchParameterDao.create(fooSp, mySrd);
fail(); fail();
} catch (UnprocessableEntityException e) { } 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); mySearchParameterDao.create(fooSp, mySrd);
fail(); fail();
} catch (UnprocessableEntityException e) { } 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.setCode("foo");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN); fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP"); fooSp.setTitle("FOO SP");
fooSp.setXpath("gender"); fooSp.setExpression("gender");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL); fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE); fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
try { try {
mySearchParameterDao.create(fooSp, mySrd); mySearchParameterDao.create(fooSp, mySrd);
fail(); fail();
} catch (UnprocessableEntityException e) { } 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.setCode("foo");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN); fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP"); fooSp.setTitle("FOO SP");
fooSp.setXpath("Patient.gender"); fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL); fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(null); fooSp.setStatus(null);
try { try {
mySearchParameterDao.create(fooSp, mySrd); mySearchParameterDao.create(fooSp, mySrd);
fail(); fail();
} catch (UnprocessableEntityException e) { } 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.setCode("foo");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN); fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP"); fooSp.setTitle("FOO SP");
fooSp.setXpath("Patient.gender"); fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL); fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE); fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
mySearchParameterDao.create(fooSp, mySrd); IIdType spId = mySearchParameterDao.create(fooSp, mySrd).getId().toUnqualifiedVersionless();
mySearchParamRegsitry.forceRefresh(); mySearchParamRegsitry.forceRefresh();
@ -150,6 +152,17 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
foundResources = toUnqualifiedVersionlessIdValues(results); foundResources = toUnqualifiedVersionlessIdValues(results);
assertThat(foundResources, contains(patId.getValue())); 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 @Test
@ -159,7 +172,7 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
fooSp.setCode("foo"); fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN); fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP"); fooSp.setTitle("FOO SP");
fooSp.setXpath("Patient.gender"); fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL); fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.DRAFT); fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.DRAFT);
mySearchParameterDao.create(fooSp, mySrd); mySearchParameterDao.create(fooSp, mySrd);

View File

@ -26,7 +26,9 @@ import org.junit.AfterClass;
import org.junit.Before; import org.junit.Before;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; 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.DaoConfig;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem.LookupCodeResult; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem.LookupCodeResult;
import ca.uhn.fhir.jpa.dao.SearchParameterMap; 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.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum; 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.parser.IParser;
import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.param.TokenParamModifier; import ca.uhn.fhir.rest.param.TokenParamModifier;
@ -52,6 +56,8 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
@After @After
public void after() { public void after() {
myDaoConfig.setDeferIndexingForCodesystemsOfSize(new DaoConfig().getDeferIndexingForCodesystemsOfSize()); myDaoConfig.setDeferIndexingForCodesystemsOfSize(new DaoConfig().getDeferIndexingForCodesystemsOfSize());
BaseHapiTerminologySvc.setForceSaveDeferredAlwaysForUnitTest(false);
} }
@Before @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 @Test
public void testExpandWithSystemAndCodesInExternalValueSet() { public void testExpandWithSystemAndCodesInExternalValueSet() {
createExternalCsAndLocalVs(); 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 @Test
public void testTransactionCreateInlineMatchUrlWithOneMatch() { public void testTransactionCreateInlineMatchUrlWithOneMatch() {
String methodName = "testTransactionCreateInlineMatchUrlWithOneMatch"; String methodName = "testTransactionCreateInlineMatchUrlWithOneMatch";

View File

@ -10,6 +10,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import java.io.InputStream; import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
@ -139,7 +140,7 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
get.addHeader("Accept", "application/xml, text/html"); get.addHeader("Accept", "application/xml, text/html");
CloseableHttpResponse http = ourHttpClient.execute(get); CloseableHttpResponse http = ourHttpClient.execute(get);
try { try {
String response = IOUtils.toString(http.getEntity().getContent()); String response = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(response); ourLog.info(response);
assertThat(response, (containsString("_format=json"))); assertThat(response, (containsString("_format=json")));
assertEquals(200, http.getStatusLine().getStatusCode()); assertEquals(200, http.getStatusLine().getStatusCode());
@ -167,7 +168,7 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
get.addHeader("Accept", "application/xml+fhir"); get.addHeader("Accept", "application/xml+fhir");
CloseableHttpResponse http = ourHttpClient.execute(get); CloseableHttpResponse http = ourHttpClient.execute(get);
try { try {
String response = IOUtils.toString(http.getEntity().getContent()); String response = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(response); ourLog.info(response);
assertThat(response, not(containsString("_format"))); assertThat(response, not(containsString("_format")));
assertEquals(200, http.getStatusLine().getStatusCode()); assertEquals(200, http.getStatusLine().getStatusCode());
@ -216,7 +217,7 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
CloseableHttpResponse http = ourHttpClient.execute(get); CloseableHttpResponse http = ourHttpClient.execute(get);
try { try {
assertEquals(200, http.getStatusLine().getStatusCode()); 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); ourLog.info(output);
Parameters parameters = ourCtx.newXmlParser().parseResource(Parameters.class, output); Parameters parameters = ourCtx.newXmlParser().parseResource(Parameters.class, output);
@ -246,7 +247,7 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
CloseableHttpResponse http = ourHttpClient.execute(get); CloseableHttpResponse http = ourHttpClient.execute(get);
try { try {
assertEquals(400, http.getStatusLine().getStatusCode()); 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); ourLog.info(output);
assertThat(output, containsString("Parameter 'context' must be provided")); assertThat(output, containsString("Parameter 'context' must be provided"));
} finally { } finally {
@ -257,7 +258,7 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
http = ourHttpClient.execute(get); http = ourHttpClient.execute(get);
try { try {
assertEquals(400, http.getStatusLine().getStatusCode()); 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); ourLog.info(output);
assertThat(output, containsString("Parameter 'searchParam' must be provided")); assertThat(output, containsString("Parameter 'searchParam' must be provided"));
} finally { } finally {
@ -268,7 +269,7 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
http = ourHttpClient.execute(get); http = ourHttpClient.execute(get);
try { try {
assertEquals(400, http.getStatusLine().getStatusCode()); 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); ourLog.info(output);
assertThat(output, containsString("Parameter 'text' must be provided")); assertThat(output, containsString("Parameter 'text' must be provided"));
} finally { } finally {
@ -286,7 +287,7 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
@Test @Test
public void testTransactionFromBundle() throws Exception { public void testTransactionFromBundle() throws Exception {
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/transaction_link_patient_eve.xml"); 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(); String response = ourClient.transaction().withBundle(bundle).prettyPrint().execute();
ourLog.info(response); ourLog.info(response);
} }
@ -295,7 +296,7 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
public void testTransactionFromBundle2() throws Exception { public void testTransactionFromBundle2() throws Exception {
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/transaction_link_patient_eve_temp.xml"); 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(); String response = ourClient.transaction().withBundle(bundle).prettyPrint().execute();
ourLog.info(response); ourLog.info(response);
@ -311,7 +312,7 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
*/ */
bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/transaction_link_patient_eve_temp.xml"); 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(); response = ourClient.transaction().withBundle(bundle).prettyPrint().execute();
ourLog.info(response); ourLog.info(response);
@ -335,7 +336,7 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
public void testTransactionFromBundle3() throws Exception { public void testTransactionFromBundle3() throws Exception {
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/grahame-transaction.xml"); 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(); String response = ourClient.transaction().withBundle(bundle).prettyPrint().execute();
ourLog.info(response); ourLog.info(response);
} }
@ -343,7 +344,7 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
@Test @Test
public void testTransactionFromBundle4() throws Exception { public void testTransactionFromBundle4() throws Exception {
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/simone_bundle.xml"); 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(); String response = ourClient.transaction().withBundle(bundle).prettyPrint().execute();
ourLog.info(response); ourLog.info(response);
Bundle bundleResp = ourCtx.newXmlParser().parseResource(Bundle.class, response); Bundle bundleResp = ourCtx.newXmlParser().parseResource(Bundle.class, response);
@ -358,7 +359,7 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
@Test @Test
public void testTransactionFromBundle5() throws Exception { public void testTransactionFromBundle5() throws Exception {
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/simone_bundle2.xml"); InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/simone_bundle2.xml");
String bundle = IOUtils.toString(bundleRes); String bundle = IOUtils.toString(bundleRes, StandardCharsets.UTF_8);
try { try {
ourClient.transaction().withBundle(bundle).prettyPrint().execute(); ourClient.transaction().withBundle(bundle).prettyPrint().execute();
fail(); fail();
@ -372,7 +373,7 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
@Test @Test
public void testTransactionFromBundle6() throws Exception { public void testTransactionFromBundle6() throws Exception {
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/simone_bundle3.xml"); 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(); ourClient.transaction().withBundle(bundle).prettyPrint().execute();
// try { // try {
// fail(); // fail();
@ -427,5 +428,18 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
assertEquals(0, respSub.getEntry().size()); 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 { public void saveCreateSearchParamInvalidWithMissingStatus() throws IOException {
SearchParameter sp = new SearchParameter(); SearchParameter sp = new SearchParameter();
sp.setCode("foo"); sp.setCode("foo");
sp.setXpath("Patient.gender"); sp.setExpression("Patient.gender");
sp.setXpathUsage(XPathUsageType.NORMAL); sp.setXpathUsage(XPathUsageType.NORMAL);
sp.setTitle("Foo Param"); sp.setTitle("Foo Param");
@ -66,7 +66,7 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv
ourClient.create().resource(sp).execute(); ourClient.create().resource(sp).execute();
fail(); fail();
} catch (UnprocessableEntityException e) { } 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.setCode("foo");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN); fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP"); fooSp.setTitle("FOO SP");
fooSp.setXpath("Patient.gender"); fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL); fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE); fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
mySearchParameterDao.create(fooSp, mySrd); mySearchParameterDao.create(fooSp, mySrd);
@ -110,7 +110,7 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv
fooSp.setCode("gender"); fooSp.setCode("gender");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN); fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("Gender"); fooSp.setTitle("Gender");
fooSp.setXpath("Patient.gender"); fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL); fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.RETIRED); fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.RETIRED);
mySearchParameterDao.create(fooSp, mySrd); mySearchParameterDao.create(fooSp, mySrd);
@ -152,7 +152,7 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv
fooSp.setCode("foo"); fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN); fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP"); fooSp.setTitle("FOO SP");
fooSp.setXpath("Patient.gender"); fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL); fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE); fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
mySearchParameterDao.create(fooSp, mySrd); mySearchParameterDao.create(fooSp, mySrd);
@ -162,7 +162,7 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv
fooSp.setCode("gender"); fooSp.setCode("gender");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN); fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("Gender"); fooSp.setTitle("Gender");
fooSp.setXpath("Patient.gender"); fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL); fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.RETIRED); fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.RETIRED);
mySearchParameterDao.create(fooSp, mySrd); mySearchParameterDao.create(fooSp, mySrd);
@ -206,7 +206,7 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv
fooSp.setCode("foo"); fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN); fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP"); fooSp.setTitle("FOO SP");
fooSp.setXpath("Patient.gender"); fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL); fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE); fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
mySearchParameterDao.create(fooSp, mySrd); mySearchParameterDao.create(fooSp, mySrd);
@ -232,6 +232,7 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv
.where(new TokenClientParam("foo").exactly().code("male")) .where(new TokenClientParam("foo").exactly().code("male"))
.returnBundle(Bundle.class) .returnBundle(Bundle.class)
.execute(); .execute();
foundResources = toUnqualifiedVersionlessIdValues(result); foundResources = toUnqualifiedVersionlessIdValues(result);
assertThat(foundResources, contains(patId.getValue())); assertThat(foundResources, contains(patId.getValue()));
@ -256,7 +257,7 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv
fooSp.setCode("foo"); fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN); fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP"); fooSp.setTitle("FOO SP");
fooSp.setXpath("Patient.gender"); fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL); fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE); fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
mySearchParameterDao.create(fooSp, mySrd); mySearchParameterDao.create(fooSp, mySrd);
@ -276,7 +277,7 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv
fooSp.setCode("foo"); fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.REFERENCE); fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.REFERENCE);
fooSp.setTitle("FOO SP"); fooSp.setTitle("FOO SP");
fooSp.setXpath("Observation.subject"); fooSp.setExpression("Observation.subject");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL); fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE); fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
mySearchParameterDao.create(fooSp, mySrd); mySearchParameterDao.create(fooSp, mySrd);

View File

@ -161,7 +161,6 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
myDaoConfig.setAllowMultipleDelete(true); myDaoConfig.setAllowMultipleDelete(true);
} }
private void checkParamMissing(String paramName) throws IOException, ClientProtocolException { private void checkParamMissing(String paramName) throws IOException, ClientProtocolException {
HttpGet get = new HttpGet(ourServerBase + "/Observation?" + paramName + ":missing=false"); HttpGet get = new HttpGet(ourServerBase + "/Observation?" + paramName + ":missing=false");
CloseableHttpResponse resp = ourHttpClient.execute(get); CloseableHttpResponse resp = ourHttpClient.execute(get);
@ -674,7 +673,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
String encoded = myFhirCtx.newXmlParser().encodeResourceToString(response); String encoded = myFhirCtx.newXmlParser().encodeResourceToString(response);
ourLog.info(encoded); ourLog.info(encoded);
assertThat(encoded, containsString( 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 { try {
ourClient.read().resource("Patient").withId(id1).execute(); ourClient.read().resource("Patient").withId(id1).execute();
fail(); fail();
@ -726,7 +725,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
* Test for #345 * Test for #345
*/ */
@Test @Test
public void testDeleteNormal() throws IOException { public void testDeleteNormal() {
Patient p = new Patient(); Patient p = new Patient();
p.addName().setFamily("FAM"); p.addName().setFamily("FAM");
IIdType id = ourClient.create().resource(p).execute().getId().toUnqualifiedVersionless(); 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 @Test
public void testDeleteResourceConditional1() throws IOException { public void testDeleteResourceConditional1() throws IOException {
String methodName = "testDeleteResourceConditional1"; String methodName = "testDeleteResourceConditional1";
@ -768,6 +778,10 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
response = ourHttpClient.execute(delete); response = ourHttpClient.execute(delete);
try { try {
assertEquals(200, response.getStatusLine().getStatusCode()); 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 { } finally {
response.close(); response.close();
} }
@ -777,6 +791,24 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
try { try {
ourLog.info(response.toString()); ourLog.info(response.toString());
assertEquals(Constants.STATUS_HTTP_410_GONE, response.getStatusLine().getStatusCode()); 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 { } finally {
response.close(); response.close();
} }
@ -1781,7 +1813,10 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
ourLog.info("Response: {}", respString); ourLog.info("Response: {}", respString);
assertEquals(400, response.getStatusLine().getStatusCode()); assertEquals(400, response.getStatusLine().getStatusCode());
OperationOutcome oo = myFhirCtx.newXmlParser().parseResource(OperationOutcome.class, respString); 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 { } finally {
response.close(); response.close();
} }
@ -1814,7 +1849,6 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
p.getManagingOrganization().setReference("http://example.com/Organization/123"); p.getManagingOrganization().setReference("http://example.com/Organization/123");
ourClient.create().resource(p).execute(); ourClient.create().resource(p).execute();
Bundle b = ourClient.search().forResource("Patient").include(Patient.INCLUDE_ORGANIZATION).returnBundle(Bundle.class).execute(); Bundle b = ourClient.search().forResource("Patient").include(Patient.INCLUDE_ORGANIZATION).returnBundle(Bundle.class).execute();
assertEquals(1, b.getEntry().size()); assertEquals(1, b.getEntry().size());
} }
@ -1834,7 +1868,6 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
} }
} }
@Test @Test
public void testMetadataSuperParamsAreIncluded() throws IOException { public void testMetadataSuperParamsAreIncluded() throws IOException {
StructureDefinition p = new StructureDefinition(); StructureDefinition p = new StructureDefinition();
@ -2102,7 +2135,8 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
String respString = ourClient.transaction().withBundle(input).prettyPrint().execute(); String respString = ourClient.transaction().withBundle(input).prettyPrint().execute();
ourLog.info(respString); 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")); 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 = // Bundle bundle =
} }
@ -3445,7 +3479,9 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
ourLog.info(responseString); ourLog.info(responseString);
assertEquals(400, response.getStatusLine().getStatusCode()); assertEquals(400, response.getStatusLine().getStatusCode());
OperationOutcome oo = myFhirCtx.newXmlParser().parseResource(OperationOutcome.class, responseString); 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 { } finally {
response.close(); response.close();
} }

View File

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

View File

@ -120,6 +120,26 @@ public class GenericClientDstu3Test {
return capt; 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 @Test
public void testPatchJsonByIdString() throws Exception { public void testPatchJsonByIdString() throws Exception {
OperationOutcome conf = new OperationOutcome(); OperationOutcome conf = new OperationOutcome();
@ -157,6 +177,7 @@ public class GenericClientDstu3Test {
OperationOutcome oo = (OperationOutcome) outcome.getOperationOutcome(); OperationOutcome oo = (OperationOutcome) outcome.getOperationOutcome();
assertThat(oo.getText().getDivAsString(), containsString("OK!")); assertThat(oo.getText().getDivAsString(), containsString("OK!"));
} }
@Test @Test
public void testPatchJsonByIdType() throws Exception { public void testPatchJsonByIdType() throws Exception {
OperationOutcome conf = new OperationOutcome(); OperationOutcome conf = new OperationOutcome();
@ -194,6 +215,7 @@ public class GenericClientDstu3Test {
OperationOutcome oo = (OperationOutcome) outcome.getOperationOutcome(); OperationOutcome oo = (OperationOutcome) outcome.getOperationOutcome();
assertThat(oo.getText().getDivAsString(), containsString("OK!")); assertThat(oo.getText().getDivAsString(), containsString("OK!"));
} }
@Test @Test
public void testPatchJsonByConditionalString() throws Exception { public void testPatchJsonByConditionalString() throws Exception {
OperationOutcome conf = new OperationOutcome(); OperationOutcome conf = new OperationOutcome();
@ -231,6 +253,7 @@ public class GenericClientDstu3Test {
OperationOutcome oo = (OperationOutcome) outcome.getOperationOutcome(); OperationOutcome oo = (OperationOutcome) outcome.getOperationOutcome();
assertThat(oo.getText().getDivAsString(), containsString("OK!")); assertThat(oo.getText().getDivAsString(), containsString("OK!"));
} }
@Test @Test
public void testPatchJsonByConditionalParam() throws Exception { public void testPatchJsonByConditionalParam() throws Exception {
OperationOutcome conf = new OperationOutcome(); OperationOutcome conf = new OperationOutcome();
@ -269,6 +292,7 @@ public class GenericClientDstu3Test {
OperationOutcome oo = (OperationOutcome) outcome.getOperationOutcome(); OperationOutcome oo = (OperationOutcome) outcome.getOperationOutcome();
assertThat(oo.getText().getDivAsString(), containsString("OK!")); assertThat(oo.getText().getDivAsString(), containsString("OK!"));
} }
@Test @Test
public void testPatchJsonByConditionalParamResourceType() throws Exception { public void testPatchJsonByConditionalParamResourceType() throws Exception {
OperationOutcome conf = new OperationOutcome(); OperationOutcome conf = new OperationOutcome();
@ -307,6 +331,7 @@ public class GenericClientDstu3Test {
OperationOutcome oo = (OperationOutcome) outcome.getOperationOutcome(); OperationOutcome oo = (OperationOutcome) outcome.getOperationOutcome();
assertThat(oo.getText().getDivAsString(), containsString("OK!")); assertThat(oo.getText().getDivAsString(), containsString("OK!"));
} }
@Test @Test
public void testPatchXmlByIdString() throws Exception { public void testPatchXmlByIdString() throws Exception {
OperationOutcome conf = new OperationOutcome(); OperationOutcome conf = new OperationOutcome();
@ -481,8 +506,6 @@ public class GenericClientDstu3Test {
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Test @Test
public void testClientFailures() throws Exception { public void testClientFailures() throws Exception {
@ -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()); assertEquals("Unable to determing encoding of request (body does not appear to be valid XML or JSON)", e.getMessage());
} }
} }
@Test @Test
@ -2051,7 +2073,6 @@ public class GenericClientDstu3Test {
patient.getText().setDivAsString("OK!"); patient.getText().setDivAsString("OK!");
b.addEntry().setResource(patient); b.addEntry().setResource(patient);
final String respString = p.encodeResourceToString(b); final String respString = p.encodeResourceToString(b);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class); ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse); when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
@ -2082,8 +2103,6 @@ public class GenericClientDstu3Test {
assertEquals(expectedUserAgent(), capt.getAllValues().get(0).getHeaders("User-Agent")[0].getValue()); assertEquals(expectedUserAgent(), capt.getAllValues().get(0).getHeaders("User-Agent")[0].getValue());
} }
@AfterClass @AfterClass
public static void afterClassClearContext() { public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest(); TestUtil.clearAllStaticFieldsForUnitTest();

View File

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

View File

@ -123,6 +123,21 @@
Clean up dependencies and remove Eclipse project files from git. Thanks to Clean up dependencies and remove Eclipse project files from git. Thanks to
@sekaijin for the pull request! @sekaijin for the pull request!
</action> </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>
<release version="2.2" date="2016-12-20"> <release version="2.2" date="2016-12-20">
<action type="add"> <action type="add">