Merge branch 'master' into ja_20190928_rationalize_search_param_extractor

This commit is contained in:
James Agnew 2019-10-30 08:41:14 -04:00
commit b90f285218
35 changed files with 621 additions and 229 deletions

View File

@ -1,4 +1,5 @@
ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.expansionRefersToUnknownCs=Unknown CodeSystem URI "{0}" referenced from ValueSet
# Core Library Messages # Core Library Messages
ca.uhn.fhir.context.FhirContext.unknownResourceName=Unknown resource name "{0}" (this name is not known in FHIR version "{1}") ca.uhn.fhir.context.FhirContext.unknownResourceName=Unknown resource name "{0}" (this name is not known in FHIR version "{1}")

View File

@ -132,6 +132,7 @@ public class FhirResourceDaoCodeSystemDstu3 extends BaseHapiFhirResourceDao<Code
public ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing, public ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) { boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry); ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry);
if (!retVal.isUnchangedInCurrentOperation()) {
CodeSystem csDstu3 = (CodeSystem) theResource; CodeSystem csDstu3 = (CodeSystem) theResource;
@ -140,6 +141,8 @@ public class FhirResourceDaoCodeSystemDstu3 extends BaseHapiFhirResourceDao<Code
myTerminologyCodeSystemStorageSvc.storeNewCodeSystemVersionIfNeeded(cs, theEntity); myTerminologyCodeSystemStorageSvc.storeNewCodeSystemVersionIfNeeded(cs, theEntity);
}
return retVal; return retVal;
} }

View File

@ -161,6 +161,7 @@ public class FhirResourceDaoConceptMapDstu3 extends BaseHapiFhirResourceDao<Conc
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) { boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry); ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry);
if (!retVal.isUnchangedInCurrentOperation()) {
if (retVal.getDeleted() == null) { if (retVal.getDeleted() == null) {
try { try {
ConceptMap conceptMap = (ConceptMap) theResource; ConceptMap conceptMap = (ConceptMap) theResource;
@ -172,6 +173,7 @@ public class FhirResourceDaoConceptMapDstu3 extends BaseHapiFhirResourceDao<Conc
} else { } else {
myHapiTerminologySvc.deleteConceptMapAndChildren(retVal); myHapiTerminologySvc.deleteConceptMapAndChildren(retVal);
} }
}
return retVal; return retVal;
} }

View File

@ -415,7 +415,7 @@ public class FhirResourceDaoValueSetDstu3 extends BaseHapiFhirResourceDao<ValueS
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) { boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry); ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry);
if (myDaoConfig.isPreExpandValueSets()) { if (myDaoConfig.isPreExpandValueSets() && !retVal.isUnchangedInCurrentOperation()) {
if (retVal.getDeleted() == null) { if (retVal.getDeleted() == null) {
try { try {
ValueSet valueSet = (ValueSet) theResource; ValueSet valueSet = (ValueSet) theResource;

View File

@ -136,10 +136,12 @@ public class FhirResourceDaoCodeSystemR4 extends BaseHapiFhirResourceDao<CodeSys
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) { boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry); ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry);
if (!retVal.isUnchangedInCurrentOperation()) {
CodeSystem cs = (CodeSystem) theResource; CodeSystem cs = (CodeSystem) theResource;
addPidToResource(theEntity, theResource); addPidToResource(theEntity, theResource);
myTerminologyCodeSystemStorageSvc.storeNewCodeSystemVersionIfNeeded(cs, theEntity); myTerminologyCodeSystemStorageSvc.storeNewCodeSystemVersionIfNeeded(cs, theEntity);
}
return retVal; return retVal;
} }

View File

@ -161,12 +161,14 @@ public class FhirResourceDaoConceptMapR4 extends BaseHapiFhirResourceDao<Concept
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) { boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry); ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry);
if (!retVal.isUnchangedInCurrentOperation()) {
if (retVal.getDeleted() == null) { if (retVal.getDeleted() == null) {
ConceptMap conceptMap = (ConceptMap) theResource; ConceptMap conceptMap = (ConceptMap) theResource;
myHapiTerminologySvc.storeTermConceptMapAndChildren(retVal, conceptMap); myHapiTerminologySvc.storeTermConceptMapAndChildren(retVal, conceptMap);
} else { } else {
myHapiTerminologySvc.deleteConceptMapAndChildren(retVal); myHapiTerminologySvc.deleteConceptMapAndChildren(retVal);
} }
}
return retVal; return retVal;
} }

View File

@ -396,7 +396,7 @@ public class FhirResourceDaoValueSetR4 extends BaseHapiFhirResourceDao<ValueSet>
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) { boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry); ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry);
if (myDaoConfig.isPreExpandValueSets()) { if (myDaoConfig.isPreExpandValueSets() && !retVal.isUnchangedInCurrentOperation()) {
if (retVal.getDeleted() == null) { if (retVal.getDeleted() == null) {
ValueSet valueSet = (ValueSet) theResource; ValueSet valueSet = (ValueSet) theResource;
myHapiTerminologySvc.storeTermValueSet(retVal, valueSet); myHapiTerminologySvc.storeTermValueSet(retVal, valueSet);

View File

@ -135,11 +135,13 @@ public class FhirResourceDaoCodeSystemR5 extends BaseHapiFhirResourceDao<CodeSys
public ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing, public ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) { boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry); ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry);
if (!retVal.isUnchangedInCurrentOperation()) {
CodeSystem cs = (CodeSystem) theResource; CodeSystem cs = (CodeSystem) theResource;
addPidToResource(theEntity, theResource); addPidToResource(theEntity, theResource);
myTerminologyCodeSystemStorageSvc.storeNewCodeSystemVersionIfNeeded(org.hl7.fhir.convertors.conv40_50.CodeSystem.convertCodeSystem(cs), theEntity); myTerminologyCodeSystemStorageSvc.storeNewCodeSystemVersionIfNeeded(org.hl7.fhir.convertors.conv40_50.CodeSystem.convertCodeSystem(cs), theEntity);
}
return retVal; return retVal;
} }

View File

