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
ca.uhn.fhir.context.FhirContext.unknownResourceName=Unknown resource name "{0}" (this name is not known in FHIR version "{1}")

View File

@ -132,13 +132,16 @@ public class FhirResourceDaoCodeSystemDstu3 extends BaseHapiFhirResourceDao<Code
public ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean 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;
org.hl7.fhir.r4.model.CodeSystem cs = VersionConvertor_30_40.convertCodeSystem(csDstu3);
addPidToResource(theEntity, cs);
org.hl7.fhir.r4.model.CodeSystem cs = VersionConvertor_30_40.convertCodeSystem(csDstu3);
addPidToResource(theEntity, cs);
myTerminologyCodeSystemStorageSvc.storeNewCodeSystemVersionIfNeeded(cs, theEntity);
myTerminologyCodeSystemStorageSvc.storeNewCodeSystemVersionIfNeeded(cs, theEntity);
}
return retVal;
}

View File

@ -161,16 +161,18 @@ public class FhirResourceDaoConceptMapDstu3 extends BaseHapiFhirResourceDao<Conc
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry);
if (retVal.getDeleted() == null) {
try {
ConceptMap conceptMap = (ConceptMap) theResource;
org.hl7.fhir.r4.model.ConceptMap converted = VersionConvertor_30_40.convertConceptMap(conceptMap);
myHapiTerminologySvc.storeTermConceptMapAndChildren(retVal, converted);
} catch (FHIRException fe) {
throw new InternalErrorException(fe);
if (!retVal.isUnchangedInCurrentOperation()) {
if (retVal.getDeleted() == null) {
try {
ConceptMap conceptMap = (ConceptMap) theResource;
org.hl7.fhir.r4.model.ConceptMap converted = VersionConvertor_30_40.convertConceptMap(conceptMap);
myHapiTerminologySvc.storeTermConceptMapAndChildren(retVal, converted);
} catch (FHIRException fe) {
throw new InternalErrorException(fe);
}
} else {
myHapiTerminologySvc.deleteConceptMapAndChildren(retVal);
}
} else {
myHapiTerminologySvc.deleteConceptMapAndChildren(retVal);
}
return retVal;

View File

@ -415,7 +415,7 @@ public class FhirResourceDaoValueSetDstu3 extends BaseHapiFhirResourceDao<ValueS
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean 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) {
try {
ValueSet valueSet = (ValueSet) theResource;

View File

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

View File

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

View File

@ -396,7 +396,7 @@ public class FhirResourceDaoValueSetR4 extends BaseHapiFhirResourceDao<ValueSet>
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean 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) {
ValueSet valueSet = (ValueSet) theResource;
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,
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry);
if (!retVal.isUnchangedInCurrentOperation()) {
CodeSystem cs = (CodeSystem) theResource;
addPidToResource(theEntity, theResource);
CodeSystem cs = (CodeSystem) 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;
}

View File

@ -161,14 +161,16 @@ public class FhirResourceDaoConceptMapR5 extends BaseHapiFhirResourceDao<Concept
public ResourceTable updateEntity(RequestDetails theRequestDetails, IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry);
if (!retVal.isUnchangedInCurrentOperation()) {
if (retVal.getDeleted() == null) {
ConceptMap conceptMap = (ConceptMap) theResource;
myHapiTerminologySvc.storeTermConceptMapAndChildren(retVal, org.hl7.fhir.convertors.conv40_50.ConceptMap.convertConceptMap(conceptMap));
} else {
myHapiTerminologySvc.deleteConceptMapAndChildren(retVal);
if (retVal.getDeleted() == null) {
ConceptMap conceptMap = (ConceptMap) theResource;
myHapiTerminologySvc.storeTermConceptMapAndChildren(retVal, org.hl7.fhir.convertors.conv40_50.ConceptMap.convertConceptMap(conceptMap));
} else {
myHapiTerminologySvc.deleteConceptMapAndChildren(retVal);
}
}
return retVal;
}
}

View File

@ -408,7 +408,7 @@ public class FhirResourceDaoValueSetR5 extends BaseHapiFhirResourceDao<ValueSet>
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean 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) {
ValueSet valueSet = (ValueSet) theResource;
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.
* #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.RuntimeSearchParam;
import ca.uhn.fhir.jpa.dao.DaoConfig;
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.util.CoverageIgnore;
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.StringUtils.isNotBlank;
@ -50,17 +51,17 @@ public class JpaConformanceProviderR4 extends org.hl7.fhir.r4.hapi.rest.server.S
private boolean myIncludeResourceCounts;
private RestfulServer myRestfulServer;
private IFhirSystemDao<Bundle, Meta> mySystemDao;
/**
* Constructor
*/
@CoverageIgnore
public JpaConformanceProviderR4(){
public JpaConformanceProviderR4() {
super();
super.setCache(false);
setIncludeResourceCounts(true);
}
/**
* Constructor
*/
@ -94,7 +95,7 @@ public class JpaConformanceProviderR4 extends org.hl7.fhir.r4.hapi.rest.server.S
for (CapabilityStatementRestResourceComponent nextResource : nextRest.getResource()) {
nextResource.setVersioning(ResourceVersionPolicy.VERSIONEDUPDATE);
ConditionalDeleteStatus conditionalDelete = nextResource.getConditionalDelete();
if (conditionalDelete == ConditionalDeleteStatus.MULTIPLE && myDaoConfig.isAllowMultipleDelete() == false) {
nextResource.setConditionalDelete(ConditionalDeleteStatus.SINGLE);
@ -109,7 +110,7 @@ public class JpaConformanceProviderR4 extends org.hl7.fhir.r4.hapi.rest.server.S
nextResource.getSearchParam().clear();
String resourceName = nextResource.getType();
RuntimeResourceDefinition resourceDef = myRestfulServer.getFhirContext().getResourceDefinition(resourceName);
Collection<RuntimeSearchParam> searchParams = mySearchParamRegistry.getSearchParamsByResourceType(resourceDef);
Collection<RuntimeSearchParam> searchParams = mySearchParamRegistry.getSearchParamsByResourceType(resourceDef);
for (RuntimeSearchParam runtimeSp : searchParams) {
CapabilityStatementRestResourceSearchParamComponent confSp = nextResource.addSearchParam();
@ -117,37 +118,40 @@ public class JpaConformanceProviderR4 extends org.hl7.fhir.r4.hapi.rest.server.S
confSp.setDocumentation(runtimeSp.getDescription());
confSp.setDefinition(runtimeSp.getUri());
switch (runtimeSp.getParamType()) {
case COMPOSITE:
confSp.setType(SearchParamType.COMPOSITE);
break;
case DATE:
confSp.setType(SearchParamType.DATE);
break;
case NUMBER:
confSp.setType(SearchParamType.NUMBER);
break;
case QUANTITY:
confSp.setType(SearchParamType.QUANTITY);
break;
case REFERENCE:
confSp.setType(SearchParamType.REFERENCE);
break;
case STRING:
confSp.setType(SearchParamType.STRING);
break;
case TOKEN:
confSp.setType(SearchParamType.TOKEN);
break;
case URI:
confSp.setType(SearchParamType.URI);
break;
case HAS:
// Shouldn't happen
break;
case COMPOSITE:
confSp.setType(SearchParamType.COMPOSITE);
break;
case DATE:
confSp.setType(SearchParamType.DATE);
break;
case NUMBER:
confSp.setType(SearchParamType.NUMBER);
break;
case QUANTITY:
confSp.setType(SearchParamType.QUANTITY);
break;
case REFERENCE:
confSp.setType(SearchParamType.REFERENCE);
break;
case STRING:
confSp.setType(SearchParamType.STRING);
break;
case TOKEN:
confSp.setType(SearchParamType.TOKEN);
break;
case URI:
confSp.setType(SearchParamType.URI);
break;
case SPECIAL:
confSp.setType(SearchParamType.SPECIAL);
break;
case HAS:
// Shouldn't happen
break;
}
}
}
}
@ -161,7 +165,7 @@ public class JpaConformanceProviderR4 extends org.hl7.fhir.r4.hapi.rest.server.S
}
massage(retVal);
retVal.getImplementation().setDescription(myImplementationDescription);
myCachedValue = retVal;
return retVal;
@ -170,7 +174,11 @@ public class JpaConformanceProviderR4 extends org.hl7.fhir.r4.hapi.rest.server.S
public boolean isIncludeResourceCounts() {
return myIncludeResourceCounts;
}
public void setIncludeResourceCounts(boolean theIncludeResourceCounts) {
myIncludeResourceCounts = theIncludeResourceCounts;
}
/**
* Subclasses may override
*/
@ -187,10 +195,6 @@ public class JpaConformanceProviderR4 extends org.hl7.fhir.r4.hapi.rest.server.S
myImplementationDescription = theImplDesc;
}
public void setIncludeResourceCounts(boolean theIncludeResourceCounts) {
myIncludeResourceCounts = theIncludeResourceCounts;
}
@Override
public void setRestfulServer(RestfulServer theRestfulServer) {
this.myRestfulServer = theRestfulServer;

View File

@ -51,16 +51,16 @@ public class JpaConformanceProviderR5 extends org.hl7.fhir.r5.hapi.rest.server.S
private boolean myIncludeResourceCounts;
private RestfulServer myRestfulServer;
private IFhirSystemDao<Bundle, Meta> mySystemDao;
/**
* Constructor
*/
@CoverageIgnore
public JpaConformanceProviderR5(){
public JpaConformanceProviderR5() {
super();
setIncludeResourceCounts(true);
}
/**
* Constructor
*/
@ -93,7 +93,7 @@ public class JpaConformanceProviderR5 extends org.hl7.fhir.r5.hapi.rest.server.S
for (CapabilityStatementRestResourceComponent nextResource : nextRest.getResource()) {
nextResource.setVersioning(ResourceVersionPolicy.VERSIONEDUPDATE);
ConditionalDeleteStatus conditionalDelete = nextResource.getConditionalDelete();
if (conditionalDelete == ConditionalDeleteStatus.MULTIPLE && myDaoConfig.isAllowMultipleDelete() == false) {
nextResource.setConditionalDelete(ConditionalDeleteStatus.SINGLE);
@ -108,7 +108,7 @@ public class JpaConformanceProviderR5 extends org.hl7.fhir.r5.hapi.rest.server.S
nextResource.getSearchParam().clear();
String resourceName = nextResource.getType();
RuntimeResourceDefinition resourceDef = myRestfulServer.getFhirContext().getResourceDefinition(resourceName);
Collection<RuntimeSearchParam> searchParams = mySearchParamRegistry.getSearchParamsByResourceType(resourceDef);
Collection<RuntimeSearchParam> searchParams = mySearchParamRegistry.getSearchParamsByResourceType(resourceDef);
for (RuntimeSearchParam runtimeSp : searchParams) {
CapabilityStatementRestResourceSearchParamComponent confSp = nextResource.addSearchParam();
@ -116,37 +116,40 @@ public class JpaConformanceProviderR5 extends org.hl7.fhir.r5.hapi.rest.server.S
confSp.setDocumentation(runtimeSp.getDescription());
confSp.setDefinition(runtimeSp.getUri());
switch (runtimeSp.getParamType()) {
case COMPOSITE:
confSp.setType(SearchParamType.COMPOSITE);
break;
case DATE:
confSp.setType(SearchParamType.DATE);
break;
case NUMBER:
confSp.setType(SearchParamType.NUMBER);
break;
case QUANTITY:
confSp.setType(SearchParamType.QUANTITY);
break;
case REFERENCE:
confSp.setType(SearchParamType.REFERENCE);
break;
case STRING:
confSp.setType(SearchParamType.STRING);
break;
case TOKEN:
confSp.setType(SearchParamType.TOKEN);
break;
case URI:
confSp.setType(SearchParamType.URI);
break;
case HAS:
// Shouldn't happen
break;
case COMPOSITE:
confSp.setType(SearchParamType.COMPOSITE);
break;
case DATE:
confSp.setType(SearchParamType.DATE);
break;
case NUMBER:
confSp.setType(SearchParamType.NUMBER);
break;
case QUANTITY:
confSp.setType(SearchParamType.QUANTITY);
break;
case REFERENCE:
confSp.setType(SearchParamType.REFERENCE);
break;
case STRING:
confSp.setType(SearchParamType.STRING);
break;
case TOKEN:
confSp.setType(SearchParamType.TOKEN);
break;
case URI:
confSp.setType(SearchParamType.URI);
break;
case SPECIAL:
confSp.setType(SearchParamType.SPECIAL);
break;
case HAS:
// Shouldn't happen
break;
}
}
}
}
@ -160,7 +163,7 @@ public class JpaConformanceProviderR5 extends org.hl7.fhir.r5.hapi.rest.server.S
}
massage(retVal);
retVal.getImplementation().setDescription(myImplementationDescription);
myCachedValue = retVal;
return retVal;
@ -169,7 +172,11 @@ public class JpaConformanceProviderR5 extends org.hl7.fhir.r5.hapi.rest.server.S
public boolean isIncludeResourceCounts() {
return myIncludeResourceCounts;
}
public void setIncludeResourceCounts(boolean theIncludeResourceCounts) {
myIncludeResourceCounts = theIncludeResourceCounts;
}
/**
* Subclasses may override
*/
@ -186,10 +193,6 @@ public class JpaConformanceProviderR5 extends org.hl7.fhir.r5.hapi.rest.server.S
myImplementationDescription = theImplDesc;
}
public void setIncludeResourceCounts(boolean theIncludeResourceCounts) {
myIncludeResourceCounts = theIncludeResourceCounts;
}
@Override
public void setRestfulServer(RestfulServer theRestfulServer) {
this.myRestfulServer = theRestfulServer;

View File

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

View File

@ -675,7 +675,10 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc, ApplicationCo
CodeSystem codeSystemFromContext = getCodeSystemFromContext(system);
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()) {

View File

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

View File

@ -57,6 +57,12 @@ public class ValueSetConceptAccumulator implements IValueSetConceptAccumulator {
myConceptsExcluded = 0;
}
@Override
public void addMessage(String theMessage) {
// ignore for now
}
@Override
public void includeConcept(String theSystem, String theCode, String theDisplay) {
saveConcept(theSystem, theCode, theDisplay);
@ -82,7 +88,7 @@ public class ValueSetConceptAccumulator implements IValueSetConceptAccumulator {
if (optionalConcept.isPresent()) {
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()) {
myValueSetConceptDesignationDao.deleteById(designation.getId());
myTermValueSet.decrementTotalConceptDesignations();
@ -90,7 +96,7 @@ public class ValueSetConceptAccumulator implements IValueSetConceptAccumulator {
myValueSetConceptDao.deleteById(concept.getId());
myTermValueSet.decrementTotalConcepts();
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) {
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.jpa.entity.TermConceptDesignation;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.term.ex.ExpansionTooCostlyException;
import ca.uhn.fhir.model.api.annotation.Block;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import org.hl7.fhir.r4.model.StringType;
import org.hl7.fhir.r4.model.ValueSet;
import javax.annotation.Nullable;
@ -54,6 +56,13 @@ public class ValueSetExpansionComponentWithConceptAccumulator extends ValueSet.V
return myMaxCapacity - myConceptsCount;
}
@Override
public void addMessage(String theMessage) {
addExtension()
.setUrl(JpaConstants.EXT_VALUESET_EXPANSION_MESSAGE)
.setValue(new StringType(theMessage));
}
@Override
public void includeConcept(String theSystem, String theCode, String theDisplay) {
incrementConceptsCount();

View File

@ -1,5 +1,7 @@
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.util.TestUtil;
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.StringParam;
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.PractitionerRole;
import org.junit.AfterClass;
import org.junit.Test;
import java.util.Date;
import static org.junit.Assert.assertEquals;
@SuppressWarnings({"unchecked", "Duplicates"})
@ -98,6 +103,40 @@ public class FhirResourceDaoR5SearchNoFtTest extends BaseJpaR5Test {
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
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();

View File

@ -4080,22 +4080,16 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
Patient patient = new Patient();
patient.addName().addGiven("James");
patient.setBirthDateElement(new DateType("2011-02-02"));
patient.addContact().setGender(AdministrativeGender.MALE);
String inputStr = myFhirCtx.newXmlParser().encodeResourceToString(patient);
HttpPost post = new HttpPost(ourServerBase + "/Patient/$validate");
post.setEntity(new StringEntity(inputStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
CloseableHttpResponse response = ourHttpClient.execute(post);
try {
try (CloseableHttpResponse response = ourHttpClient.execute(post)) {
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(resp);
assertEquals(412, response.getStatusLine().getStatusCode());
assertEquals(200, response.getStatusLine().getStatusCode());
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;
protected static Server ourServer;
protected IGenericClient ourClient;
ResourceCountCache ourResourceCountsCache;
ResourceCountCache myResourceCountsCache;
private TerminologyUploaderProvider myTerminologyUploaderProvider;
private boolean ourRestHookSubscriptionInterceptorRequested;
@ -93,6 +93,7 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
myFhirCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
myFhirCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000);
myFhirCtx.setParserErrorHandler(new StrictErrorHandler());
myResourceCountsCache = (ResourceCountCache) myAppCtx.getBean("myResourceCountsCache");
if (ourServer == null) {
ourRestServer = new RestfulServer(myFhirCtx);
@ -113,7 +114,6 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
ourRestServer.setServerConformanceProvider(confProvider);
ourPagingProvider = myAppCtx.getBean(DatabaseBackedPagingProvider.class);
ourResourceCountsCache = (ResourceCountCache) myAppCtx.getBean("myResourceCountsCache");
Server server = new Server(0);

View File

@ -5308,7 +5308,6 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
Patient patient = new Patient();
patient.addName().addGiven("James");
patient.setBirthDateElement(new DateType("2011-02-02"));
patient.addContact().setGender(AdministrativeGender.MALE);
String inputStr = myFhirCtx.newXmlParser().encodeResourceToString(patient);
HttpPost post = new HttpPost(ourServerBase + "/Patient/$validate");
@ -5317,9 +5316,8 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
try (CloseableHttpResponse response = ourHttpClient.execute(post)) {
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(resp);
assertEquals(412, response.getStatusLine().getStatusCode());
assertEquals(200, response.getStatusLine().getStatusCode());
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;
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;
@ -13,6 +16,7 @@ import org.hl7.fhir.r4.model.Extension;
import org.hl7.fhir.r4.model.Patient;
import org.junit.AfterClass;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import java.io.IOException;
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);
@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
@ -71,7 +98,7 @@ public class ServerR4Test extends BaseResourceProviderR4Test {
* Initial fetch after a clear should return
* no results
*/
ourResourceCountsCache.clear();
myResourceCountsCache.clear();
CapabilityStatement capabilityStatement = ourClient
.capabilities()
@ -93,7 +120,7 @@ public class ServerR4Test extends BaseResourceProviderR4Test {
* Now run a background pass (the update
* method is called by the scheduler normally)
*/
ourResourceCountsCache.update();
myResourceCountsCache.update();
capabilityStatement = ourClient
.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 java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
@ -94,6 +96,17 @@ public class ResourceReindexingSvcImplTest extends BaseJpaTest {
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
public void testReindexPassOnlyReturnsValuesAtLowThreshold() {
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.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.term.custom.CustomTerminologySet;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import com.google.common.collect.Lists;
import org.hl7.fhir.instance.model.api.IIdType;
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.codesystems.HttpVerb;
import org.junit.Test;
@ -688,6 +690,21 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test {
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
public void testExpandTermValueSetAndChildrenWithOffsetAndCountWithClientAssignedId() throws Exception {
myDaoConfig.setPreExpandValueSets(true);

View File

@ -24,6 +24,13 @@ import ca.uhn.fhir.rest.api.Constants;
public class JpaConstants {
/**
* Non-instantiable
*/
private JpaConstants() {
// nothing
}
/**
* 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";
/**
* 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
*/

View File

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

View File

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

View File

@ -101,6 +101,31 @@ public class ReadMethodBindingTest {
when(myRequestDetails.getOperation()).thenReturn("$foo");
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 {

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.api.Constants;
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.method.*;
import ca.uhn.fhir.rest.server.method.SearchParameter;
@ -113,7 +116,6 @@ public class ServerCapabilityStatementProvider extends BaseServerCapabilityState
}
private DateTimeType conformanceDate(RequestDetails theRequestDetails) {
IPrimitiveType<Date> buildDate = getServerConfiguration(theRequestDetails).getConformanceDate();
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
* value defaults to "Not provided" but may be set to null, which will cause this element to be omitted.
@ -194,47 +195,46 @@ public class ServerCapabilityStatementProvider extends BaseServerCapabilityState
// Map<String, CapabilityStatement.RestResourceSearchParam> nameToSearchParam = new HashMap<String,
// CapabilityStatement.RestResourceSearchParam>();
for (BaseMethodBinding<?> nextMethodBinding : nextEntry.getValue()) {
if (nextMethodBinding.getRestOperationType() != null) {
String resOpCode = nextMethodBinding.getRestOperationType().getCode();
if (resOpCode != null) {
TypeRestfulInteraction resOp;
try {
resOp = TypeRestfulInteraction.fromCode(resOpCode);
} catch (Exception e) {
resOp = null;
nextMethodBinding.getRestOperationType();
String resOpCode = nextMethodBinding.getRestOperationType().getCode();
if (resOpCode != null) {
TypeRestfulInteraction resOp;
try {
resOp = TypeRestfulInteraction.fromCode(resOpCode);
} catch (Exception e) {
resOp = null;
}
if (resOp != null) {
if (resourceOps.contains(resOp) == false) {
resourceOps.add(resOp);
resource.addInteraction().setCode(resOp);
}
if (resOp != null) {
if ("vread".equals(resOpCode)) {
// vread implies read
resOp = TypeRestfulInteraction.READ;
if (resourceOps.contains(resOp) == false) {
resourceOps.add(resOp);
resource.addInteraction().setCode(resOp);
}
if ("vread".equals(resOpCode)) {
// vread implies read
resOp = TypeRestfulInteraction.READ;
if (resourceOps.contains(resOp) == false) {
resourceOps.add(resOp);
resource.addInteraction().setCode(resOp);
}
}
}
if (nextMethodBinding.isSupportsConditional()) {
switch (resOp) {
case CREATE:
resource.setConditionalCreate(true);
break;
case DELETE:
if (nextMethodBinding.isSupportsConditionalMultiple()) {
resource.setConditionalDelete(ConditionalDeleteStatus.MULTIPLE);
} else {
resource.setConditionalDelete(ConditionalDeleteStatus.SINGLE);
}
break;
case UPDATE:
resource.setConditionalUpdate(true);
break;
default:
break;
}
if (nextMethodBinding.isSupportsConditional()) {
switch (resOp) {
case CREATE:
resource.setConditionalCreate(true);
break;
case DELETE:
if (nextMethodBinding.isSupportsConditionalMultiple()) {
resource.setConditionalDelete(ConditionalDeleteStatus.MULTIPLE);
} else {
resource.setConditionalDelete(ConditionalDeleteStatus.SINGLE);
}
break;
case UPDATE:
resource.setConditionalUpdate(true);
break;
default:
break;
}
}
}
@ -315,28 +315,18 @@ public class ServerCapabilityStatementProvider extends BaseServerCapabilityState
}
sortSearchParameters(searchParameters);
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) {
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 chain = null;
String nextParamUnchainedName = nextParamName;
if (nextParamName.contains(".")) {
chain = nextParamName.substring(nextParamName.indexOf('.') + 1);
nextParamUnchainedName = nextParamName.substring(0, nextParamName.indexOf('.'));
}
@ -352,45 +342,18 @@ public class ServerCapabilityStatementProvider extends BaseServerCapabilityState
}
}
CapabilityStatementRestResourceSearchParamComponent param = resource.addSearchParam();
String typeCode = nextParameter.getParamType().getCode();
param.getTypeElement().setValueAsString(typeCode);
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);
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)
public OperationDefinition readOperationDefinition(@IdParam IdType theId, RequestDetails theRequestDetails) {
if (theId == null || theId.hasIdPart() == false) {

View File

@ -111,7 +111,11 @@ public class ValidatorWrapper {
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) {

View File

@ -799,6 +799,8 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
ourLog.info(output.getMessages().get(0).getMessage());
assertEquals("/f:Patient", output.getMessages().get(0).getLocationString());
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

View File

@ -476,6 +476,10 @@
<action type="fix">
Search parameters of type URI did not work in the hapi-fhir-testpage-overlay. This has been corrected.
</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 version="4.0.3" date="2019-09-03" description="Igloo (Point Release)">
<action type="fix">

View File

@ -262,6 +262,16 @@
<td style="background: #CEC;">4.0.0</td>
<td style="background: #EEB;">4.1.0-e0e3caf9ba</td>
</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>
</table>