Merge remote-tracking branch 'upstream/master'

This commit is contained in:
patrick-werner 2018-02-25 17:31:56 +01:00
commit 66ed2bca38
20 changed files with 376 additions and 212 deletions

View File

@ -181,7 +181,7 @@ public enum FhirVersionEnum {
private String myVersion;
public Dstu3Version() {
Dstu3Version() {
try {
Class<?> c = Class.forName("org.hl7.fhir.dstu3.model.Constants");
myVersion = (String) c.getDeclaredField("VERSION").get(null);
@ -201,7 +201,7 @@ public enum FhirVersionEnum {
private String myVersion;
public R4Version() {
R4Version() {
try {
Class<?> c = Class.forName("org.hl7.fhir.r4.model.Constants");
myVersion = (String) c.getDeclaredField("VERSION").get(null);

View File

@ -55,7 +55,7 @@ public interface IParserErrorHandler {
void incorrectJsonType(IParseLocation theLocation, String theElementName, ValueType theExpectedValueType, ScalarType theExpectedScalarType, ValueType theFoundValueType, ScalarType theFoundScalarType);
/**
* The parser detected an atttribute value that was invalid (such as: empty "" values are not permitted)
* The parser detected an attribute value that was invalid (such as: empty "" values are not permitted)
*
* @param theLocation
* The location in the document. Note that this may be <code>null</code> as the ParseLocation feature is experimental. Use with caution, as the API may change.
@ -70,7 +70,7 @@ public interface IParserErrorHandler {
*
* @param theLocation
* The location in the document. Note that this may be <code>null</code> as the ParseLocation feature is experimental. Use with caution, as the API may change.
* @param theReference The actual invalid reference (e.g. "#3")
* @param theElementName The missing element name
* @since 2.1
*/
void missingRequiredElement(IParseLocation theLocation, String theElementName);
@ -123,7 +123,7 @@ public interface IParserErrorHandler {
* type which will currently always be set to null. This interface is included here so that
* locations can be added to the API in a future release without changing the API.
*/
public interface IParseLocation {
interface IParseLocation {
/**
* Returns the name of the parent element (the element containing the element currently being parsed)

View File

@ -1033,7 +1033,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
} else {
parentElementName = "extension";
}
getErrorHandler().missingRequiredElement(new ParseLocation(parentElementName), "url");
getErrorHandler().missingRequiredElement(new ParseLocation().setParentElementName(parentElementName), "url");
url = null;
} else {
url = getExtensionUrl(jsonElement.getAsString());

View File

@ -29,9 +29,8 @@ class ParseLocation implements IParseLocation {
/**
* Constructor
*/
public ParseLocation(String theParentElementName) {
public ParseLocation() {
super();
myParentElementName = theParentElementName;
}
@Override
@ -39,4 +38,9 @@ class ParseLocation implements IParseLocation {
return myParentElementName;
}
public ParseLocation setParentElementName(String theParentElementName) {
myParentElementName = theParentElementName;
return this;
}
}

View File

@ -806,7 +806,7 @@ class ParserState<T> {
@SuppressWarnings("unchecked")
List<IBase> securityLabels = (List<IBase>) myMap.get(ResourceMetadataKeyEnum.SECURITY_LABELS);
if (securityLabels == null) {
securityLabels = new ArrayList<IBase>();
securityLabels = new ArrayList<>();
myMap.put(ResourceMetadataKeyEnum.SECURITY_LABELS, securityLabels);
}
IBase securityLabel = myContext.getVersion().newCodingDt();

View File

@ -139,7 +139,7 @@ public class XmlParser extends BaseParser /* implements IParser */ {
Attribute urlAttr = elem.getAttributeByName(new QName("url"));
String url;
if (urlAttr == null || isBlank(urlAttr.getValue())) {
getErrorHandler().missingRequiredElement(new ParseLocation("extension"), "url");
getErrorHandler().missingRequiredElement(new ParseLocation().setParentElementName("extension"), "url");
url = null;
} else {
url = urlAttr.getValue();
@ -149,7 +149,7 @@ public class XmlParser extends BaseParser /* implements IParser */ {
Attribute urlAttr = elem.getAttributeByName(new QName("url"));
String url;
if (urlAttr == null || isBlank(urlAttr.getValue())) {
getErrorHandler().missingRequiredElement(new ParseLocation("modifierExtension"), "url");
getErrorHandler().missingRequiredElement(new ParseLocation().setParentElementName("modifierExtension"), "url");
url = null;
} else {
url = urlAttr.getValue();

View File

@ -7,6 +7,7 @@ import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
import ca.uhn.fhir.model.dstu2.resource.StructureDefinition;
import ca.uhn.fhir.model.dstu2.resource.ValueSet;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
@ -274,6 +275,8 @@ public class ValidationDataUploader extends BaseCommand {
ourLog.info(" - Got ID: {}", id.getValue());
} catch (UnprocessableEntityException e) {
ourLog.warn("UnprocessableEntityException: " + e.toString());
} catch (BaseServerResponseException e) {
ourLog.warn("Server responded HTTP " + e.getStatusCode() + ": " + e.toString());
}
count++;
}
@ -519,7 +522,11 @@ public class ValidationDataUploader extends BaseCommand {
}
ourLog.info("Uploading {} StructureDefinition {}/{} : {}", new Object[] {name, count, total, next.getIdElement().getValue()});
try {
client.update().resource(next).execute();
} catch (BaseServerResponseException e) {
ourLog.warn("Server responded HTTP " + e.getStatusCode() + ": " + e.toString());
}
count++;
}

View File

@ -82,10 +82,11 @@ import java.util.Map.Entry;
import static org.apache.commons.lang3.StringUtils.*;
@SuppressWarnings("WeakerAccess")
public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
public static final long INDEX_STATUS_INDEXED = Long.valueOf(1L);
public static final long INDEX_STATUS_INDEXING_FAILED = Long.valueOf(2L);
public static final long INDEX_STATUS_INDEXED = 1L;
public static final long INDEX_STATUS_INDEXING_FAILED = 2L;
public static final String NS_JPA_PROFILE = "https://github.com/jamesagnew/hapi-fhir/ns/jpa/profile";
public static final String OO_SEVERITY_ERROR = "error";
public static final String OO_SEVERITY_INFO = "information";
@ -379,7 +380,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
} else {
ResourceLink resourceLink = new ResourceLink(nextPathAndRef.getPath(), theEntity, nextId, theUpdateTime);
if (theLinks.add(resourceLink)) {
ourLog.info("Indexing remote resource reference URL: {}", nextId);
ourLog.debug("Indexing remote resource reference URL: {}", nextId);
}
continue;
}
@ -417,7 +418,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
IBaseResource newResource = missingResourceDef.newInstance();
newResource.setId(resName + "/" + id);
IFhirResourceDao<IBaseResource> placeholderResourceDao = (IFhirResourceDao<IBaseResource>) getDao(newResource.getClass());
ourLog.info("Automatically creating empty placeholder resource: {}", newResource.getIdElement().getValue());
ourLog.debug("Automatically creating empty placeholder resource: {}", newResource.getIdElement().getValue());
valueOf = placeholderResourceDao.update(newResource).getEntity().getId();
} else {
throw new InvalidRequestException("Resource " + resName + "/" + id + " not found, specified in path: " + nextPathsUnsplit);
@ -673,7 +674,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
@SuppressWarnings("unchecked")
public <R extends IBaseResource> IFhirResourceDao<R> getDao(Class<R> theType) {
if (myResourceTypeToDao == null) {
Map<Class<? extends IBaseResource>, IFhirResourceDao<?>> theResourceTypeToDao = new HashMap<Class<? extends IBaseResource>, IFhirResourceDao<?>>();
Map<Class<? extends IBaseResource>, IFhirResourceDao<?>> theResourceTypeToDao = new HashMap<>();
for (IFhirResourceDao<?> next : myResourceDaos) {
theResourceTypeToDao.put(next.getResourceType(), next);
}
@ -1549,7 +1550,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
}
Long next = matches.iterator().next();
String newId = translatePidIdToForcedId(resourceTypeString, next);
ourLog.info("Replacing inline match URL[{}] with ID[{}}", nextId.getValue(), newId);
ourLog.debug("Replacing inline match URL[{}] with ID[{}}", nextId.getValue(), newId);
nextRef.setReference(newId);
}
}
@ -1615,7 +1616,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
}
if (!changed.isChanged() && !theForceUpdate && myConfig.isSuppressUpdatesWithNoChange()) {
ourLog.info("Resource {} has not changed", theEntity.getIdDt().toUnqualified().getValue());
ourLog.debug("Resource {} has not changed", theEntity.getIdDt().toUnqualified().getValue());
if (theResource != null) {
populateResourceIdFromEntity(theEntity, theResource);
}
@ -1657,7 +1658,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
historyEntry.setEncoding(changed.getEncoding());
historyEntry.setResource(changed.getResource());
ourLog.info("Saving history entry {}", historyEntry.getIdDt());
ourLog.debug("Saving history entry {}", historyEntry.getIdDt());
myResourceHistoryTableDao.save(historyEntry);
}
@ -1765,7 +1766,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
// Store composite string uniques
if (getConfig().isUniqueIndexesEnabled()) {
for (ResourceIndexedCompositeStringUnique next : removeCommon(existingCompositeStringUniques, compositeStringUniques)) {
ourLog.info("Removing unique index: {}", next);
ourLog.debug("Removing unique index: {}", next);
myEntityManager.remove(next);
theEntity.getParamsCompositeStringUnique().remove(next);
}
@ -1776,7 +1777,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
throw new PreconditionFailedException("Can not create resource of type " + theEntity.getResourceType() + " as it would create a duplicate index matching query: " + next.getIndexString() + " (existing index belongs to " + existing.getResource().getIdDt().toUnqualifiedVersionless().getValue() + ")");
}
}
ourLog.info("Persisting unique index: {}", next);
ourLog.debug("Persisting unique index: {}", next);
myEntityManager.persist(next);
}
}
@ -1821,7 +1822,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
if (nextChildDef instanceof RuntimeChildResourceDefinition) {
RuntimeChildResourceDefinition nextChildDefRes = (RuntimeChildResourceDefinition) nextChildDef;
Set<String> validTypes = new HashSet<String>();
Set<String> validTypes = new HashSet<>();
boolean allowAny = false;
for (Class<? extends IBaseResource> nextValidType : nextChildDefRes.getResourceTypes()) {
if (nextValidType.isInterface()) {
@ -2147,7 +2148,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
}
if (forcedId.isEmpty() == false) {
List<Long> retVal = new ArrayList<Long>(forcedId.size());
List<Long> retVal = new ArrayList<>(forcedId.size());
for (ForcedId next : forcedId) {
retVal.add(next.getResourcePid());
}

View File

@ -127,7 +127,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
}
}
ourLog.info("Processed addTag {}/{} on {} in {}ms", theScheme, theTerm, theId, w.getMillisAndRestart());
ourLog.debug("Processed addTag {}/{} on {} in {}ms", theScheme, theTerm, theId, w.getMillisAndRestart());
}
@Override
@ -273,7 +273,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
validateDeleteConflictsEmptyOrThrowException(deleteConflicts);
ourLog.info("Processed delete on {} in {}ms", theId.getValue(), w.getMillisAndRestart());
ourLog.debug("Processed delete on {} in {}ms", theId.getValue(), w.getMillisAndRestart());
return retVal;
}
@ -350,7 +350,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
OperationOutcomeUtil.addIssue(getContext(), oo, severity, message, null, code);
}
ourLog.info("Processed delete on {} (matched {} resource(s)) in {}ms", theUrl, deletedResources.size(), w.getMillis());
ourLog.debug("Processed delete on {} (matched {} resource(s)) in {}ms", theUrl, deletedResources.size(), w.getMillis());
DeleteMethodOutcome retVal = new DeleteMethodOutcome();
retVal.setDeletedEntities(deletedResources);
@ -462,7 +462,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "successfulCreate", outcome.getId(), w.getMillisAndRestart());
outcome.setOperationOutcome(createInfoOperationOutcome(msg));
ourLog.info(msg);
ourLog.debug(msg);
return outcome;
}
@ -530,7 +530,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
StopWatch w = new StopWatch();
TagList tags = super.getTags(myResourceType, null);
ourLog.info("Processed getTags on {} in {}ms", myResourceName, w.getMillisAndRestart());
ourLog.debug("Processed getTags on {} in {}ms", myResourceName, w.getMillisAndRestart());
return tags;
}
@ -557,7 +557,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
StopWatch w = new StopWatch();
TagList retVal = super.getTags(myResourceType, theResourceId);
ourLog.info("Processed getTags on {} in {}ms", theResourceId, w.getMillisAndRestart());
ourLog.debug("Processed getTags on {} in {}ms", theResourceId, w.getMillisAndRestart());
return retVal;
}
@ -569,7 +569,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
StopWatch w = new StopWatch();
IBundleProvider retVal = super.history(myResourceName, null, theSince, theUntil);
ourLog.info("Processed history on {} in {}ms", myResourceName, w.getMillisAndRestart());
ourLog.debug("Processed history on {} in {}ms", myResourceName, w.getMillisAndRestart());
return retVal;
}
@ -586,7 +586,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
IBundleProvider retVal = super.history(myResourceName, entity.getId(), theSince, theUntil);
ourLog.info("Processed history on {} in {}ms", id, w.getMillisAndRestart());
ourLog.debug("Processed history on {} in {}ms", id, w.getMillisAndRestart());
return retVal;
}
@ -622,7 +622,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
if (myDaoConfig.isMarkResourcesForReindexingUponSearchParameterChange()) {
if (isNotBlank(theExpression)) {
final String resourceType = theExpression.substring(0, theExpression.indexOf('.'));
ourLog.info("Marking all resources of type {} for reindexing due to updated search parameter with path: {}", resourceType, theExpression);
ourLog.debug("Marking all resources of type {} for reindexing due to updated search parameter with path: {}", resourceType, theExpression);
TransactionTemplate txTemplate = new TransactionTemplate(myPlatformTransactionManager);
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
@ -633,7 +633,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
}
});
ourLog.info("Marked {} resources for reindexing", updatedCount);
ourLog.debug("Marked {} resources for reindexing", updatedCount);
}
}
@ -665,7 +665,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
doMetaAdd(theMetaAdd, history);
}
ourLog.info("Processed metaAddOperation on {} in {}ms", new Object[]{theResourceId, w.getMillisAndRestart()});
ourLog.debug("Processed metaAddOperation on {} in {}ms", new Object[]{theResourceId, w.getMillisAndRestart()});
@SuppressWarnings("unchecked")
MT retVal = (MT) metaGetOperation(theMetaAdd.getClass(), theResourceId, theRequestDetails);
@ -699,7 +699,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
myEntityManager.flush();
ourLog.info("Processed metaDeleteOperation on {} in {}ms", new Object[]{theResourceId.getValue(), w.getMillisAndRestart()});
ourLog.debug("Processed metaDeleteOperation on {} in {}ms", new Object[]{theResourceId.getValue(), w.getMillisAndRestart()});
@SuppressWarnings("unchecked")
MT retVal = (MT) metaGetOperation(theMetaDel.getClass(), theResourceId, theRequestDetails);
@ -864,7 +864,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
throw new ResourceGoneException("Resource was deleted at " + deleted.getValueAsString());
}
ourLog.info("Processed read on {} in {}ms", theId.getValue(), w.getMillisAndRestart());
ourLog.debug("Processed read on {} in {}ms", theId.getValue(), w.getMillisAndRestart());
return retVal;
}
@ -968,7 +968,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
myEntityManager.merge(entity);
ourLog.info("Processed remove tag {}/{} on {} in {}ms", theScheme, theTerm, theId.getValue(), w.getMillisAndRestart());
ourLog.debug("Processed remove tag {}/{} on {} in {}ms", theScheme, theTerm, theId.getValue(), w.getMillisAndRestart());
}
@Transactional(propagation = Propagation.SUPPORTS)
@ -1282,7 +1282,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "successfulCreate", outcome.getId(), w.getMillisAndRestart());
outcome.setOperationOutcome(createInfoOperationOutcome(msg));
ourLog.info(msg);
ourLog.debug(msg);
return outcome;
}
@ -1339,7 +1339,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
}
if (myDaoConfig.isEnforceReferentialIntegrityOnDelete() == false && !theForValidate) {
ourLog.info("Deleting {} resource dependencies which can no longer be satisfied", resultList.size());
ourLog.debug("Deleting {} resource dependencies which can no longer be satisfied", resultList.size());
myResourceLinkDao.delete(resultList);
return;
}

