Issue 2332 create via put for implementationguide that references some placeholders (#2423)
* begin with failing test * fixed. test passes. * documentation and changelog * added ConceptMap * review feedback
This commit is contained in:
parent
60b462540f
commit
7d769e3b05
|
@ -32,6 +32,7 @@ import org.apache.jena.riot.Lang;
|
|||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
@ -103,7 +104,7 @@ public class FhirContext {
|
|||
private AddProfileTagEnum myAddProfileTagWhenEncoding = AddProfileTagEnum.ONLY_FOR_CUSTOM;
|
||||
private volatile Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> myClassToElementDefinition = Collections.emptyMap();
|
||||
private ArrayList<Class<? extends IBase>> myCustomTypes;
|
||||
private Map<String, Class<? extends IBaseResource>> myDefaultTypeForProfile = new HashMap<>();
|
||||
private final Map<String, Class<? extends IBaseResource>> myDefaultTypeForProfile = new HashMap<>();
|
||||
private volatile Map<String, RuntimeResourceDefinition> myIdToResourceDefinition = Collections.emptyMap();
|
||||
private volatile boolean myInitialized;
|
||||
private volatile boolean myInitializing = false;
|
||||
|
@ -114,8 +115,8 @@ public class FhirContext {
|
|||
private volatile INarrativeGenerator myNarrativeGenerator;
|
||||
private volatile IParserErrorHandler myParserErrorHandler = new LenientErrorHandler();
|
||||
private ParserOptions myParserOptions = new ParserOptions();
|
||||
private Set<PerformanceOptionsEnum> myPerformanceOptions = new HashSet<>();
|
||||
private Collection<Class<? extends IBaseResource>> myResourceTypesToScan;
|
||||
private final Set<PerformanceOptionsEnum> myPerformanceOptions = new HashSet<>();
|
||||
private final Collection<Class<? extends IBaseResource>> myResourceTypesToScan;
|
||||
private volatile IRestfulClientFactory myRestfulClientFactory;
|
||||
private volatile RuntimeChildUndeclaredExtensionDefinition myRuntimeChildUndeclaredExtensionDefinition;
|
||||
private IValidationSupport myValidationSupport;
|
||||
|
@ -1066,4 +1067,10 @@ public class FhirContext {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
// TODO KHS add the other primitive types
|
||||
public IPrimitiveType<Boolean> getPrimitiveBoolean(Boolean theValue) {
|
||||
IPrimitiveType<Boolean> retval = (IPrimitiveType<Boolean>) getElementDefinition("boolean").newInstance();
|
||||
retval.setValue(theValue);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -101,6 +101,17 @@ public class HapiExtensions {
|
|||
public static final String EXT_META_SOURCE = "http://hapifhir.io/fhir/StructureDefinition/resource-meta-source";
|
||||
public static final String EXT_SP_UNIQUE = "http://hapifhir.io/fhir/StructureDefinition/sp-unique";
|
||||
|
||||
/**
|
||||
* URL for extension on a Phonetic String SearchParameter indicating that text values should be phonetically indexed with the named encoder
|
||||
*/
|
||||
public static final String EXT_SEARCHPARAM_PHONETIC_ENCODER = "http://hapifhir.io/fhir/StructureDefinition/searchparameter-phonetic-encoder";
|
||||
|
||||
/**
|
||||
* URL for boolean extension added to all placeholder resources
|
||||
*/
|
||||
public static final String EXT_RESOURCE_META_PLACEHOLDER = "http://hapifhir.io/fhir/StructureDefinition/resource-meta-placeholder";
|
||||
|
||||
|
||||
/**
|
||||
* Non instantiable
|
||||
*/
|
||||
|
|
|
@ -80,10 +80,7 @@ public class ParametersUtil {
|
|||
.filter(t -> t instanceof IPrimitiveType<?>)
|
||||
.map(t -> ((IPrimitiveType<?>) t))
|
||||
.findFirst();
|
||||
if (!nameValue.isPresent() || !theParameterName.equals(nameValue.get().getValueAsString())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return nameValue.isPresent() && theParameterName.equals(nameValue.get().getValueAsString());
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
|
@ -227,9 +224,7 @@ public class ParametersUtil {
|
|||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static void addParameterToParametersBoolean(FhirContext theCtx, IBaseParameters theParameters, String theName, boolean theValue) {
|
||||
IPrimitiveType<Boolean> value = (IPrimitiveType<Boolean>) theCtx.getElementDefinition("boolean").newInstance();
|
||||
value.setValue(theValue);
|
||||
addParameterToParameters(theCtx, theParameters, theName, value);
|
||||
addParameterToParameters(theCtx, theParameters, theName, theCtx.getPrimitiveBoolean(theValue));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
@ -310,10 +305,7 @@ public class ParametersUtil {
|
|||
}
|
||||
|
||||
public static void addPartBoolean(FhirContext theContext, IBase theParameter, String theName, Boolean theValue) {
|
||||
IPrimitiveType<Boolean> value = (IPrimitiveType<Boolean>) theContext.getElementDefinition("boolean").newInstance();
|
||||
value.setValue(theValue);
|
||||
|
||||
addPart(theContext, theParameter, theName, value);
|
||||
addPart(theContext, theParameter, theName, theContext.getPrimitiveBoolean(theValue));
|
||||
}
|
||||
|
||||
public static void addPartDecimal(FhirContext theContext, IBase theParameter, String theName, Double theValue) {
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
type: add
|
||||
issue: 2332
|
||||
title: "All created placeholder resources now have a meta extension with the url http://hapifhir.io/fhir/StructureDefinition/resource-meta-placeholder
|
||||
and the value 'true'. Also, terminology storage is now skipped for placeholder ValueSet and ConceptMap resources."
|
|
@ -88,7 +88,7 @@ public class DaoConfig {
|
|||
* Child Configurations
|
||||
*/
|
||||
|
||||
private ModelConfig myModelConfig = new ModelConfig();
|
||||
private final ModelConfig myModelConfig = new ModelConfig();
|
||||
|
||||
/**
|
||||
* update setter javadoc if default changes
|
||||
|
@ -171,7 +171,7 @@ public class DaoConfig {
|
|||
*
|
||||
* @since 4.1.0
|
||||
*/
|
||||
private int myPreExpandValueSetsDefaultOffset = 0;
|
||||
private final int myPreExpandValueSetsDefaultOffset = 0;
|
||||
/**
|
||||
* Do not change default of {@code 1000}!
|
||||
*
|
||||
|
@ -1067,6 +1067,9 @@ public class DaoConfig {
|
|||
* This property can be useful in cases where replication between two servers is wanted.
|
||||
* Note however that references containing purely numeric IDs will not be auto-created
|
||||
* as they are never allowed to be client supplied in HAPI FHIR JPA.
|
||||
*
|
||||
* All placeholder resources created in this way have a meta extension
|
||||
* with the URL {@link HapiExtensions#EXT_RESOURCE_META_PLACEHOLDER} and the value "true".
|
||||
* </p>
|
||||
*/
|
||||
public boolean isAutoCreatePlaceholderReferenceTargets() {
|
||||
|
@ -1088,6 +1091,9 @@ public class DaoConfig {
|
|||
* This property can be useful in cases where replication between two servers is wanted.
|
||||
* Note however that references containing purely numeric IDs will not be auto-created
|
||||
* as they are never allowed to be client supplied in HAPI FHIR JPA.
|
||||
*
|
||||
* All placeholder resources created in this way have a meta extension
|
||||
* with the URL {@link HapiExtensions#EXT_RESOURCE_META_PLACEHOLDER} and the value "true".
|
||||
* </p>
|
||||
*/
|
||||
public void setAutoCreatePlaceholderReferenceTargets(boolean theAutoCreatePlaceholderReferenceTargets) {
|
||||
|
|
|
@ -37,7 +37,11 @@ import ca.uhn.fhir.rest.api.server.RequestDetails;
|
|||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||
import ca.uhn.fhir.util.HapiExtensions;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
import org.hl7.fhir.instance.model.api.IBaseExtension;
|
||||
import org.hl7.fhir.instance.model.api.IBaseHasExtensions;
|
||||
import org.hl7.fhir.instance.model.api.IBaseMetaType;
|
||||
import org.hl7.fhir.instance.model.api.IBaseReference;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
@ -119,6 +123,13 @@ public class DaoResourceLinkResolver implements IResourceLinkResolver {
|
|||
@SuppressWarnings("unchecked")
|
||||
T newResource = (T) missingResourceDef.newInstance();
|
||||
|
||||
IBaseMetaType meta = newResource.getMeta();
|
||||
if (meta instanceof IBaseHasExtensions) {
|
||||
IBaseExtension<?, ?> extension = ((IBaseHasExtensions) meta).addExtension();
|
||||
extension.setUrl(HapiExtensions.EXT_RESOURCE_META_PLACEHOLDER);
|
||||
extension.setValue(myContext.getPrimitiveBoolean(true));
|
||||
}
|
||||
|
||||
IFhirResourceDao<T> placeholderResourceDao = myDaoRegistry.getResourceDao(theType);
|
||||
ourLog.debug("Automatically creating empty placeholder resource: {}", newResource.getIdElement().getValue());
|
||||
|
||||
|
|
|
@ -127,6 +127,7 @@ import org.hl7.fhir.r4.model.ConceptMap;
|
|||
import org.hl7.fhir.r4.model.Enumerations;
|
||||
import org.hl7.fhir.r4.model.IdType;
|
||||
import org.hl7.fhir.r4.model.IntegerType;
|
||||
import org.hl7.fhir.r4.model.MetadataResource;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
import org.hl7.fhir.r4.model.ValueSet;
|
||||
import org.hl7.fhir.r4.model.codesystems.ConceptSubsumptionOutcome;
|
||||
|
@ -656,9 +657,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
return true;
|
||||
|
||||
//-- token case
|
||||
if (startsWithByWordBoundaries(theDisplay, theFilterDisplay)) return true;
|
||||
|
||||
return false;
|
||||
return startsWithByWordBoundaries(theDisplay, theFilterDisplay);
|
||||
}
|
||||
|
||||
private boolean startsWithByWordBoundaries(String theDisplay, String theFilterDisplay) {
|
||||
|
@ -1770,10 +1769,15 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
@Override
|
||||
@Transactional
|
||||
public void storeTermConceptMapAndChildren(ResourceTable theResourceTable, ConceptMap theConceptMap) {
|
||||
ourLog.debug("Storing TermConceptMap for {}", theConceptMap.getIdElement().toVersionless().getValueAsString());
|
||||
|
||||
ValidateUtil.isTrueOrThrowInvalidRequest(theResourceTable != null, "No resource supplied");
|
||||
if (isPlaceholder(theConceptMap)) {
|
||||
ourLog.info("Not storing TermConceptMap for placeholder {}", theConceptMap.getIdElement().toVersionless().getValueAsString());
|
||||
return;
|
||||
}
|
||||
|
||||
ValidateUtil.isNotBlankOrThrowUnprocessableEntity(theConceptMap.getUrl(), "ConceptMap has no value for ConceptMap.url");
|
||||
ourLog.info("Storing TermConceptMap for {}", theConceptMap.getIdElement().toVersionless().getValueAsString());
|
||||
|
||||
TermConceptMap termConceptMap = new TermConceptMap();
|
||||
termConceptMap.setResource(theResourceTable);
|
||||
|
@ -2063,10 +2067,15 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
@Override
|
||||
@Transactional
|
||||
public void storeTermValueSet(ResourceTable theResourceTable, ValueSet theValueSet) {
|
||||
ourLog.info("Storing TermValueSet for {}", theValueSet.getIdElement().toVersionless().getValueAsString());
|
||||
|
||||
ValidateUtil.isTrueOrThrowInvalidRequest(theResourceTable != null, "No resource supplied");
|
||||
if (isPlaceholder(theValueSet)) {
|
||||
ourLog.info("Not storing TermValueSet for placeholder {}", theValueSet.getIdElement().toVersionless().getValueAsString());
|
||||
return;
|
||||
}
|
||||
|
||||
ValidateUtil.isNotBlankOrThrowUnprocessableEntity(theValueSet.getUrl(), "ValueSet has no value for ValueSet.url");
|
||||
ourLog.info("Storing TermValueSet for {}", theValueSet.getIdElement().toVersionless().getValueAsString());
|
||||
|
||||
/*
|
||||
* Get CodeSystem and validate CodeSystemVersion
|
||||
|
@ -2113,6 +2122,9 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
}
|
||||
}
|
||||
|
||||
private boolean isPlaceholder(MetadataResource theResource) {
|
||||
return theResource.getMeta().getExtensionByUrl(HapiExtensions.EXT_RESOURCE_META_PLACEHOLDER) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
|
|
|
@ -4,12 +4,11 @@ import ca.uhn.fhir.context.phonetic.ApacheEncoder;
|
|||
import ca.uhn.fhir.context.phonetic.PhoneticEncoderEnum;
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
|
||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
import ca.uhn.fhir.util.HapiExtensions;
|
||||
import org.apache.commons.codec.language.Soundex;
|
||||
import org.aspectj.lang.annotation.Before;
|
||||
import org.hl7.fhir.dstu3.model.Enumerations;
|
||||
import org.hl7.fhir.dstu3.model.Patient;
|
||||
import org.hl7.fhir.dstu3.model.SearchParameter;
|
||||
|
@ -126,7 +125,7 @@ public class FhirResourceDaoDstu3PhoneticSearchNoFtTest extends BaseJpaDstu3Test
|
|||
// searchParameter.setXpathUsage(SearchParameter.XPathUsageType.PHONETIC);
|
||||
searchParameter.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
searchParameter.addExtension()
|
||||
.setUrl(JpaConstants.EXT_SEARCHPARAM_PHONETIC_ENCODER)
|
||||
.setUrl(HapiExtensions.EXT_SEARCHPARAM_PHONETIC_ENCODER)
|
||||
.setValue(new StringType(theEncoder.name()));
|
||||
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(searchParameter));
|
||||
mySearchParameterDao.create(searchParameter, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
|
|
@ -5,6 +5,7 @@ import ca.uhn.fhir.context.FhirVersionEnum;
|
|||
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
|
||||
import ca.uhn.fhir.interceptor.api.IInterceptorService;
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.INpmPackageDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.INpmPackageVersionDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.INpmPackageVersionResourceDao;
|
||||
|
@ -19,7 +20,6 @@ import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
|||
import ca.uhn.fhir.rest.param.ReferenceParam;
|
||||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
import ca.uhn.fhir.rest.param.UriParam;
|
||||
import ca.uhn.fhir.rest.server.IRestfulServerDefaults;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.partition.RequestTenantPartitionInterceptor;
|
||||
|
@ -48,6 +48,7 @@ import org.junit.jupiter.api.Test;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
|
||||
|
@ -94,6 +95,9 @@ public class NpmR4Test extends BaseJpaR4Test {
|
|||
private IInterceptorService myInterceptorService;
|
||||
@Autowired
|
||||
private RequestTenantPartitionInterceptor myRequestTenantPartitionInterceptor;
|
||||
@Autowired
|
||||
@Qualifier("myImplementationGuideDaoR4")
|
||||
protected IFhirResourceDao<ImplementationGuide> myImplementationGuideDao;
|
||||
|
||||
@BeforeEach
|
||||
public void before() throws Exception {
|
||||
|
@ -118,6 +122,7 @@ public class NpmR4Test extends BaseJpaR4Test {
|
|||
public void after() throws Exception {
|
||||
JettyUtil.closeServer(myServer);
|
||||
myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences());
|
||||
myDaoConfig.setAutoCreatePlaceholderReferenceTargets(new DaoConfig().isAutoCreatePlaceholderReferenceTargets());
|
||||
myPartitionSettings.setPartitioningEnabled(false);
|
||||
myInterceptorService.unregisterInterceptor(myRequestTenantPartitionInterceptor);
|
||||
}
|
||||
|
@ -352,6 +357,34 @@ public class NpmR4Test extends BaseJpaR4Test {
|
|||
}
|
||||
}
|
||||
|
||||
// Reproduces https://github.com/hapifhir/hapi-fhir/issues/2332
|
||||
@Test
|
||||
public void testInstallR4Package_AutoCreatePlaceholder() throws Exception {
|
||||
myDaoConfig.setAllowExternalReferences(true);
|
||||
myDaoConfig.setAutoCreatePlaceholderReferenceTargets(true);
|
||||
|
||||
byte[] bytes = loadClasspathBytes("/packages/test-auto-create-placeholder.tgz");
|
||||
myFakeNpmServlet.myResponses.put("/test-ig/1.0.0", bytes);
|
||||
|
||||
List<String> resourceList = new ArrayList<>();
|
||||
resourceList.add("ImplementationGuide");
|
||||
PackageInstallationSpec spec = new PackageInstallationSpec().setName("test-ig").setVersion("1.0.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL);
|
||||
spec.setInstallResourceTypes(resourceList);
|
||||
PackageInstallOutcomeJson outcome = igInstaller.install(spec);
|
||||
assertEquals(1, outcome.getResourcesInstalled().get("ImplementationGuide"));
|
||||
|
||||
// Be sure no further communication with the server
|
||||
JettyUtil.closeServer(myServer);
|
||||
|
||||
// Search for the installed resources
|
||||
runInTransaction(() -> {
|
||||
SearchParameterMap map = SearchParameterMap.newSynchronous();
|
||||
IBundleProvider result = myImplementationGuideDao.search(map);
|
||||
assertEquals(1, result.sizeOrThrowNpe());
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInstallR4Package_DraftResourcesNotInstalled() throws Exception {
|
||||
myDaoConfig.setAllowExternalReferences(true);
|
||||
|
|
Binary file not shown.
|
@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.model.util;
|
|||
*/
|
||||
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.util.HapiExtensions;
|
||||
|
||||
public class JpaConstants {
|
||||
|
||||
|
@ -195,9 +196,11 @@ public class JpaConstants {
|
|||
public static final String EXTENSION_EXT_SYSTEMDEFINED = JpaConstants.class.getName() + "_EXTENSION_EXT_SYSTEMDEFINED";
|
||||
|
||||
/**
|
||||
* URL for extension on a Phonetic String SearchParameter indicating that text values should be phonetically indexed with the named encoder
|
||||
* Deprecated. Please use {@link HapiExtensions#EXT_SEARCHPARAM_PHONETIC_ENCODER} instead.
|
||||
*/
|
||||
public static final String EXT_SEARCHPARAM_PHONETIC_ENCODER = "http://hapifhir.io/fhir/StructureDefinition/searchparameter-phonetic-encoder";
|
||||
@Deprecated
|
||||
public static final String EXT_SEARCHPARAM_PHONETIC_ENCODER = HapiExtensions.EXT_SEARCHPARAM_PHONETIC_ENCODER;
|
||||
|
||||
public static final String VALUESET_FILTER_DISPLAY = "display";
|
||||
|
||||
/**
|
||||
|
|
|
@ -23,7 +23,6 @@ package ca.uhn.fhir.jpa.searchparam.registry;
|
|||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.context.phonetic.PhoneticEncoderEnum;
|
||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||
import ca.uhn.fhir.jpa.searchparam.JpaRuntimeSearchParam;
|
||||
import ca.uhn.fhir.model.api.ExtensionDt;
|
||||
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
||||
|
@ -431,7 +430,7 @@ public class SearchParameterCanonicalizer {
|
|||
String nextUrl = next.getUrl();
|
||||
if (isNotBlank(nextUrl)) {
|
||||
theRuntimeSearchParam.addExtension(nextUrl, next);
|
||||
if (JpaConstants.EXT_SEARCHPARAM_PHONETIC_ENCODER.equals(nextUrl)) {
|
||||
if (HapiExtensions.EXT_SEARCHPARAM_PHONETIC_ENCODER.equals(nextUrl)) {
|
||||
setEncoder(theRuntimeSearchParam, next.getValue());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue