Merge pull request #2271 from hapifhir/im_2021_01_04_package_load_with_partitioning
Fixed package loading when partitioning is enabled
This commit is contained in:
commit
4b310f0a3c
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
type: fix
|
||||||
|
issue: 2271
|
||||||
|
title: "Attempts to load IG packs when partitioning was enabled, resulted in nullpointer exceptions.
|
||||||
|
This has now been fixed and IG packs and conformance resources will be loaded to the DEFAULT partition."
|
|
@ -139,3 +139,5 @@ None of the limitations listed here are considered permanent. Over time the HAPI
|
||||||
* **Cross-partition History Operations are not supported**: It is not possible to perform a `_history` operation that spans all partitions (`_history` does work when applied to a single partition however).
|
* **Cross-partition History Operations are not supported**: It is not possible to perform a `_history` operation that spans all partitions (`_history` does work when applied to a single partition however).
|
||||||
|
|
||||||
* **Bulk Operations are not partition aware**: Bulk export operations will export data across all partitions.
|
* **Bulk Operations are not partition aware**: Bulk export operations will export data across all partitions.
|
||||||
|
|
||||||
|
* **Package Operations are not partition aware**: Package operations will only create, update and query resources in the default partition.
|
||||||
|
|
|
@ -29,10 +29,12 @@ import ca.uhn.fhir.jpa.api.model.ExpungeOptions;
|
||||||
import ca.uhn.fhir.jpa.dao.data.INpmPackageDao;
|
import ca.uhn.fhir.jpa.dao.data.INpmPackageDao;
|
||||||
import ca.uhn.fhir.jpa.dao.data.INpmPackageVersionDao;
|
import ca.uhn.fhir.jpa.dao.data.INpmPackageVersionDao;
|
||||||
import ca.uhn.fhir.jpa.dao.data.INpmPackageVersionResourceDao;
|
import ca.uhn.fhir.jpa.dao.data.INpmPackageVersionResourceDao;
|
||||||
|
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||||
import ca.uhn.fhir.jpa.model.entity.NpmPackageEntity;
|
import ca.uhn.fhir.jpa.model.entity.NpmPackageEntity;
|
||||||
import ca.uhn.fhir.jpa.model.entity.NpmPackageVersionEntity;
|
import ca.uhn.fhir.jpa.model.entity.NpmPackageVersionEntity;
|
||||||
import ca.uhn.fhir.jpa.model.entity.NpmPackageVersionResourceEntity;
|
import ca.uhn.fhir.jpa.model.entity.NpmPackageVersionResourceEntity;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||||
|
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
|
||||||
import ca.uhn.fhir.rest.api.Constants;
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||||
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
|
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
|
||||||
|
@ -54,6 +56,7 @@ import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
|
||||||
import org.hl7.fhir.exceptions.FHIRException;
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseBinary;
|
import org.hl7.fhir.instance.model.api.IBaseBinary;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
import org.hl7.fhir.utilities.npm.BasePackageCacheManager;
|
import org.hl7.fhir.utilities.npm.BasePackageCacheManager;
|
||||||
import org.hl7.fhir.utilities.npm.NpmPackage;
|
import org.hl7.fhir.utilities.npm.NpmPackage;
|
||||||
|
@ -115,6 +118,8 @@ public class JpaPackageCache extends BasePackageCacheManager implements IHapiPac
|
||||||
private FhirContext myCtx;
|
private FhirContext myCtx;
|
||||||
@Autowired
|
@Autowired
|
||||||
private PlatformTransactionManager myTxManager;
|
private PlatformTransactionManager myTxManager;
|
||||||
|
@Autowired
|
||||||
|
private PartitionSettings myPartitionSettings;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NpmPackage loadPackageFromCacheOnly(String theId, @Nullable String theVersion) {
|
public NpmPackage loadPackageFromCacheOnly(String theId, @Nullable String theVersion) {
|
||||||
|
@ -205,7 +210,7 @@ public class JpaPackageCache extends BasePackageCacheManager implements IHapiPac
|
||||||
|
|
||||||
return newTxTemplate().execute(tx -> {
|
return newTxTemplate().execute(tx -> {
|
||||||
|
|
||||||
ResourceTable persistedPackage = (ResourceTable) getBinaryDao().create(binary).getEntity();
|
ResourceTable persistedPackage = createResourceBinary(binary);
|
||||||
NpmPackageEntity pkg = myPackageDao.findByPackageId(thePackageId).orElseGet(() -> createPackage(npmPackage));
|
NpmPackageEntity pkg = myPackageDao.findByPackageId(thePackageId).orElseGet(() -> createPackage(npmPackage));
|
||||||
NpmPackageVersionEntity packageVersion = myPackageVersionDao.findByPackageIdAndVersion(thePackageId, packageVersionId).orElse(null);
|
NpmPackageVersionEntity packageVersion = myPackageVersionDao.findByPackageIdAndVersion(thePackageId, packageVersionId).orElse(null);
|
||||||
if (packageVersion != null) {
|
if (packageVersion != null) {
|
||||||
|
@ -282,7 +287,7 @@ public class JpaPackageCache extends BasePackageCacheManager implements IHapiPac
|
||||||
byte[] minimizedContents = packageContext.newJsonParser().encodeResourceToString(resource).getBytes(StandardCharsets.UTF_8);
|
byte[] minimizedContents = packageContext.newJsonParser().encodeResourceToString(resource).getBytes(StandardCharsets.UTF_8);
|
||||||
|
|
||||||
IBaseBinary resourceBinary = createPackageResourceBinary(nextFile, minimizedContents, contentType);
|
IBaseBinary resourceBinary = createPackageResourceBinary(nextFile, minimizedContents, contentType);
|
||||||
ResourceTable persistedResource = (ResourceTable) getBinaryDao().create(resourceBinary).getEntity();
|
ResourceTable persistedResource = createResourceBinary(resourceBinary);
|
||||||
|
|
||||||
NpmPackageVersionResourceEntity resourceEntity = new NpmPackageVersionResourceEntity();
|
NpmPackageVersionResourceEntity resourceEntity = new NpmPackageVersionResourceEntity();
|
||||||
resourceEntity.setPackageVersion(packageVersion);
|
resourceEntity.setPackageVersion(packageVersion);
|
||||||
|
@ -319,6 +324,16 @@ public class JpaPackageCache extends BasePackageCacheManager implements IHapiPac
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ResourceTable createResourceBinary(IBaseBinary theResourceBinary) {
|
||||||
|
|
||||||
|
if (myPartitionSettings.isPartitioningEnabled()) {
|
||||||
|
SystemRequestDetails myRequestDetails = new SystemRequestDetails();
|
||||||
|
return (ResourceTable) getBinaryDao().create(theResourceBinary, myRequestDetails).getEntity();
|
||||||
|
} else {
|
||||||
|
return (ResourceTable) getBinaryDao().create(theResourceBinary).getEntity();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private boolean updateCurrentVersionFlagForAllPackagesBasedOnNewIncomingVersion(String thePackageId, String thePackageVersion) {
|
private boolean updateCurrentVersionFlagForAllPackagesBasedOnNewIncomingVersion(String thePackageId, String thePackageVersion) {
|
||||||
Collection<NpmPackageVersionEntity> existingVersions = myPackageVersionDao.findByPackageId(thePackageId);
|
Collection<NpmPackageVersionEntity> existingVersions = myPackageVersionDao.findByPackageId(thePackageId);
|
||||||
boolean retVal = true;
|
boolean retVal = true;
|
||||||
|
@ -578,16 +593,14 @@ public class JpaPackageCache extends BasePackageCacheManager implements IHapiPac
|
||||||
|
|
||||||
ExpungeOptions options = new ExpungeOptions();
|
ExpungeOptions options = new ExpungeOptions();
|
||||||
options.setExpungeDeletedResources(true).setExpungeOldVersions(true);
|
options.setExpungeDeletedResources(true).setExpungeOldVersions(true);
|
||||||
getBinaryDao().delete(next.getResourceBinary().getIdDt().toVersionless());
|
deleteAndExpungeResourceBinary(next.getResourceBinary().getIdDt().toVersionless(), options);
|
||||||
getBinaryDao().forceExpungeInExistingTransaction(next.getResourceBinary().getIdDt().toVersionless(), options, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
myPackageVersionDao.delete(packageVersion.get());
|
myPackageVersionDao.delete(packageVersion.get());
|
||||||
|
|
||||||
ExpungeOptions options = new ExpungeOptions();
|
ExpungeOptions options = new ExpungeOptions();
|
||||||
options.setExpungeDeletedResources(true).setExpungeOldVersions(true);
|
options.setExpungeDeletedResources(true).setExpungeOldVersions(true);
|
||||||
getBinaryDao().delete(packageVersion.get().getPackageBinary().getIdDt().toVersionless());
|
deleteAndExpungeResourceBinary(packageVersion.get().getPackageBinary().getIdDt().toVersionless(), options);
|
||||||
getBinaryDao().forceExpungeInExistingTransaction(packageVersion.get().getPackageBinary().getIdDt().toVersionless(), options, null);
|
|
||||||
|
|
||||||
Collection<NpmPackageVersionEntity> remainingVersions = myPackageVersionDao.findByPackageId(thePackageId);
|
Collection<NpmPackageVersionEntity> remainingVersions = myPackageVersionDao.findByPackageId(thePackageId);
|
||||||
if (remainingVersions.size() == 0) {
|
if (remainingVersions.size() == 0) {
|
||||||
|
@ -622,6 +635,19 @@ public class JpaPackageCache extends BasePackageCacheManager implements IHapiPac
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void deleteAndExpungeResourceBinary(IIdType theResourceBinaryId, ExpungeOptions theOptions) {
|
||||||
|
|
||||||
|
if (myPartitionSettings.isPartitioningEnabled()) {
|
||||||
|
SystemRequestDetails myRequestDetails = new SystemRequestDetails();
|
||||||
|
getBinaryDao().delete(theResourceBinaryId, myRequestDetails).getEntity();
|
||||||
|
getBinaryDao().forceExpungeInExistingTransaction(theResourceBinaryId, theOptions, myRequestDetails);
|
||||||
|
} else {
|
||||||
|
getBinaryDao().delete(theResourceBinaryId).getEntity();
|
||||||
|
getBinaryDao().forceExpungeInExistingTransaction(theResourceBinaryId, theOptions, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public List<Predicate> createSearchPredicates(PackageSearchSpec thePackageSearchSpec, CriteriaBuilder theCb, Root<NpmPackageVersionEntity> theRoot) {
|
public List<Predicate> createSearchPredicates(PackageSearchSpec thePackageSearchSpec, CriteriaBuilder theCb, Root<NpmPackageVersionEntity> theRoot) {
|
||||||
List<Predicate> predicates = new ArrayList<>();
|
List<Predicate> predicates = new ArrayList<>();
|
||||||
|
|
|
@ -31,7 +31,9 @@ import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||||
import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome;
|
import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome;
|
||||||
import ca.uhn.fhir.jpa.dao.data.INpmPackageVersionDao;
|
import ca.uhn.fhir.jpa.dao.data.INpmPackageVersionDao;
|
||||||
|
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||||
import ca.uhn.fhir.jpa.model.entity.NpmPackageVersionEntity;
|
import ca.uhn.fhir.jpa.model.entity.NpmPackageVersionEntity;
|
||||||
|
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
|
@ -50,12 +52,12 @@ import org.hl7.fhir.instance.model.api.IBase;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
import org.hl7.fhir.r4.model.Identifier;
|
import org.hl7.fhir.r4.model.Identifier;
|
||||||
|
import org.hl7.fhir.utilities.npm.IPackageCacheManager;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.transaction.PlatformTransactionManager;
|
import org.springframework.transaction.PlatformTransactionManager;
|
||||||
import org.springframework.transaction.support.TransactionTemplate;
|
import org.springframework.transaction.support.TransactionTemplate;
|
||||||
import org.hl7.fhir.utilities.npm.BasePackageCacheManager;
|
|
||||||
import org.hl7.fhir.utilities.npm.NpmPackage;
|
import org.hl7.fhir.utilities.npm.NpmPackage;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
|
@ -102,7 +104,8 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc {
|
||||||
private INpmPackageVersionDao myPackageVersionDao;
|
private INpmPackageVersionDao myPackageVersionDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISearchParamRegistry mySearchParamRegistry;
|
private ISearchParamRegistry mySearchParamRegistry;
|
||||||
|
@Autowired
|
||||||
|
private PartitionSettings myPartitionSettings;
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*/
|
*/
|
||||||
|
@ -316,19 +319,19 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc {
|
||||||
private void create(IBaseResource theResource, PackageInstallOutcomeJson theOutcome) {
|
private void create(IBaseResource theResource, PackageInstallOutcomeJson theOutcome) {
|
||||||
IFhirResourceDao dao = myDaoRegistry.getResourceDao(theResource.getClass());
|
IFhirResourceDao dao = myDaoRegistry.getResourceDao(theResource.getClass());
|
||||||
SearchParameterMap map = createSearchParameterMapFor(theResource);
|
SearchParameterMap map = createSearchParameterMapFor(theResource);
|
||||||
IBundleProvider searchResult = dao.search(map);
|
IBundleProvider searchResult = searchResource(dao, map);
|
||||||
if (validForUpload(theResource)) {
|
if (validForUpload(theResource)) {
|
||||||
if (searchResult.isEmpty()) {
|
if (searchResult.isEmpty()) {
|
||||||
|
|
||||||
ourLog.info("Creating new resource matching {}", map.toNormalizedQueryString(myFhirContext));
|
ourLog.info("Creating new resource matching {}", map.toNormalizedQueryString(myFhirContext));
|
||||||
theOutcome.incrementResourcesInstalled(myFhirContext.getResourceType(theResource));
|
theOutcome.incrementResourcesInstalled(myFhirContext.getResourceType(theResource));
|
||||||
dao.create(theResource);
|
createResource(dao, theResource);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
ourLog.info("Updating existing resource matching {}", map.toNormalizedQueryString(myFhirContext));
|
ourLog.info("Updating existing resource matching {}", map.toNormalizedQueryString(myFhirContext));
|
||||||
theResource.setId(searchResult.getResources(0, 1).get(0).getIdElement().toUnqualifiedVersionless());
|
theResource.setId(searchResult.getResources(0, 1).get(0).getIdElement().toUnqualifiedVersionless());
|
||||||
DaoMethodOutcome outcome = dao.update(theResource);
|
DaoMethodOutcome outcome = updateResource(dao, theResource);
|
||||||
if (!outcome.isNop()) {
|
if (!outcome.isNop()) {
|
||||||
theOutcome.incrementResourcesInstalled(myFhirContext.getResourceType(theResource));
|
theOutcome.incrementResourcesInstalled(myFhirContext.getResourceType(theResource));
|
||||||
}
|
}
|
||||||
|
@ -337,6 +340,33 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IBundleProvider searchResource(IFhirResourceDao theDao, SearchParameterMap theMap) {
|
||||||
|
if (myPartitionSettings.isPartitioningEnabled()) {
|
||||||
|
SystemRequestDetails myRequestDetails = new SystemRequestDetails();
|
||||||
|
return theDao.search(theMap, myRequestDetails);
|
||||||
|
} else {
|
||||||
|
return theDao.search(theMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createResource(IFhirResourceDao theDao, IBaseResource theResource) {
|
||||||
|
if (myPartitionSettings.isPartitioningEnabled()) {
|
||||||
|
SystemRequestDetails myRequestDetails = new SystemRequestDetails();
|
||||||
|
theDao.create(theResource, myRequestDetails);
|
||||||
|
} else {
|
||||||
|
theDao.create(theResource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private DaoMethodOutcome updateResource(IFhirResourceDao theDao, IBaseResource theResource) {
|
||||||
|
if (myPartitionSettings.isPartitioningEnabled()) {
|
||||||
|
SystemRequestDetails myRequestDetails = new SystemRequestDetails();
|
||||||
|
return theDao.update(theResource, myRequestDetails);
|
||||||
|
} else {
|
||||||
|
return theDao.update(theResource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
boolean validForUpload(IBaseResource theResource) {
|
boolean validForUpload(IBaseResource theResource) {
|
||||||
String resourceType = myFhirContext.getResourceType(theResource);
|
String resourceType = myFhirContext.getResourceType(theResource);
|
||||||
if ("SearchParameter".equals(resourceType)) {
|
if ("SearchParameter".equals(resourceType)) {
|
||||||
|
|
|
@ -68,14 +68,20 @@ public class RequestPartitionHelperSvc implements IRequestPartitionHelperSvc {
|
||||||
myPartitioningBlacklist.add("Subscription");
|
myPartitioningBlacklist.add("Subscription");
|
||||||
myPartitioningBlacklist.add("SearchParameter");
|
myPartitioningBlacklist.add("SearchParameter");
|
||||||
|
|
||||||
// Validation
|
// Validation and Conformance
|
||||||
myPartitioningBlacklist.add("StructureDefinition");
|
myPartitioningBlacklist.add("StructureDefinition");
|
||||||
myPartitioningBlacklist.add("Questionnaire");
|
myPartitioningBlacklist.add("Questionnaire");
|
||||||
|
myPartitioningBlacklist.add("CapabilityStatement");
|
||||||
|
myPartitioningBlacklist.add("CompartmentDefinition");
|
||||||
|
myPartitioningBlacklist.add("OperationDefinition");
|
||||||
|
|
||||||
// Terminology
|
// Terminology
|
||||||
myPartitioningBlacklist.add("ConceptMap");
|
myPartitioningBlacklist.add("ConceptMap");
|
||||||
myPartitioningBlacklist.add("CodeSystem");
|
myPartitioningBlacklist.add("CodeSystem");
|
||||||
myPartitioningBlacklist.add("ValueSet");
|
myPartitioningBlacklist.add("ValueSet");
|
||||||
|
myPartitioningBlacklist.add("NamingSystem");
|
||||||
|
myPartitioningBlacklist.add("StructureMap");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -91,7 +97,7 @@ public class RequestPartitionHelperSvc implements IRequestPartitionHelperSvc {
|
||||||
|
|
||||||
if (myPartitionSettings.isPartitioningEnabled()) {
|
if (myPartitionSettings.isPartitioningEnabled()) {
|
||||||
// Handle system requests
|
// Handle system requests
|
||||||
if (theRequest == null && myPartitioningBlacklist.contains(theResourceType)) {
|
if ((theRequest == null && myPartitioningBlacklist.contains(theResourceType)) || theRequest instanceof SystemRequestDetails) {
|
||||||
return RequestPartitionId.defaultPartition();
|
return RequestPartitionId.defaultPartition();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,7 +129,7 @@ public class RequestPartitionHelperSvc implements IRequestPartitionHelperSvc {
|
||||||
|
|
||||||
if (myPartitionSettings.isPartitioningEnabled()) {
|
if (myPartitionSettings.isPartitioningEnabled()) {
|
||||||
// Handle system requests
|
// Handle system requests
|
||||||
if (theRequest == null && myPartitioningBlacklist.contains(theResourceType)) {
|
if ((theRequest == null && myPartitioningBlacklist.contains(theResourceType)) || theRequest instanceof SystemRequestDetails) {
|
||||||
return RequestPartitionId.defaultPartition();
|
return RequestPartitionId.defaultPartition();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,159 @@
|
||||||
|
package ca.uhn.fhir.jpa.partition;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.context.api.AddProfileTagEnum;
|
||||||
|
import ca.uhn.fhir.interceptor.api.HookParams;
|
||||||
|
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
|
||||||
|
import ca.uhn.fhir.interceptor.api.IInterceptorService;
|
||||||
|
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||||
|
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||||
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
|
import ca.uhn.fhir.rest.server.ETagSupportEnum;
|
||||||
|
import ca.uhn.fhir.rest.server.ElementsSupportEnum;
|
||||||
|
import ca.uhn.fhir.rest.server.IPagingProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.IRestfulServerDefaults;
|
||||||
|
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A default RequestDetails implementation that can be used for system calls to
|
||||||
|
* Resource DAO methods when partitioning is enabled. Using a SystemRequestDetails
|
||||||
|
* instance for system calls will ensure that any resource queries or updates will
|
||||||
|
* use the DEFAULT partition when partitioning is enabled.
|
||||||
|
*/
|
||||||
|
public class SystemRequestDetails extends RequestDetails {
|
||||||
|
public SystemRequestDetails() {
|
||||||
|
super(new MyInterceptorBroadcaster());
|
||||||
|
}
|
||||||
|
|
||||||
|
public SystemRequestDetails(IInterceptorBroadcaster theInterceptorBroadcaster) {
|
||||||
|
super(theInterceptorBroadcaster);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected byte[] getByteStreamRequestContents() {
|
||||||
|
return new byte[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Charset getCharset() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FhirContext getFhirContext() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHeader(String name) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getHeaders(String name) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getAttribute(String theAttributeName) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAttribute(String theAttributeName, Object theAttributeValue) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getInputStream() throws IOException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Reader getReader() throws IOException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IRestfulServerDefaults getServer() {
|
||||||
|
return new MyRestfulServerDefaults();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getServerBaseForRequest() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class MyRestfulServerDefaults implements IRestfulServerDefaults {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AddProfileTagEnum getAddProfileTag() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EncodingEnum getDefaultResponseEncoding() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ETagSupportEnum getETagSupport() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementsSupportEnum getElementsSupport() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FhirContext getFhirContext() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<IServerInterceptor> getInterceptors_() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IPagingProvider getPagingProvider() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDefaultPrettyPrint() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IInterceptorService getInterceptorService() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class MyInterceptorBroadcaster implements IInterceptorBroadcaster {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean callHooks(Pointcut thePointcut, HookParams theParams) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object callHooksAndReturnObject(Pointcut thePointcut, HookParams theParams) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasHooks(Pointcut thePointcut) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,21 +1,20 @@
|
||||||
package ca.uhn.fhir.jpa.packages;
|
package ca.uhn.fhir.jpa.packages;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.interceptor.api.IInterceptorService;
|
||||||
import ca.uhn.fhir.jpa.dao.data.INpmPackageDao;
|
import ca.uhn.fhir.jpa.dao.data.INpmPackageDao;
|
||||||
import ca.uhn.fhir.jpa.dao.data.INpmPackageVersionDao;
|
import ca.uhn.fhir.jpa.dao.data.INpmPackageVersionDao;
|
||||||
import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test;
|
import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
|
||||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||||
import ca.uhn.fhir.util.JsonUtil;
|
import ca.uhn.fhir.rest.server.interceptor.partition.RequestTenantPartitionInterceptor;
|
||||||
import org.hl7.fhir.utilities.npm.IPackageCacheManager;
|
|
||||||
import org.hl7.fhir.utilities.npm.NpmPackage;
|
import org.hl7.fhir.utilities.npm.NpmPackage;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
|
||||||
import static org.hamcrest.Matchers.contains;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
|
||||||
|
@ -28,6 +27,16 @@ public class JpaPackageCacheTest extends BaseJpaR4Test {
|
||||||
private INpmPackageDao myPackageDao;
|
private INpmPackageDao myPackageDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
private INpmPackageVersionDao myPackageVersionDao;
|
private INpmPackageVersionDao myPackageVersionDao;
|
||||||
|
@Autowired
|
||||||
|
private IInterceptorService myInterceptorService;
|
||||||
|
@Autowired
|
||||||
|
private RequestTenantPartitionInterceptor myRequestTenantPartitionInterceptor;
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
public void disablePartitioning() {
|
||||||
|
myPartitionSettings.setPartitioningEnabled(false);
|
||||||
|
myInterceptorService.unregisterInterceptor(myRequestTenantPartitionInterceptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -53,6 +62,36 @@ public class JpaPackageCacheTest extends BaseJpaR4Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSaveAndDeletePackagePartitionsEnabled() throws IOException {
|
||||||
|
myPartitionSettings.setPartitioningEnabled(true);
|
||||||
|
myInterceptorService.registerInterceptor(myRequestTenantPartitionInterceptor);
|
||||||
|
|
||||||
|
try (InputStream stream = IgInstallerDstu3Test.class.getResourceAsStream("/packages/basisprofil.de.tar.gz")) {
|
||||||
|
myPackageCacheManager.addPackageToCache("basisprofil.de", "0.2.40", stream, "basisprofil.de");
|
||||||
|
}
|
||||||
|
|
||||||
|
NpmPackage pkg;
|
||||||
|
|
||||||
|
pkg = myPackageCacheManager.loadPackage("basisprofil.de", null);
|
||||||
|
assertEquals("0.2.40", pkg.version());
|
||||||
|
|
||||||
|
pkg = myPackageCacheManager.loadPackage("basisprofil.de", "0.2.40");
|
||||||
|
assertEquals("0.2.40", pkg.version());
|
||||||
|
|
||||||
|
try {
|
||||||
|
myPackageCacheManager.loadPackage("basisprofil.de", "99");
|
||||||
|
fail();
|
||||||
|
} catch (ResourceNotFoundException e) {
|
||||||
|
assertEquals("Unable to locate package basisprofil.de#99", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
PackageDeleteOutcomeJson deleteOutcomeJson = myPackageCacheManager.uninstallPackage("basisprofil.de", "0.2.40");
|
||||||
|
List<String> deleteOutcomeMsgs = deleteOutcomeJson.getMessage();
|
||||||
|
assertEquals("Deleting package basisprofil.de#0.2.40", deleteOutcomeMsgs.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSavePackageWithLongDescription() throws IOException {
|
public void testSavePackageWithLongDescription() throws IOException {
|
||||||
try (InputStream stream = IgInstallerDstu3Test.class.getResourceAsStream("/packages/package-davinci-cdex-0.2.0.tgz")) {
|
try (InputStream stream = IgInstallerDstu3Test.class.getResourceAsStream("/packages/package-davinci-cdex-0.2.0.tgz")) {
|
||||||
|
|
|
@ -2,6 +2,8 @@ package ca.uhn.fhir.jpa.packages;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
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.config.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.dao.data.INpmPackageDao;
|
import ca.uhn.fhir.jpa.dao.data.INpmPackageDao;
|
||||||
import ca.uhn.fhir.jpa.dao.data.INpmPackageVersionDao;
|
import ca.uhn.fhir.jpa.dao.data.INpmPackageVersionDao;
|
||||||
|
@ -10,13 +12,18 @@ import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test;
|
||||||
import ca.uhn.fhir.jpa.model.entity.NpmPackageEntity;
|
import ca.uhn.fhir.jpa.model.entity.NpmPackageEntity;
|
||||||
import ca.uhn.fhir.jpa.model.entity.NpmPackageVersionEntity;
|
import ca.uhn.fhir.jpa.model.entity.NpmPackageVersionEntity;
|
||||||
import ca.uhn.fhir.jpa.model.entity.NpmPackageVersionResourceEntity;
|
import ca.uhn.fhir.jpa.model.entity.NpmPackageVersionResourceEntity;
|
||||||
|
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
import ca.uhn.fhir.rest.api.Constants;
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
import ca.uhn.fhir.rest.param.ReferenceParam;
|
import ca.uhn.fhir.rest.param.ReferenceParam;
|
||||||
import ca.uhn.fhir.rest.param.TokenParam;
|
import ca.uhn.fhir.rest.param.TokenParam;
|
||||||
import ca.uhn.fhir.rest.param.UriParam;
|
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.exceptions.InternalErrorException;
|
||||||
|
import ca.uhn.fhir.rest.server.interceptor.partition.RequestTenantPartitionInterceptor;
|
||||||
|
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||||
import ca.uhn.fhir.test.utilities.JettyUtil;
|
import ca.uhn.fhir.test.utilities.JettyUtil;
|
||||||
import ca.uhn.fhir.test.utilities.ProxyUtil;
|
import ca.uhn.fhir.test.utilities.ProxyUtil;
|
||||||
import ca.uhn.fhir.util.JsonUtil;
|
import ca.uhn.fhir.util.JsonUtil;
|
||||||
|
@ -63,6 +70,8 @@ import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
public class NpmR4Test extends BaseJpaR4Test {
|
public class NpmR4Test extends BaseJpaR4Test {
|
||||||
|
|
||||||
|
@ -81,6 +90,10 @@ public class NpmR4Test extends BaseJpaR4Test {
|
||||||
@Autowired
|
@Autowired
|
||||||
private INpmPackageVersionResourceDao myPackageVersionResourceDao;
|
private INpmPackageVersionResourceDao myPackageVersionResourceDao;
|
||||||
private FakeNpmServlet myFakeNpmServlet;
|
private FakeNpmServlet myFakeNpmServlet;
|
||||||
|
@Autowired
|
||||||
|
private IInterceptorService myInterceptorService;
|
||||||
|
@Autowired
|
||||||
|
private RequestTenantPartitionInterceptor myRequestTenantPartitionInterceptor;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void before() throws Exception {
|
public void before() throws Exception {
|
||||||
|
@ -105,6 +118,8 @@ public class NpmR4Test extends BaseJpaR4Test {
|
||||||
public void after() throws Exception {
|
public void after() throws Exception {
|
||||||
JettyUtil.closeServer(myServer);
|
JettyUtil.closeServer(myServer);
|
||||||
myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences());
|
myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences());
|
||||||
|
myPartitionSettings.setPartitioningEnabled(false);
|
||||||
|
myInterceptorService.unregisterInterceptor(myRequestTenantPartitionInterceptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -273,6 +288,47 @@ public class NpmR4Test extends BaseJpaR4Test {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInstallR4Package_NonConformanceResources_Partitioned() throws Exception {
|
||||||
|
myPartitionSettings.setPartitioningEnabled(true);
|
||||||
|
myInterceptorService.registerInterceptor(myRequestTenantPartitionInterceptor);
|
||||||
|
myDaoConfig.setAllowExternalReferences(true);
|
||||||
|
|
||||||
|
byte[] bytes = loadClasspathBytes("/packages/test-organizations-package.tgz");
|
||||||
|
myFakeNpmServlet.myResponses.put("/test-organizations/1.0.0", bytes);
|
||||||
|
|
||||||
|
List<String> resourceList = new ArrayList<>();
|
||||||
|
resourceList.add("Organization");
|
||||||
|
PackageInstallationSpec spec = new PackageInstallationSpec().setName("test-organizations").setVersion("1.0.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL);
|
||||||
|
spec.setInstallResourceTypes(resourceList);
|
||||||
|
PackageInstallOutcomeJson outcome = igInstaller.install(spec);
|
||||||
|
assertEquals(3, outcome.getResourcesInstalled().get("Organization"));
|
||||||
|
|
||||||
|
// Be sure no further communication with the server
|
||||||
|
JettyUtil.closeServer(myServer);
|
||||||
|
|
||||||
|
// Search for the installed resources
|
||||||
|
mySrd = mock(ServletRequestDetails.class);
|
||||||
|
when(mySrd.getTenantId()).thenReturn(JpaConstants.DEFAULT_PARTITION_NAME);
|
||||||
|
when(mySrd.getServer()).thenReturn(mock(RestfulServer.class));
|
||||||
|
when(mySrd.getInterceptorBroadcaster()).thenReturn(mock(IInterceptorBroadcaster.class));
|
||||||
|
runInTransaction(() -> {
|
||||||
|
SearchParameterMap map = SearchParameterMap.newSynchronous();
|
||||||
|
map.add(Organization.SP_IDENTIFIER, new TokenParam("https://github.com/synthetichealth/synthea", "organization1"));
|
||||||
|
IBundleProvider result = myOrganizationDao.search(map, mySrd);
|
||||||
|
assertEquals(1, result.sizeOrThrowNpe());
|
||||||
|
map = SearchParameterMap.newSynchronous();
|
||||||
|
map.add(Organization.SP_IDENTIFIER, new TokenParam("https://github.com/synthetichealth/synthea", "organization2"));
|
||||||
|
result = myOrganizationDao.search(map, mySrd);
|
||||||
|
assertEquals(1, result.sizeOrThrowNpe());
|
||||||
|
map = SearchParameterMap.newSynchronous();
|
||||||
|
map.add(Organization.SP_IDENTIFIER, new TokenParam("https://github.com/synthetichealth/synthea", "organization3"));
|
||||||
|
result = myOrganizationDao.search(map, mySrd);
|
||||||
|
assertEquals(1, result.sizeOrThrowNpe());
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testInstallR4Package_NoIdentifierNoUrl() throws Exception {
|
public void testInstallR4Package_NoIdentifierNoUrl() throws Exception {
|
||||||
myDaoConfig.setAllowExternalReferences(true);
|
myDaoConfig.setAllowExternalReferences(true);
|
||||||
|
|
Loading…
Reference in New Issue