View File

@ -83,7 +83,7 @@ public class SearchBuilder implements ISearchBuilder {
private static final List<Long> EMPTY_LONG_LIST = Collections.unmodifiableList(new ArrayList<Long>());
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchBuilder.class);
private static Long NO_MORE = Long.valueOf(-1);
private static Long NO_MORE = -1L;
private static HandlerTypeEnum ourLastHandlerMechanismForUnitTest;
private List<Long> myAlsoIncludePids;
private CriteriaBuilder myBuilder;
@ -331,10 +331,9 @@ public class SearchBuilder implements ISearchBuilder {
List<Predicate> codePredicates = new ArrayList<Predicate>();
for (IQueryParameterType nextOr : theList) {
IQueryParameterType params = nextOr;
if (params instanceof ReferenceParam) {
ReferenceParam ref = (ReferenceParam) params;
if (nextOr instanceof ReferenceParam) {
ReferenceParam ref = (ReferenceParam) nextOr;
if (isBlank(ref.getChain())) {
IIdType dt = new IdDt(ref.getBaseUrl(), ref.getResourceType(), ref.getIdPart(), null);
@ -533,7 +532,7 @@ public class SearchBuilder implements ISearchBuilder {
}
} else {
throw new IllegalArgumentException("Invalid token type (expecting ReferenceParam): " + params.getClass());
throw new IllegalArgumentException("Invalid token type (expecting ReferenceParam): " + nextOr.getClass());
}
}
@ -2006,8 +2005,17 @@ public class SearchBuilder implements ISearchBuilder {
RuntimeResourceDefinition resourceDef = theContext.getResourceDefinition(theResourceType);
RuntimeSearchParam param = theCallingDao.getSearchParamByName(resourceDef, theParamName);
List<String> path = param.getPathsSplit();
Predicate type = theFrom.get("mySourcePath").in(path);
return type;
/*
* SearchParameters can declare paths on multiple resources
* types. Here we only want the ones that actually apply.
*/
for (Iterator<String> iter = path.iterator(); iter.hasNext(); ) {
if (!iter.next().startsWith(theResourceType + ".")) {
iter.remove();
}
}
return theFrom.get("mySourcePath").in(path);
}
private static List<Long> filterResourceIdsByLastUpdated(EntityManager theEntityManager, final DateRangeParam theLastUpdated, Collection<Long> thePids) {

View File

@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.dao.dstu3;
*/
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.trim;
import java.math.BigDecimal;
import java.util.*;
@ -478,8 +479,8 @@ public class SearchParamExtractorDstu3 extends BaseSearchParamExtractor implemen
multiType = true;
}
List<String> systems = new ArrayList<String>();
List<String> codes = new ArrayList<String>();
List<String> systems = new ArrayList<>();
List<String> codes = new ArrayList<>();
// String needContactPointSystem = null;
// if (nextPath.contains(".where(system='phone')")) {
@ -693,11 +694,11 @@ public class SearchParamExtractorDstu3 extends BaseSearchParamExtractor implemen
IWorkerContext worker = new org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext(getContext(), myValidationSupport);
FHIRPathEngine fp = new FHIRPathEngine(worker);
List<Object> values = new ArrayList<Object>();
List<Object> values = new ArrayList<>();
try {
String[] nextPathsSplit = SPLIT.split(thePaths);
for (String nextPath : nextPathsSplit) {
List<Base> allValues = fp.evaluate((Base) theResource, nextPath);
List<Base> allValues = fp.evaluate((Base) theResource, trim(nextPath));
if (allValues.isEmpty() == false) {
values.addAll(allValues);
}

View File

@ -40,9 +40,6 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
public class FhirResourceDaoSearchParameterR4 extends FhirResourceDaoR4<SearchParameter> implements IFhirResourceDaoSearchParameter<SearchParameter> {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoSearchParameterR4.class);
@Autowired
private IFhirSystemDao<Bundle, Meta> mySystemDao;
@ -130,7 +127,6 @@ public class FhirResourceDaoSearchParameterR4 extends FhirResourceDaoR4<SearchPa
theExpression = theExpression.trim();
String[] expressionSplit = BaseSearchParamExtractor.SPLIT.split(theExpression);
String allResourceName = null;
for (String nextPath : expressionSplit) {
nextPath = nextPath.trim();
@ -146,14 +142,6 @@ public class FhirResourceDaoSearchParameterR4 extends FhirResourceDaoR4<SearchPa
throw new UnprocessableEntityException("Invalid SearchParameter.expression value \"" + nextPath + "\": " + e.getMessage());
}
if (allResourceName == null) {
allResourceName = resourceName;
} else {
if (!allResourceName.equals(resourceName)) {
throw new UnprocessableEntityException("Invalid SearchParameter.expression value \"" + nextPath + "\". All paths in a single SearchParameter must match the same resource type");
}
}
}
} // if have expression

View File

@ -36,6 +36,7 @@ public abstract class BaseHasResource {
@Temporal(TemporalType.TIMESTAMP)
private Date myDeleted;
// TODO: move to resource history table
@Column(name = "RES_VERSION", nullable = true, length = 7)
@Enumerated(EnumType.STRING)
@OptimisticLock(excluded = true)

View File

@ -95,7 +95,6 @@ public class JavaMailEmailSender implements IEmailSender {
@Override
public void send(EmailDetails theDetails) {
String subscriptionId = theDetails.getSubscription().toUnqualifiedVersionless().getValue();
ourLog.info("Sending email for subscription {} to recipients: {}", subscriptionId, theDetails.getTo());
StopWatch sw = new StopWatch();
StringTemplateResolver templateResolver = new StringTemplateResolver();
@ -116,15 +115,18 @@ public class JavaMailEmailSender implements IEmailSender {
MimeMessage email = mySender.createMimeMessage();
String from = trim(theDetails.getFrom());
ourLog.info("Sending email for subscription {} from [{}] to recipients: [{}]", subscriptionId, from, theDetails.getTo());
try {
email.setFrom(trim(theDetails.getFrom()));
email.setFrom(from);
email.setRecipients(Message.RecipientType.TO, toTrimmedCommaSeparatedString(theDetails.getTo()));
email.setSubject(subject);
email.setText(body);
email.setSentDate(new Date());
email.addHeader("X-FHIR-Subscription", subscriptionId);
} catch (MessagingException e) {
throw new InternalErrorException("Failed to create email messaage", e);
throw new InternalErrorException("Failed to create email message", e);
}
mySender.send(email);

View File

@ -1,17 +1,5 @@
package ca.uhn.fhir.jpa.dao.dstu3;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.util.List;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.dstu3.model.Appointment.AppointmentStatus;
import org.hl7.fhir.dstu3.model.Enumerations.AdministrativeGender;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.*;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
@ -19,6 +7,19 @@ import ca.uhn.fhir.rest.param.*;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.util.TestUtil;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.dstu3.model.Appointment.AppointmentStatus;
import org.hl7.fhir.dstu3.model.Enumerations.AdministrativeGender;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Test;
import java.util.List;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu3Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu3SearchCustomSearchParamTest.class);
@ -228,6 +229,76 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
}
/**
* See #863
*/
@Test
public void testParamWithMultipleBasesReference() {
SearchParameter sp = new SearchParameter();
sp.setUrl("http://clinicalcloud.solutions/fhir/SearchParameter/request-reason");
sp.setName("reason");
sp.setStatus(Enumerations.PublicationStatus.ACTIVE);
sp.setCode("reason");
sp.addBase("MedicationRequest");
sp.addBase("ProcedureRequest");
sp.setType(Enumerations.SearchParamType.REFERENCE);
sp.setExpression("MedicationRequest.reasonReference | ProcedureRequest.reasonReference");
sp.addTarget("Condition");
sp.addTarget("Observation");
mySearchParameterDao.create(sp);
mySearchParamRegsitry.forceRefresh();
Condition condition = new Condition();
condition.getCode().setText("A condition");
String conditionId = myConditionDao.create(condition).getId().toUnqualifiedVersionless().getValue();
MedicationRequest mr = new MedicationRequest();
mr.addReasonReference().setReference(conditionId);
String mrId = myMedicationRequestDao.create(mr).getId().toUnqualifiedVersionless().getValue();
ProcedureRequest pr = new ProcedureRequest();
pr.addReasonReference().setReference(conditionId);
myProcedureRequestDao.create(pr);
SearchParameterMap map = new SearchParameterMap();
map.setLoadSynchronous(true);
map.add("reason", new ReferenceParam(conditionId));
List<String> results = toUnqualifiedVersionlessIdValues(myMedicationRequestDao.search(map));
assertThat(results, contains(mrId));
}
/**
* See #863
*/
@Test
public void testParamWithMultipleBasesToken() {
SearchParameter sp = new SearchParameter();
sp.setUrl("http://clinicalcloud.solutions/fhir/SearchParameter/request-reason");
sp.setName("reason");
sp.setStatus(Enumerations.PublicationStatus.ACTIVE);
sp.setCode("reason");
sp.addBase("MedicationRequest");
sp.addBase("ProcedureRequest");
sp.setType(Enumerations.SearchParamType.TOKEN);
sp.setExpression("MedicationRequest.reasonCode | ProcedureRequest.reasonCode");
mySearchParameterDao.create(sp);
mySearchParamRegsitry.forceRefresh();
MedicationRequest mr = new MedicationRequest();
mr.addReasonCode().addCoding().setSystem("foo").setCode("bar");
String mrId = myMedicationRequestDao.create(mr).getId().toUnqualifiedVersionless().getValue();
ProcedureRequest pr = new ProcedureRequest();
pr.addReasonCode().addCoding().setSystem("foo").setCode("bar");
myProcedureRequestDao.create(pr);
SearchParameterMap map = new SearchParameterMap();
map.setLoadSynchronous(true);
map.add("reason", new TokenParam("foo", "bar"));
List<String> results = toUnqualifiedVersionlessIdValues(myMedicationRequestDao.search(map));
assertThat(results, contains(mrId));
}
@Test
public void testSearchForExtensionReferenceWithNonMatchingTarget() {
SearchParameter siblingSp = new SearchParameter();

View File

@ -71,23 +71,6 @@ public class FhirResourceDaoR4SearchCustomSearchParamTest extends BaseJpaR4Test
}
}
@Test
public void testCreateInvalidParamMismatchedResourceName() {
SearchParameter fooSp = new SearchParameter();
fooSp.addBase("Patient");
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP");
fooSp.setExpression("Patient.gender or Observation.code");
fooSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE);
try {
mySearchParameterDao.create(fooSp, mySrd);
fail();
} catch (UnprocessableEntityException e) {
assertEquals("Invalid SearchParameter.expression value \"Observation.code\". All paths in a single SearchParameter must match the same resource type", e.getMessage());
}
}
@Test
public void testCreateInvalidParamNoPath() {
@ -334,6 +317,77 @@ public class FhirResourceDaoR4SearchCustomSearchParamTest extends BaseJpaR4Test
}), empty());
}
/**
* See #863
*/
@Test
public void testParamWithMultipleBases() {
SearchParameter sp = new SearchParameter();
sp.setUrl("http://clinicalcloud.solutions/fhir/SearchParameter/request-reason");
sp.setName("reason");
sp.setStatus(Enumerations.PublicationStatus.ACTIVE);
sp.setCode("reason");
sp.addBase("MedicationRequest");
sp.addBase("ServiceRequest");
sp.setType(Enumerations.SearchParamType.REFERENCE);
sp.setExpression("MedicationRequest.reasonReference | ServiceRequest.reasonReference");
sp.addTarget("Condition");
sp.addTarget("Observation");
mySearchParameterDao.create(sp);
mySearchParamRegsitry.forceRefresh();
Condition condition = new Condition();
condition.getCode().setText("A condition");
String conditionId = myConditionDao.create(condition).getId().toUnqualifiedVersionless().getValue();
MedicationRequest mr = new MedicationRequest();
mr.addReasonReference().setReference(conditionId);
String mrId = myMedicationRequestDao.create(mr).getId().toUnqualifiedVersionless().getValue();
ServiceRequest pr = new ServiceRequest();
pr.addReasonReference().setReference(conditionId);
myServiceRequestDao.create(pr);
SearchParameterMap map = new SearchParameterMap();
map.setLoadSynchronous(true);
map.add("reason", new ReferenceParam(conditionId));
List<String> results = toUnqualifiedVersionlessIdValues(myMedicationRequestDao.search(map));
assertThat(results, contains(mrId));
}
/**
* See #863
*/
@Test
public void testParamWithMultipleBasesToken() {
SearchParameter sp = new SearchParameter();
sp.setUrl("http://clinicalcloud.solutions/fhir/SearchParameter/request-reason");
sp.setName("reason");
sp.setStatus(Enumerations.PublicationStatus.ACTIVE);
sp.setCode("reason");
sp.addBase("MedicationRequest");
sp.addBase("ServiceRequest");
sp.setType(Enumerations.SearchParamType.TOKEN);
sp.setExpression("MedicationRequest.reasonCode | ServiceRequest.reasonCode");
mySearchParameterDao.create(sp);
mySearchParamRegsitry.forceRefresh();
MedicationRequest mr = new MedicationRequest();
mr.addReasonCode().addCoding().setSystem("foo").setCode("bar");
String mrId = myMedicationRequestDao.create(mr).getId().toUnqualifiedVersionless().getValue();
ServiceRequest pr = new ServiceRequest();
pr.addReasonCode().addCoding().setSystem("foo").setCode("bar");
myServiceRequestDao.create(pr);
SearchParameterMap map = new SearchParameterMap();
map.setLoadSynchronous(true);
map.add("reason", new TokenParam("foo", "bar"));
List<String> results = toUnqualifiedVersionlessIdValues(myMedicationRequestDao.search(map));
assertThat(results, contains(mrId));
}
@Test
public void testSearchForExtensionReferenceWithNonMatchingTarget() {
SearchParameter siblingSp = new SearchParameter();

View File

@ -96,27 +96,29 @@ public class Controller extends BaseController {
}
@RequestMapping(value = { "/delete" })
public String actionDelete(HttpServletRequest theReq, HomeRequest theRequest, BindingResult theBindingResult, ModelMap theModel) {
addCommonParams(theReq, theRequest, theModel);
public String actionDelete(HttpServletRequest theServletRequest, HomeRequest theRequest, BindingResult theBindingResult, ModelMap theModel) {
addCommonParams(theServletRequest, theRequest, theModel);
CaptureInterceptor interceptor = new CaptureInterceptor();
GenericClient client = theRequest.newClient(theReq, getContext(theRequest), myConfig, interceptor);
GenericClient client = theRequest.newClient(theServletRequest, getContext(theRequest), myConfig, interceptor);
RuntimeResourceDefinition def;
try {
def = getResourceType(theRequest, theReq);
def = getResourceType(theRequest, theServletRequest);
} catch (ServletException e) {
populateModelForResource(theServletRequest, theRequest, theModel);
theModel.put("errorMsg", toDisplayError(e.toString(), e));
return "resource";
}
String id = StringUtils.defaultString(theReq.getParameter("resource-delete-id"));
String id = StringUtils.defaultString(theServletRequest.getParameter("resource-delete-id"));
if (StringUtils.isBlank(id)) {
populateModelForResource(theServletRequest, theRequest, theModel);
theModel.put("errorMsg", toDisplayError("No ID specified", null));
return "resource";
}
ResultType returnsResource = ResultType.BUNDLE;
ResultType returnsResource = ResultType.RESOURCE;
String outcomeDescription = "Delete Resource";
long start = System.currentTimeMillis();
@ -192,27 +194,29 @@ public class Controller extends BaseController {
}
@RequestMapping(value = { "/read" })
public String actionRead(HttpServletRequest theReq, HomeRequest theRequest, BindingResult theBindingResult, ModelMap theModel) {
addCommonParams(theReq, theRequest, theModel);
public String actionRead(HttpServletRequest theServletRequest, HomeRequest theRequest, BindingResult theBindingResult, ModelMap theModel) {
addCommonParams(theServletRequest, theRequest, theModel);
CaptureInterceptor interceptor = new CaptureInterceptor();
GenericClient client = theRequest.newClient(theReq, getContext(theRequest), myConfig, interceptor);
GenericClient client = theRequest.newClient(theServletRequest, getContext(theRequest), myConfig, interceptor);
RuntimeResourceDefinition def;
try {
def = getResourceType(theRequest, theReq);
def = getResourceType(theRequest, theServletRequest);
} catch (ServletException e) {
populateModelForResource(theServletRequest, theRequest, theModel);
theModel.put("errorMsg", toDisplayError(e.toString(), e));
return "resource";
}
String id = StringUtils.defaultString(theReq.getParameter("id"));
String id = StringUtils.defaultString(theServletRequest.getParameter("id"));
if (StringUtils.isBlank(id)) {
populateModelForResource(theServletRequest, theRequest, theModel);
theModel.put("errorMsg", toDisplayError("No ID specified", null));
return "resource";
}
ResultType returnsResource = ResultType.RESOURCE;
String versionId = StringUtils.defaultString(theReq.getParameter("vid"));
String versionId = StringUtils.defaultString(theServletRequest.getParameter("vid"));
String outcomeDescription;
if (StringUtils.isBlank(versionId)) {
versionId = null;
@ -242,19 +246,38 @@ public class Controller extends BaseController {
@RequestMapping({ "/resource" })
public String actionResource(HttpServletRequest theServletRequest, final ResourceRequest theRequest, final BindingResult theBindingResult, final ModelMap theModel) {
IBaseResource conformance = addCommonParams(theServletRequest, theRequest, theModel);
CaptureInterceptor interceptor = new CaptureInterceptor();
GenericClient client = theRequest.newClient(theServletRequest, getContext(theRequest), myConfig, interceptor);
String resourceName = theRequest.getResource();
RuntimeResourceDefinition def = getContext(theRequest).getResourceDefinition(theRequest.getResource());
TreeSet<String> includes = new TreeSet<String>();
TreeSet<String> revIncludes = new TreeSet<String>();
TreeSet<String> sortParams = new TreeSet<String>();
populateModelForResource(theServletRequest, theRequest, theModel);
if (isNotBlank(theRequest.getUpdateId())) {
String updateId = theRequest.getUpdateId();
String updateVid = defaultIfEmpty(theRequest.getUpdateVid(), null);
CaptureInterceptor interceptor = new CaptureInterceptor();
GenericClient client = theRequest.newClient(theServletRequest, getContext(theRequest), myConfig, interceptor);
RuntimeResourceDefinition def = getContext(theRequest).getResourceDefinition(theRequest.getResource());
IBaseResource updateResource = client.read(def.getImplementingClass(), new IdDt(resourceName, updateId, updateVid));
String updateResourceString = theRequest.newParser(getContext(theRequest)).setPrettyPrint(true).encodeResourceToString(updateResource);
theModel.put("updateResource", updateResourceString);
theModel.put("updateResourceId", updateId);
}
ourLog.info(logPrefix(theModel) + "Showing resource page: {}", resourceName);
return "resource";
}
private void populateModelForResource(HttpServletRequest theServletRequest, HomeRequest theRequest, ModelMap theModel) {
IBaseResource conformance = addCommonParams(theServletRequest, theRequest, theModel);
String resourceName = theRequest.getResource();
TreeSet<String> includes = new TreeSet<>();
TreeSet<String> revIncludes = new TreeSet<>();
TreeSet<String> sortParams = new TreeSet<>();
boolean haveSearchParams = false;
List<List<String>> queryIncludes = new ArrayList<List<String>>();
List<List<String>> queryIncludes = new ArrayList<>();
switch (theRequest.getFhirVersion(myConfig)) {
case DSTU2:
@ -276,25 +299,12 @@ public class Controller extends BaseController {
theModel.put("haveSearchParams", haveSearchParams);
theModel.put("queryIncludes", queryIncludes);
theModel.put("sortParams", sortParams);
if (isNotBlank(theRequest.getUpdateId())) {
String updateId = theRequest.getUpdateId();
String updateVid = defaultIfEmpty(theRequest.getUpdateVid(), null);
IBaseResource updateResource = (IBaseResource) client.read(def.getImplementingClass(), new IdDt(resourceName, updateId, updateVid));
String updateResourceString = theRequest.newParser(getContext(theRequest)).setPrettyPrint(true).encodeResourceToString(updateResource);
theModel.put("updateResource", updateResourceString);
theModel.put("updateResourceId", updateId);
}
ourLog.info(logPrefix(theModel) + "Showing resource page: {}", resourceName);
return "resource";
}
@SuppressWarnings("unchecked")
@RequestMapping(value = { "/search" })
public String actionSearch(HttpServletRequest theReq, HomeRequest theRequest, BindingResult theBindingResult, ModelMap theModel) throws IOException {
addCommonParams(theReq, theRequest, theModel);
public String actionSearch(HttpServletRequest theServletRequest, HomeRequest theRequest, BindingResult theBindingResult, ModelMap theModel) throws IOException {
addCommonParams(theServletRequest, theRequest, theModel);
StringWriter clientCodeJsonStringWriter = new StringWriter();
JsonWriter clientCodeJsonWriter = new JsonWriter(clientCodeJsonStringWriter);
@ -305,19 +315,20 @@ public class Controller extends BaseController {
clientCodeJsonWriter.value((String) theModel.get("base"));
CaptureInterceptor interceptor = new CaptureInterceptor();
GenericClient client = theRequest.newClient(theReq, getContext(theRequest), myConfig, interceptor);
GenericClient client = theRequest.newClient(theServletRequest, getContext(theRequest), myConfig, interceptor);
IUntypedQuery search = client.search();
IQuery query;
if (isNotBlank(theReq.getParameter("resource"))) {
if (isNotBlank(theServletRequest.getParameter("resource"))) {
try {
query = search.forResource((Class<? extends IBaseResource>) getResourceType(theRequest, theReq).getImplementingClass());
query = search.forResource(getResourceType(theRequest, theServletRequest).getImplementingClass());
} catch (ServletException e) {
populateModelForResource(theServletRequest, theRequest, theModel);
theModel.put("errorMsg", toDisplayError(e.toString(), e));
return "resource";
}
clientCodeJsonWriter.name("resource");
clientCodeJsonWriter.value(theReq.getParameter("resource"));
clientCodeJsonWriter.value(theServletRequest.getParameter("resource"));
} else {
query = search.forAllResources();
clientCodeJsonWriter.name("resource");
@ -349,7 +360,7 @@ public class Controller extends BaseController {
paramIdx++;
String paramIdxString = Integer.toString(paramIdx);
boolean shouldContinue = handleSearchParam(paramIdxString, theReq, query, clientCodeJsonWriter);
boolean shouldContinue = handleSearchParam(paramIdxString, theServletRequest, query, clientCodeJsonWriter);
if (!shouldContinue) {
break;
}
@ -358,7 +369,7 @@ public class Controller extends BaseController {
clientCodeJsonWriter.name("includes");
clientCodeJsonWriter.beginArray();
String[] incValues = theReq.getParameterValues(Constants.PARAM_INCLUDE);
String[] incValues = theServletRequest.getParameterValues(Constants.PARAM_INCLUDE);
if (incValues != null) {
for (String next : incValues) {
if (isNotBlank(next)) {
@ -371,7 +382,7 @@ public class Controller extends BaseController {
clientCodeJsonWriter.name("revincludes");
clientCodeJsonWriter.beginArray();
String[] revIncValues = theReq.getParameterValues(Constants.PARAM_REVINCLUDE);
String[] revIncValues = theServletRequest.getParameterValues(Constants.PARAM_REVINCLUDE);
if (revIncValues != null) {
for (String next : revIncValues) {
if (isNotBlank(next)) {
@ -382,9 +393,10 @@ public class Controller extends BaseController {
}
clientCodeJsonWriter.endArray();
String limit = theReq.getParameter("resource-search-limit");
String limit = theServletRequest.getParameter("resource-search-limit");
if (isNotBlank(limit)) {
if (!limit.matches("[0-9]+")) {
populateModelForResource(theServletRequest, theRequest, theModel);
theModel.put("errorMsg", toDisplayError("Search limit must be a numeric value.", null));
return "resource";
}
@ -397,13 +409,13 @@ public class Controller extends BaseController {
clientCodeJsonWriter.nullValue();
}
String[] sort = theReq.getParameterValues("sort_by");
String[] sort = theServletRequest.getParameterValues("sort_by");
if (sort != null) {
for (String next : sort) {
if (isBlank(next)) {
continue;
}
String direction = theReq.getParameter("sort_direction");
String direction = theServletRequest.getParameter("sort_direction");
if ("asc".equals(direction)) {
query.sort().ascending(new StringClientParam(next));
} else if ("desc".equals(direction)) {
@ -505,7 +517,7 @@ public class Controller extends BaseController {
Class<? extends IBaseResource> type = null; // def.getImplementingClass();
if ("history-type".equals(theMethod)) {
RuntimeResourceDefinition def = getContext(theRequest).getResourceDefinition(theRequest.getResource());
type = (Class<? extends IBaseResource>) def.getImplementingClass();
type = def.getImplementingClass();
}
String body = validate ? theReq.getParameter("resource-validate-body") : theReq.getParameter("resource-create-body");
@ -588,7 +600,7 @@ public class Controller extends BaseController {
Class<? extends IBaseResource> type = null; // def.getImplementingClass();
if ("history-type".equals(theMethod)) {
RuntimeResourceDefinition def = getContext(theRequest).getResourceDefinition(theRequest.getResource());
type = (Class<? extends IBaseResource>) def.getImplementingClass();
type = def.getImplementingClass();
id = StringUtils.defaultString(theReq.getParameter("resource-history-id"));
}

View File

@ -1118,8 +1118,8 @@
<encoding>UTF-8</encoding>
<fork>true</fork>
<meminitial>128m</meminitial>
<maxmem>1600m</maxmem>
<meminitial>500m</meminitial>
<maxmem>2000m</maxmem>
</configuration>
<dependencies>
<dependency>

View File

@ -150,6 +150,21 @@
elements of type "Reference". Thanks to GitHub user @t4deon for supplying
a testcase!
</action>
<action type="fix">
Deleting a resource from the testpage overlay resulted in an error page after
clicking "delete", even though the delete succeeded.
</action>
<action type="remove">
A number of info level log lines have been reduced to debug level in the JPA server, in
order to reduce contention during heavy loads.
</action>
<action type="fix" issue="863">
JPA server now correctly indexes custom search parameters which
have multiple base resource types. Previously, the indexing could
cause resources of the wrong type to be returned in a search
if a parameter being used also matched that type. Thanks
to Dave Carlson for reporting!
</action>
</release>
<release version="3.2.0" date="2018-01-13">
<action type="add">