@ -161,6 +161,7 @@ public class FhirResourceDaoConceptMapR5 extends BaseHapiFhirResourceDao<Concept
public ResourceTable updateEntity(RequestDetails theRequestDetails, IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing, public ResourceTable updateEntity(RequestDetails theRequestDetails, IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) { boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry); ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry);
if (!retVal.isUnchangedInCurrentOperation()) {
if (retVal.getDeleted() == null) { if (retVal.getDeleted() == null) {
ConceptMap conceptMap = (ConceptMap) theResource; ConceptMap conceptMap = (ConceptMap) theResource;
@ -168,6 +169,7 @@ public class FhirResourceDaoConceptMapR5 extends BaseHapiFhirResourceDao<Concept
} else { } else {
myHapiTerminologySvc.deleteConceptMapAndChildren(retVal); myHapiTerminologySvc.deleteConceptMapAndChildren(retVal);
} }
}
return retVal; return retVal;
} }

View File

@ -408,7 +408,7 @@ public class FhirResourceDaoValueSetR5 extends BaseHapiFhirResourceDao<ValueSet>
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) { boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry); ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry);
if (myDaoConfig.isPreExpandValueSets()) { if (myDaoConfig.isPreExpandValueSets() && !retVal.isUnchangedInCurrentOperation()) {
if (retVal.getDeleted() == null) { if (retVal.getDeleted() == null) {
ValueSet valueSet = (ValueSet) theResource; ValueSet valueSet = (ValueSet) theResource;
myHapiTerminologySvc.storeTermValueSet(retVal, org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(valueSet)); myHapiTerminologySvc.storeTermValueSet(retVal, org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(valueSet));

View File

@ -19,24 +19,25 @@ package ca.uhn.fhir.jpa.provider.r4;
* limitations under the License. * limitations under the License.
* #L% * #L%
*/ */
import java.util.*;
import javax.servlet.http.HttpServletRequest;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.CapabilityStatement.*;
import org.hl7.fhir.r4.model.Enumerations.SearchParamType;
import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam; import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.IFhirSystemDao; import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.util.CoverageIgnore; import ca.uhn.fhir.util.CoverageIgnore;
import ca.uhn.fhir.util.ExtensionConstants; import ca.uhn.fhir.util.ExtensionConstants;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.CapabilityStatement.*;
import org.hl7.fhir.r4.model.Enumerations.SearchParamType;
import javax.servlet.http.HttpServletRequest;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank;
@ -141,6 +142,9 @@ public class JpaConformanceProviderR4 extends org.hl7.fhir.r4.hapi.rest.server.S
case URI: case URI:
confSp.setType(SearchParamType.URI); confSp.setType(SearchParamType.URI);
break; break;
case SPECIAL:
confSp.setType(SearchParamType.SPECIAL);
break;
case HAS: case HAS:
// Shouldn't happen // Shouldn't happen
break; break;
@ -171,6 +175,10 @@ public class JpaConformanceProviderR4 extends org.hl7.fhir.r4.hapi.rest.server.S
return myIncludeResourceCounts; return myIncludeResourceCounts;
} }
public void setIncludeResourceCounts(boolean theIncludeResourceCounts) {
myIncludeResourceCounts = theIncludeResourceCounts;
}
/** /**
* Subclasses may override * Subclasses may override
*/ */
@ -187,10 +195,6 @@ public class JpaConformanceProviderR4 extends org.hl7.fhir.r4.hapi.rest.server.S
myImplementationDescription = theImplDesc; myImplementationDescription = theImplDesc;
} }
public void setIncludeResourceCounts(boolean theIncludeResourceCounts) {
myIncludeResourceCounts = theIncludeResourceCounts;
}
@Override @Override
public void setRestfulServer(RestfulServer theRestfulServer) { public void setRestfulServer(RestfulServer theRestfulServer) {
this.myRestfulServer = theRestfulServer; this.myRestfulServer = theRestfulServer;

View File

@ -140,6 +140,9 @@ public class JpaConformanceProviderR5 extends org.hl7.fhir.r5.hapi.rest.server.S
case URI: case URI:
confSp.setType(SearchParamType.URI); confSp.setType(SearchParamType.URI);
break; break;
case SPECIAL:
confSp.setType(SearchParamType.SPECIAL);
break;
case HAS: case HAS:
// Shouldn't happen // Shouldn't happen
break; break;
@ -170,6 +173,10 @@ public class JpaConformanceProviderR5 extends org.hl7.fhir.r5.hapi.rest.server.S
return myIncludeResourceCounts; return myIncludeResourceCounts;
} }
public void setIncludeResourceCounts(boolean theIncludeResourceCounts) {
myIncludeResourceCounts = theIncludeResourceCounts;
}
/** /**
* Subclasses may override * Subclasses may override
*/ */
@ -186,10 +193,6 @@ public class JpaConformanceProviderR5 extends org.hl7.fhir.r5.hapi.rest.server.S
myImplementationDescription = theImplDesc; myImplementationDescription = theImplDesc;
} }
public void setIncludeResourceCounts(boolean theIncludeResourceCounts) {
myIncludeResourceCounts = theIncludeResourceCounts;
}
@Override @Override
public void setRestfulServer(RestfulServer theRestfulServer) { public void setRestfulServer(RestfulServer theRestfulServer) {
this.myRestfulServer = theRestfulServer; this.myRestfulServer = theRestfulServer;

View File

@ -195,6 +195,11 @@ public class ResourceReindexingSvcImpl implements IResourceReindexingSvc {
mySchedulerService.scheduleFixedDelay(10 * DateUtils.MILLIS_PER_SECOND, true, jobDetail); mySchedulerService.scheduleFixedDelay(10 * DateUtils.MILLIS_PER_SECOND, true, jobDetail);
} }
@VisibleForTesting
ReentrantLock getIndexingLockForUnitTest() {
return myIndexingLock;
}
@Override @Override
@Transactional(Transactional.TxType.NEVER) @Transactional(Transactional.TxType.NEVER)
public Integer runReindexingPass() { public Integer runReindexingPass() {

View File

@ -675,7 +675,10 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc, ApplicationCo
CodeSystem codeSystemFromContext = getCodeSystemFromContext(system); CodeSystem codeSystemFromContext = getCodeSystemFromContext(system);
if (codeSystemFromContext == null) { if (codeSystemFromContext == null) {
throw new InvalidRequestException("Unknown code system: " + system); String msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "expansionRefersToUnknownCs", system);
ourLog.warn(msg);
theValueSetCodeAccumulator.addMessage(msg);
return false;
} }
if (!theIncludeOrExclude.getConcept().isEmpty()) { if (!theIncludeOrExclude.getConcept().isEmpty()) {

View File

@ -27,6 +27,8 @@ import java.util.Collection;
public interface IValueSetConceptAccumulator { public interface IValueSetConceptAccumulator {
void addMessage(String theMessage);
void includeConcept(String theSystem, String theCode, String theDisplay); void includeConcept(String theSystem, String theCode, String theDisplay);
void includeConceptWithDesignations(String theSystem, String theCode, String theDisplay, Collection<TermConceptDesignation> theDesignations); void includeConceptWithDesignations(String theSystem, String theCode, String theDisplay, Collection<TermConceptDesignation> theDesignations);

View File

@ -57,6 +57,12 @@ public class ValueSetConceptAccumulator implements IValueSetConceptAccumulator {
myConceptsExcluded = 0; myConceptsExcluded = 0;
} }
@Override
public void addMessage(String theMessage) {
// ignore for now
}
@Override @Override
public void includeConcept(String theSystem, String theCode, String theDisplay) { public void includeConcept(String theSystem, String theCode, String theDisplay) {
saveConcept(theSystem, theCode, theDisplay); saveConcept(theSystem, theCode, theDisplay);
@ -82,7 +88,7 @@ public class ValueSetConceptAccumulator implements IValueSetConceptAccumulator {
if (optionalConcept.isPresent()) { if (optionalConcept.isPresent()) {
TermValueSetConcept concept = optionalConcept.get(); TermValueSetConcept concept = optionalConcept.get();
ourLog.info("Excluding [{}|{}] from ValueSet[{}]", concept.getSystem(), concept.getCode(), myTermValueSet.getUrl()); ourLog.debug("Excluding [{}|{}] from ValueSet[{}]", concept.getSystem(), concept.getCode(), myTermValueSet.getUrl());
for (TermValueSetConceptDesignation designation : concept.getDesignations()) { for (TermValueSetConceptDesignation designation : concept.getDesignations()) {
myValueSetConceptDesignationDao.deleteById(designation.getId()); myValueSetConceptDesignationDao.deleteById(designation.getId());
myTermValueSet.decrementTotalConceptDesignations(); myTermValueSet.decrementTotalConceptDesignations();
@ -90,7 +96,7 @@ public class ValueSetConceptAccumulator implements IValueSetConceptAccumulator {
myValueSetConceptDao.deleteById(concept.getId()); myValueSetConceptDao.deleteById(concept.getId());
myTermValueSet.decrementTotalConcepts(); myTermValueSet.decrementTotalConcepts();
myValueSetDao.save(myTermValueSet); myValueSetDao.save(myTermValueSet);
ourLog.info("Done excluding [{}|{}] from ValueSet[{}]", concept.getSystem(), concept.getCode(), myTermValueSet.getUrl()); ourLog.debug("Done excluding [{}|{}] from ValueSet[{}]", concept.getSystem(), concept.getCode(), myTermValueSet.getUrl());
if (++myConceptsExcluded % 250 == 0) { if (++myConceptsExcluded % 250 == 0) {
ourLog.info("Have excluded {} concepts from ValueSet[{}]", myConceptsExcluded, myTermValueSet.getUrl()); ourLog.info("Have excluded {} concepts from ValueSet[{}]", myConceptsExcluded, myTermValueSet.getUrl());

View File

@ -22,9 +22,11 @@ package ca.uhn.fhir.jpa.term;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.entity.TermConceptDesignation; import ca.uhn.fhir.jpa.entity.TermConceptDesignation;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.term.ex.ExpansionTooCostlyException; import ca.uhn.fhir.jpa.term.ex.ExpansionTooCostlyException;
import ca.uhn.fhir.model.api.annotation.Block; import ca.uhn.fhir.model.api.annotation.Block;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import org.hl7.fhir.r4.model.StringType;
import org.hl7.fhir.r4.model.ValueSet; import org.hl7.fhir.r4.model.ValueSet;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -54,6 +56,13 @@ public class ValueSetExpansionComponentWithConceptAccumulator extends ValueSet.V
return myMaxCapacity - myConceptsCount; return myMaxCapacity - myConceptsCount;
} }
@Override
public void addMessage(String theMessage) {
addExtension()
.setUrl(JpaConstants.EXT_VALUESET_EXPANSION_MESSAGE)
.setValue(new StringType(theMessage));
}
@Override @Override
public void includeConcept(String theSystem, String theCode, String theDisplay) { public void includeConcept(String theSystem, String theCode, String theDisplay) {
incrementConceptsCount(); incrementConceptsCount();

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.jpa.dao.r5; package ca.uhn.fhir.jpa.dao.r5;
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.util.TestUtil; import ca.uhn.fhir.jpa.util.TestUtil;
import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.Constants;
@ -9,11 +11,14 @@ import ca.uhn.fhir.rest.param.HasOrListParam;
import ca.uhn.fhir.rest.param.HasParam; import ca.uhn.fhir.rest.param.HasParam;
import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.param.StringParam;
import org.hl7.fhir.r5.model.Organization; import org.hl7.fhir.r5.model.Organization;
import org.hl7.fhir.r5.model.Patient;
import org.hl7.fhir.r5.model.Practitioner; import org.hl7.fhir.r5.model.Practitioner;
import org.hl7.fhir.r5.model.PractitionerRole; import org.hl7.fhir.r5.model.PractitionerRole;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.Test; import org.junit.Test;
import java.util.Date;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@SuppressWarnings({"unchecked", "Duplicates"}) @SuppressWarnings({"unchecked", "Duplicates"})
@ -98,6 +103,40 @@ public class FhirResourceDaoR5SearchNoFtTest extends BaseJpaR5Test {
assertEquals(1, outcome.getResources(0, 1).size()); assertEquals(1, outcome.getResources(0, 1).size());
} }
@Test
public void testSearchDoesntFailIfResourcesAreDeleted() {
Patient p = new Patient();
p.addIdentifier().setValue("1");
myPatientDao.create(p);
p = new Patient();
p.addIdentifier().setValue("2");
myPatientDao.create(p);
p = new Patient();
p.addIdentifier().setValue("3");
Long id = myPatientDao.create(p).getId().getIdPartAsLong();
IBundleProvider outcome = myPatientDao.search(new SearchParameterMap());
assertEquals(3, outcome.size().intValue());
runInTransaction(()->{
ResourceTable table = myResourceTableDao.findById(id).orElseThrow(() -> new IllegalArgumentException());
table.setDeleted(new Date());
myResourceTableDao.save(table);
});
assertEquals(2, outcome.getResources(0, 3).size());
runInTransaction(()->{
myResourceHistoryTableDao.deleteAll();
});
assertEquals(0, outcome.getResources(0, 3).size());
}
@AfterClass @AfterClass
public static void afterClassClearContext() { public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest(); TestUtil.clearAllStaticFieldsForUnitTest();

View File

@ -4080,22 +4080,16 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
Patient patient = new Patient(); Patient patient = new Patient();
patient.addName().addGiven("James"); patient.addName().addGiven("James");
patient.setBirthDateElement(new DateType("2011-02-02")); patient.setBirthDateElement(new DateType("2011-02-02"));
patient.addContact().setGender(AdministrativeGender.MALE);
String inputStr = myFhirCtx.newXmlParser().encodeResourceToString(patient); String inputStr = myFhirCtx.newXmlParser().encodeResourceToString(patient);
HttpPost post = new HttpPost(ourServerBase + "/Patient/$validate"); HttpPost post = new HttpPost(ourServerBase + "/Patient/$validate");
post.setEntity(new StringEntity(inputStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); post.setEntity(new StringEntity(inputStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
CloseableHttpResponse response = ourHttpClient.execute(post); try (CloseableHttpResponse response = ourHttpClient.execute(post)) {
try {
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(resp); ourLog.info(resp);
assertEquals(412, response.getStatusLine().getStatusCode()); assertEquals(200, response.getStatusLine().getStatusCode());
assertThat(resp, not(containsString("Resource has no id"))); assertThat(resp, not(containsString("Resource has no id")));
assertThat(resp, containsString("<issue><severity value=\"error\"/><code value=\"processing\"/><diagnostics value=\"SHALL at least contain a contact's details or a reference to an organization [name.exists() or telecom.exists() or address.exists() or organization.exists()]\"/><location value=\"Patient.contact[0]\"/><location value=\"Line 0, Col 0\"/></issue>"));
} finally {
IOUtils.closeQuietly(response.getEntity().getContent());
response.close();
} }
} }

View File

@ -68,7 +68,7 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
private static SubscriptionMatcherInterceptor ourSubscriptionMatcherInterceptor; private static SubscriptionMatcherInterceptor ourSubscriptionMatcherInterceptor;
protected static Server ourServer; protected static Server ourServer;
protected IGenericClient ourClient; protected IGenericClient ourClient;
ResourceCountCache ourResourceCountsCache; ResourceCountCache myResourceCountsCache;
private TerminologyUploaderProvider myTerminologyUploaderProvider; private TerminologyUploaderProvider myTerminologyUploaderProvider;
private boolean ourRestHookSubscriptionInterceptorRequested; private boolean ourRestHookSubscriptionInterceptorRequested;
@ -93,6 +93,7 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
myFhirCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); myFhirCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
myFhirCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000); myFhirCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000);
myFhirCtx.setParserErrorHandler(new StrictErrorHandler()); myFhirCtx.setParserErrorHandler(new StrictErrorHandler());
myResourceCountsCache = (ResourceCountCache) myAppCtx.getBean("myResourceCountsCache");
if (ourServer == null) { if (ourServer == null) {
ourRestServer = new RestfulServer(myFhirCtx); ourRestServer = new RestfulServer(myFhirCtx);
@ -113,7 +114,6 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
ourRestServer.setServerConformanceProvider(confProvider); ourRestServer.setServerConformanceProvider(confProvider);
ourPagingProvider = myAppCtx.getBean(DatabaseBackedPagingProvider.class); ourPagingProvider = myAppCtx.getBean(DatabaseBackedPagingProvider.class);
ourResourceCountsCache = (ResourceCountCache) myAppCtx.getBean("myResourceCountsCache");
Server server = new Server(0); Server server = new Server(0);

View File

@ -5308,7 +5308,6 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
Patient patient = new Patient(); Patient patient = new Patient();
patient.addName().addGiven("James"); patient.addName().addGiven("James");
patient.setBirthDateElement(new DateType("2011-02-02")); patient.setBirthDateElement(new DateType("2011-02-02"));
patient.addContact().setGender(AdministrativeGender.MALE);
String inputStr = myFhirCtx.newXmlParser().encodeResourceToString(patient); String inputStr = myFhirCtx.newXmlParser().encodeResourceToString(patient);
HttpPost post = new HttpPost(ourServerBase + "/Patient/$validate"); HttpPost post = new HttpPost(ourServerBase + "/Patient/$validate");
@ -5317,9 +5316,8 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
try (CloseableHttpResponse response = ourHttpClient.execute(post)) { try (CloseableHttpResponse response = ourHttpClient.execute(post)) {
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(resp); ourLog.info(resp);
assertEquals(412, response.getStatusLine().getStatusCode()); assertEquals(200, response.getStatusLine().getStatusCode());
assertThat(resp, not(containsString("Resource has no id"))); assertThat(resp, not(containsString("Resource has no id")));
assertThat(resp, containsString("<issue><severity value=\"error\"/><code value=\"processing\"/><diagnostics value=\"SHALL at least contain a contact's details or a reference to an organization [name.exists() or telecom.exists() or address.exists() or organization.exists()]\"/><location value=\"Patient.contact[0]\"/><location value=\"Line 0, Col 0\"/></issue>"));
} }
} }

View File

@ -1,6 +1,9 @@
package ca.uhn.fhir.jpa.provider.r4; package ca.uhn.fhir.jpa.provider.r4;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.util.ExtensionConstants; import ca.uhn.fhir.util.ExtensionConstants;
import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.TestUtil;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
@ -13,6 +16,7 @@ import org.hl7.fhir.r4.model.Extension;
import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.Patient;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
@ -25,6 +29,29 @@ public class ServerR4Test extends BaseResourceProviderR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerR4Test.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerR4Test.class);
@Autowired
private IFhirResourceDao<CapabilityStatement> myCapabilityStatementDao;
@Test
public void testCapabilityStatementValidates() throws IOException {
HttpGet get = new HttpGet(ourServerBase + "/metadata?_pretty=true&_format=json");
try (CloseableHttpResponse resp = ourHttpClient.execute(get)) {
assertEquals(200, resp.getStatusLine().getStatusCode());
String respString = IOUtils.toString(resp.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(respString);
CapabilityStatement cs = myFhirCtx.newJsonParser().parseResource(CapabilityStatement.class, respString);
try {
myCapabilityStatementDao.validate(cs, null, respString, EncodingEnum.JSON, null, null, null);
} catch (PreconditionFailedException e) {
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(e.getOperationOutcome()));
fail();
}
}
}
/** /**
* See #519 * See #519
@ -71,7 +98,7 @@ public class ServerR4Test extends BaseResourceProviderR4Test {
* Initial fetch after a clear should return * Initial fetch after a clear should return
* no results * no results
*/ */
ourResourceCountsCache.clear(); myResourceCountsCache.clear();
CapabilityStatement capabilityStatement = ourClient CapabilityStatement capabilityStatement = ourClient
.capabilities() .capabilities()
@ -93,7 +120,7 @@ public class ServerR4Test extends BaseResourceProviderR4Test {
* Now run a background pass (the update * Now run a background pass (the update
* method is called by the scheduler normally) * method is called by the scheduler normally)
*/ */
ourResourceCountsCache.update(); myResourceCountsCache.update();
capabilityStatement = ourClient capabilityStatement = ourClient
.capabilities() .capabilities()

View File

@ -0,0 +1,151 @@
package ca.uhn.fhir.jpa.provider.r5;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.util.ExtensionConstants;
import ca.uhn.fhir.util.TestUtil;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.hl7.fhir.r5.model.CapabilityStatement;
import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestResourceComponent;
import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent;
import org.hl7.fhir.r5.model.Extension;
import org.hl7.fhir.r5.model.Patient;
import org.junit.AfterClass;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.Set;
import static org.junit.Assert.*;
public class ServerR5Test extends BaseResourceProviderR5Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerR5Test.class);
@Autowired
private IFhirResourceDao<CapabilityStatement> myCapabilityStatementDao;
@Test
@Ignore
public void testCapabilityStatementValidates() throws IOException {
HttpGet get = new HttpGet(ourServerBase + "/metadata?_pretty=true&_format=json");
try (CloseableHttpResponse resp = ourHttpClient.execute(get)) {
assertEquals(200, resp.getStatusLine().getStatusCode());
String respString = IOUtils.toString(resp.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(respString);
CapabilityStatement cs = myFhirCtx.newJsonParser().parseResource(CapabilityStatement.class, respString);
try {
myCapabilityStatementDao.validate(cs, null, respString, EncodingEnum.JSON, null, null, null);
} catch (PreconditionFailedException e) {
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(e.getOperationOutcome()));
fail();
}
}
}
/**
* See #519
*/
@Test
public void saveIdParamOnlyAppearsOnce() throws IOException {
HttpGet get = new HttpGet(ourServerBase + "/metadata?_pretty=true&_format=xml");
CloseableHttpResponse resp = ourHttpClient.execute(get);
try {
ourLog.info(resp.toString());
assertEquals(200, resp.getStatusLine().getStatusCode());
String respString = IOUtils.toString(resp.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(respString);
CapabilityStatement cs = myFhirCtx.newXmlParser().parseResource(CapabilityStatement.class, respString);
for (CapabilityStatementRestResourceComponent nextResource : cs.getRest().get(0).getResource()) {
ourLog.info("Testing resource: " + nextResource.getType());
Set<String> sps = new HashSet<String>();
for (CapabilityStatementRestResourceSearchParamComponent nextSp : nextResource.getSearchParam()) {
if (sps.add(nextSp.getName()) == false) {
fail("Duplicate search parameter " + nextSp.getName() + " for resource " + nextResource.getType());
}
}
if (!sps.contains("_id")) {
fail("No search parameter _id for resource " + nextResource.getType());
}
}
} finally {
IOUtils.closeQuietly(resp.getEntity().getContent());
}
}
@Test
public void testMetadataIncludesResourceCounts() {
Patient p = new Patient();
p.setActive(true);
ourClient.create().resource(p).execute();
/*
* Initial fetch after a clear should return
* no results
*/
myResourceCountsCache.clear();
CapabilityStatement capabilityStatement = ourClient
.capabilities()
.ofType(CapabilityStatement.class)
.execute();
Extension patientCountExt = capabilityStatement
.getRest()
.get(0)
.getResource()
.stream()
.filter(t -> t.getType().equals("Patient"))
.findFirst()
.orElseThrow(() -> new InternalErrorException("No patient"))
.getExtensionByUrl(ExtensionConstants.CONF_RESOURCE_COUNT);
assertNull(patientCountExt);
/*
* Now run a background pass (the update
* method is called by the scheduler normally)
*/
myResourceCountsCache.update();
capabilityStatement = ourClient
.capabilities()
.ofType(CapabilityStatement.class)
.execute();
patientCountExt = capabilityStatement
.getRest()
.get(0)
.getResource()
.stream()
.filter(t -> t.getType().equals("Patient"))
.findFirst()
.orElseThrow(() -> new InternalErrorException("No patient"))
.getExtensionByUrl(ExtensionConstants.CONF_RESOURCE_COUNT);
assertEquals("1", patientCountExt.getValueAsPrimitive().getValueAsString());
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
}

View File

@ -26,6 +26,8 @@ import org.springframework.data.domain.SliceImpl;
import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.PlatformTransactionManager;
import java.util.*; import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
@ -94,6 +96,17 @@ public class ResourceReindexingSvcImplTest extends BaseJpaTest {
mySvc.start(); mySvc.start();
} }
@Test
public void testNoParallelReindexing() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(1);
new Thread(()->{
mySvc.getIndexingLockForUnitTest().lock();
latch.countDown();
}).start();
latch.await(10, TimeUnit.SECONDS);
mySvc.runReindexingPass();
}
@Test @Test
public void testReindexPassOnlyReturnsValuesAtLowThreshold() { public void testReindexPassOnlyReturnsValuesAtLowThreshold() {
mockNothingToExpunge(); mockNothingToExpunge();

View File

@ -0,0 +1,75 @@
package ca.uhn.fhir.jpa.term;
import ca.uhn.fhir.jpa.dao.data.ITermValueSetConceptDao;
import ca.uhn.fhir.jpa.dao.data.ITermValueSetConceptDesignationDao;
import ca.uhn.fhir.jpa.dao.data.ITermValueSetDao;
import ca.uhn.fhir.jpa.entity.TermValueSet;
import ca.uhn.fhir.jpa.entity.TermValueSetConcept;
import ca.uhn.fhir.jpa.entity.TermValueSetConceptDesignation;
import org.hl7.fhir.r4.model.ValueSet;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.Optional;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class ValueSetConceptAccumulatorTest {
private ValueSetConceptAccumulator myAccumulator;
private TermValueSet myValueSet;
@Mock
private ITermValueSetDao myValueSetDao;
@Mock
private ITermValueSetConceptDesignationDao myValueSetDesignationDao;
@Mock
private ITermValueSetConceptDao myValueSetConceptDao;
@Before
public void before() {
myValueSet = new TermValueSet();
myAccumulator = new ValueSetConceptAccumulator(myValueSet, myValueSetDao, myValueSetConceptDao, myValueSetDesignationDao);
}
@Test
public void testIncludeConcept() {
for (int i = 0; i < 1000; i++) {
myAccumulator.includeConcept("sys", "code", "display");
}
verify(myValueSetConceptDao, times(1000)).save(any());
}
@Test
public void testExcludeBlankConcept() {
myAccumulator.excludeConcept("", "");
verifyNoInteractions(myValueSetConceptDao);
}
@Test
public void testAddMessage() {
myAccumulator.addMessage("foo");
verifyNoInteractions(myValueSetConceptDao);
}
@Test
public void testExcludeConceptWithDesignations() {
for (int i = 0; i <1000; i++) {
TermValueSetConcept value = new TermValueSetConcept();
value.setCode("code");
value.getDesignations().add(new TermValueSetConceptDesignation().setValue("foo"));
when(myValueSetConceptDao.findByTermValueSetIdSystemAndCode(any(), eq("sys"), eq("code"+i))).thenReturn(Optional.of(value));
myAccumulator.excludeConcept("sys", "code"+i);
}
}
}

View File

@ -2,11 +2,13 @@ package ca.uhn.fhir.jpa.term;
import ca.uhn.fhir.jpa.entity.*; import ca.uhn.fhir.jpa.entity.*;
import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.term.custom.CustomTerminologySet; import ca.uhn.fhir.jpa.term.custom.CustomTerminologySet;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.CodeSystem; import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.Extension;
import org.hl7.fhir.r4.model.ValueSet; import org.hl7.fhir.r4.model.ValueSet;
import org.hl7.fhir.r4.model.codesystems.HttpVerb; import org.hl7.fhir.r4.model.codesystems.HttpVerb;
import org.junit.Test; import org.junit.Test;
@ -688,6 +690,21 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test {
assertEquals("Systolische bloeddruk minimaal 1 uur", designationComponent.getValue()); assertEquals("Systolische bloeddruk minimaal 1 uur", designationComponent.getValue());
} }
@Test
public void testExpandValueSetWithUnknownCodeSystem() {
ValueSet vs = new ValueSet();
ValueSet.ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem("http://unknown-system");
ValueSet outcome = myTermSvc.expandValueSetInMemory(vs, null);
assertEquals(0, outcome.getExpansion().getContains().size());
String encoded = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome);
ourLog.info(encoded);
Extension extensionByUrl = outcome.getExpansion().getExtensionByUrl(JpaConstants.EXT_VALUESET_EXPANSION_MESSAGE);
assertEquals("Unknown CodeSystem URI \"http://unknown-system\" referenced from ValueSet", extensionByUrl.getValueAsPrimitive().getValueAsString());
}
@Test @Test
public void testExpandTermValueSetAndChildrenWithOffsetAndCountWithClientAssignedId() throws Exception { public void testExpandTermValueSetAndChildrenWithOffsetAndCountWithClientAssignedId() throws Exception {
myDaoConfig.setPreExpandValueSets(true); myDaoConfig.setPreExpandValueSets(true);

View File

@ -24,6 +24,13 @@ import ca.uhn.fhir.rest.api.Constants;
public class JpaConstants { public class JpaConstants {
/**
* Non-instantiable
*/
private JpaConstants() {
// nothing
}
/** /**
* Operation name for the $apply-codesystem-delta-add operation * Operation name for the $apply-codesystem-delta-add operation
*/ */
@ -243,6 +250,12 @@ public class JpaConstants {
*/ */
public static final String EXTENSION_EXT_SYSTEMDEFINED = JpaConstants.class.getName() + "_EXTENSION_EXT_SYSTEMDEFINED"; public static final String EXTENSION_EXT_SYSTEMDEFINED = JpaConstants.class.getName() + "_EXTENSION_EXT_SYSTEMDEFINED";
/**
* Message added to expansion valueset
*/
public static final String EXT_VALUESET_EXPANSION_MESSAGE = "http://hapifhir.io/fhir/StructureDefinition/valueset-expansion-message";
/** /**
* Parameter for the $export operation * Parameter for the $export operation
*/ */

View File

@ -11,6 +11,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@ -50,7 +52,17 @@ public class SubscriptionLoaderTest extends BaseBlockingQueueSubscribableChannel
@Test @Test
public void testMultipleThreadsDontBlock() throws InterruptedException { public void testMultipleThreadsDontBlock() throws InterruptedException {
SubscriptionLoader svc = new SubscriptionLoader(); SubscriptionLoader svc = new SubscriptionLoader();
CountDownLatch latch = new CountDownLatch(1);
new Thread(()->{
try {
svc.acquireSemaphoreForUnitTest(); svc.acquireSemaphoreForUnitTest();
latch.countDown();
} catch (InterruptedException theE) {
// ignore
}
}).start();
latch.await(10, TimeUnit.SECONDS);
svc.syncSubscriptions(); svc.syncSubscriptions();
} }

View File

@ -140,8 +140,7 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding {
if (Constants.PARAM_HISTORY.equals(theRequest.getOperation())) { if (Constants.PARAM_HISTORY.equals(theRequest.getOperation())) {
if (mySupportsVersion == false) { if (mySupportsVersion == false) {
return false; return false;
} } else if (theRequest.getId().hasVersionIdPart() == false) {
if (theRequest.getId().hasVersionIdPart() == false) {
return false; return false;
} }
} else if (!StringUtils.isBlank(theRequest.getOperation())) { } else if (!StringUtils.isBlank(theRequest.getOperation())) {

View File

@ -101,6 +101,31 @@ public class ReadMethodBindingTest {
when(myRequestDetails.getOperation()).thenReturn("$foo"); when(myRequestDetails.getOperation()).thenReturn("$foo");
assertFalse(binding.incomingServerRequestMatchesMethod(myRequestDetails)); assertFalse(binding.incomingServerRequestMatchesMethod(myRequestDetails));
// History operation
when(myRequestDetails.getId()).thenReturn(new IdDt("Patient/123"));
when(myRequestDetails.getOperation()).thenReturn("_history");
assertFalse(binding.incomingServerRequestMatchesMethod(myRequestDetails));
}
@Test
public void testIncomingServerRequestNoMatch_HasCompartment() throws NoSuchMethodException {
class MyProvider {
@Read(version = false)
public IBaseResource read(@IdParam IIdType theIdType) {
return null;
}
}
when(myCtx.getResourceDefinition(any(Class.class))).thenReturn(definition);
when(definition.getName()).thenReturn("Patient");
when(myRequestDetails.getResourceName()).thenReturn("Patient");
when(myRequestDetails.getCompartmentName()).thenReturn("Patient");
when(myRequestDetails.getId()).thenReturn(new IdDt("Patient/123"));
ReadMethodBinding binding = createBinding(new MyProvider());
assertFalse(binding.incomingServerRequestMatchesMethod(myRequestDetails));
} }
public ReadMethodBinding createBinding(Object theProvider) throws NoSuchMethodException { public ReadMethodBinding createBinding(Object theProvider) throws NoSuchMethodException {

View File

@ -9,7 +9,10 @@ import ca.uhn.fhir.rest.annotation.Metadata;
import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.*; import ca.uhn.fhir.rest.server.Bindings;
import ca.uhn.fhir.rest.server.IServerConformanceProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.RestfulServerConfiguration;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.method.*; import ca.uhn.fhir.rest.server.method.*;
import ca.uhn.fhir.rest.server.method.SearchParameter; import ca.uhn.fhir.rest.server.method.SearchParameter;
@ -113,7 +116,6 @@ public class ServerCapabilityStatementProvider extends BaseServerCapabilityState
} }
private DateTimeType conformanceDate(RequestDetails theRequestDetails) { private DateTimeType conformanceDate(RequestDetails theRequestDetails) {
IPrimitiveType<Date> buildDate = getServerConfiguration(theRequestDetails).getConformanceDate(); IPrimitiveType<Date> buildDate = getServerConfiguration(theRequestDetails).getConformanceDate();
if (buildDate != null && buildDate.getValue() != null) { if (buildDate != null && buildDate.getValue() != null) {
@ -127,7 +129,6 @@ public class ServerCapabilityStatementProvider extends BaseServerCapabilityState
} }
/** /**
* Gets the value of the "publisher" that will be placed in the generated conformance statement. As this is a mandatory element, the value should not be null (although this is not enforced). The * Gets the value of the "publisher" that will be placed in the generated conformance statement. As this is a mandatory element, the value should not be null (although this is not enforced). The
* value defaults to "Not provided" but may be set to null, which will cause this element to be omitted. * value defaults to "Not provided" but may be set to null, which will cause this element to be omitted.
@ -194,7 +195,7 @@ public class ServerCapabilityStatementProvider extends BaseServerCapabilityState
// Map<String, CapabilityStatement.RestResourceSearchParam> nameToSearchParam = new HashMap<String, // Map<String, CapabilityStatement.RestResourceSearchParam> nameToSearchParam = new HashMap<String,
// CapabilityStatement.RestResourceSearchParam>(); // CapabilityStatement.RestResourceSearchParam>();
for (BaseMethodBinding<?> nextMethodBinding : nextEntry.getValue()) { for (BaseMethodBinding<?> nextMethodBinding : nextEntry.getValue()) {
if (nextMethodBinding.getRestOperationType() != null) { nextMethodBinding.getRestOperationType();
String resOpCode = nextMethodBinding.getRestOperationType().getCode(); String resOpCode = nextMethodBinding.getRestOperationType().getCode();
if (resOpCode != null) { if (resOpCode != null) {
TypeRestfulInteraction resOp; TypeRestfulInteraction resOp;
@ -238,7 +239,6 @@ public class ServerCapabilityStatementProvider extends BaseServerCapabilityState
} }
} }
} }
}
checkBindingForSystemOps(rest, systemOps, nextMethodBinding); checkBindingForSystemOps(rest, systemOps, nextMethodBinding);
@ -315,28 +315,18 @@ public class ServerCapabilityStatementProvider extends BaseServerCapabilityState
} }
sortSearchParameters(searchParameters); sortSearchParameters(searchParameters);
if (!searchParameters.isEmpty()) { if (!searchParameters.isEmpty()) {
// boolean allOptional = searchParameters.get(0).isRequired() == false;
//
// OperationDefinition query = null;
// if (!allOptional) {
// RestOperation operation = rest.addOperation();
// query = new OperationDefinition();
// operation.setDefinition(new ResourceReferenceDt(query));
// query.getDescriptionElement().setValue(searchMethodBinding.getDescription());
// query.addUndeclaredExtension(false, ExtensionConstants.QUERY_RETURN_TYPE, new CodeDt(resourceName));
// for (String nextInclude : searchMethodBinding.getIncludes()) {
// query.addUndeclaredExtension(false, ExtensionConstants.QUERY_ALLOWED_INCLUDE, new StringDt(nextInclude));
// }
// }
for (SearchParameter nextParameter : searchParameters) { for (SearchParameter nextParameter : searchParameters) {
if (nextParameter.getParamType() == null) {
ourLog.warn("SearchParameter {}:{} does not declare a type - Not exporting in CapabilityStatement", def.getName(), nextParameter.getName());
continue;
}
String nextParamName = nextParameter.getName(); String nextParamName = nextParameter.getName();
String chain = null;
String nextParamUnchainedName = nextParamName; String nextParamUnchainedName = nextParamName;
if (nextParamName.contains(".")) { if (nextParamName.contains(".")) {
chain = nextParamName.substring(nextParamName.indexOf('.') + 1);
nextParamUnchainedName = nextParamName.substring(0, nextParamName.indexOf('.')); nextParamUnchainedName = nextParamName.substring(0, nextParamName.indexOf('.'));
} }
@ -352,43 +342,16 @@ public class ServerCapabilityStatementProvider extends BaseServerCapabilityState
} }
} }
CapabilityStatementRestResourceSearchParamComponent param = resource.addSearchParam(); CapabilityStatementRestResourceSearchParamComponent param = resource.addSearchParam();
String typeCode = nextParameter.getParamType().getCode();
param.getTypeElement().setValueAsString(typeCode);
param.setName(nextParamUnchainedName); param.setName(nextParamUnchainedName);
// if (StringUtils.isNotBlank(chain)) {
// param.addChain(chain);
// }
//
// if (nextParameter.getParamType() == RestSearchParameterTypeEnum.REFERENCE) {
// for (String nextWhitelist : new TreeSet<String>(nextParameter.getQualifierWhitelist())) {
// if (nextWhitelist.startsWith(".")) {
// param.addChain(nextWhitelist.substring(1));
// }
// }
// }
param.setDocumentation(nextParamDescription); param.setDocumentation(nextParamDescription);
if (nextParameter.getParamType() != null) {
param.getTypeElement().setValueAsString(nextParameter.getParamType().getCode());
}
for (Class<? extends IBaseResource> nextTarget : nextParameter.getDeclaredTypes()) {
RuntimeResourceDefinition targetDef = getServerConfiguration(theRequestDetails).getFhirContext().getResourceDefinition(nextTarget);
if (targetDef != null) {
ResourceType code;
try {
code = ResourceType.fromCode(targetDef.getName());
} catch (FHIRException e) {
code = null;
}
// if (code != null) {
// param.addTarget(targetDef.getName());
// }
}
}
}
}
}
}
}
}
@Read(type = OperationDefinition.class) @Read(type = OperationDefinition.class)

View File

@ -111,7 +111,11 @@ public class ValidatorWrapper {
profileSet.getCanonical().add(new ValidationProfileSet.ProfileRegistration(nextProfile, true)); profileSet.getCanonical().add(new ValidationProfileSet.ProfileRegistration(nextProfile, true));
} }
v.validate(null, messages, document, profileSet); String resourceAsString = theValidationContext.getResourceAsString();
InputStream inputStream = new ReaderInputStream(new StringReader(resourceAsString), Charsets.UTF_8);
Manager.FhirFormat format = Manager.FhirFormat.XML;
v.validate(null, messages, inputStream, format, profileSet);
} else if (encoding == EncodingEnum.JSON) { } else if (encoding == EncodingEnum.JSON) {

View File

@ -799,6 +799,8 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
ourLog.info(output.getMessages().get(0).getMessage()); ourLog.info(output.getMessages().get(0).getMessage());
assertEquals("/f:Patient", output.getMessages().get(0).getLocationString()); assertEquals("/f:Patient", output.getMessages().get(0).getLocationString());
assertEquals("Undefined element 'foo'", output.getMessages().get(0).getMessage()); assertEquals("Undefined element 'foo'", output.getMessages().get(0).getMessage());
assertEquals(28, output.getMessages().get(0).getLocationCol().intValue());
assertEquals(4, output.getMessages().get(0).getLocationLine().intValue());
} }
@Test @Test

View File

@ -476,6 +476,10 @@
<action type="fix"> <action type="fix">
Search parameters of type URI did not work in the hapi-fhir-testpage-overlay. This has been corrected. Search parameters of type URI did not work in the hapi-fhir-testpage-overlay. This has been corrected.
</action> </action>
<action type="fix" issue="1568">
JPA servers accidentally stripped the type attribute from the server-exported CapabilityStatement
when search parameters of type "special" were found. This has been corrected.
</action>
</release> </release>
<release version="4.0.3" date="2019-09-03" description="Igloo (Point Release)"> <release version="4.0.3" date="2019-09-03" description="Igloo (Point Release)">
<action type="fix"> <action type="fix">

View File

@ -262,6 +262,16 @@
<td style="background: #CEC;">4.0.0</td> <td style="background: #CEC;">4.0.0</td>
<td style="background: #EEB;">4.1.0-e0e3caf9ba</td> <td style="background: #EEB;">4.1.0-e0e3caf9ba</td>
</tr> </tr>
<tr>
<td>HAPI FHIR 4.1.0</td>
<td>JDK8</td>
<td style="background: #DDD;"></td>
<td style="background: #CEC;">1.0.2</td>
<td style="background: #EEB;">1.4.0</td>
<td style="background: #CEC;">3.0.1</td>
<td style="background: #CEC;">4.0.0</td>
<td style="background: #EEB;">4.1.0-1a7623d866</td>
</tr>
</tbody> </tbody>
</table> </table>