Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package org.opencds.cqf.fhir.cr.hapi.config;

import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.cache.IResourceChangeListenerRegistry;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.repository.IRepository;
import ca.uhn.fhir.rest.api.server.IRepositoryFactory;
Expand Down Expand Up @@ -124,6 +126,7 @@ public CdsServiceInterceptor cdsServiceInterceptor(
CdsServiceRegistryImpl cdsServiceRegistry,
ICrDiscoveryServiceFactory discoveryServiceFactory,
ICdsCrServiceFactory crServiceFactory,
PartitionSettings partitionSettings,
Optional<IResourceChangeListenerRegistry> resourceChangeListenerRegistry) {
if (resourceChangeListenerRegistry.isEmpty()) {
return null;
Expand All @@ -132,7 +135,11 @@ public CdsServiceInterceptor cdsServiceInterceptor(
resourceChangeListenerRegistry
.get()
.registerResourceResourceChangeListener(
PLAN_DEFINITION_RESOURCE_NAME, SearchParameterMap.newSynchronous(), listener, 1000);
PLAN_DEFINITION_RESOURCE_NAME,
RequestPartitionId.defaultPartition(partitionSettings),
SearchParameterMap.newSynchronous(),
listener,
1000);
return listener;
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
package org.opencds.cqf.fhir.cr.hapi.config.test;

import ca.uhn.fhir.batch2.jobs.reindex.ReindexProvider;
import ca.uhn.fhir.batch2.jobs.bulkmodify.reindex.ReindexProvider;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
import ca.uhn.fhir.jpa.cache.IResourceChangeListener;
import ca.uhn.fhir.jpa.cache.IResourceChangeListenerCacheRefresher;
import ca.uhn.fhir.jpa.cache.IResourceChangeListenerRegistry;
import ca.uhn.fhir.jpa.cache.ResourceChangeListenerCacheFactory;
import ca.uhn.fhir.jpa.cache.ResourceChangeListenerCacheRefresherImpl;
import ca.uhn.fhir.jpa.cache.ResourceChangeListenerRegistryImpl;
import ca.uhn.fhir.jpa.cache.ResourceChangeListenerRegistryInterceptor;
import ca.uhn.fhir.jpa.graphql.GraphQLProvider;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.provider.DiffProvider;
import ca.uhn.fhir.jpa.provider.IJpaSystemProvider;
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
Expand All @@ -26,6 +29,7 @@
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.provider.ResourceProviderFactory;
import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
import jakarta.annotation.Nonnull;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
Expand All @@ -49,6 +53,9 @@
@Configuration
@Import({SubscriptionSubmitterConfig.class, SubscriptionChannelConfig.class})
public class TestCrConfig {
public static final String LIBRARY_RESOURCE_NAME = "Library";
public static final String VALUESET_RESOURCE_NAME = "ValueSet";

@Bean
public RestfulServer restfulServer(
IFhirSystemDao<?, ?> fhirSystemDao,
Expand Down Expand Up @@ -116,26 +123,28 @@ public Map<String, List<Code>> globalValueSetCache() {
@Bean
public ElmCacheResourceChangeListener elmCacheResourceChangeListener(
IResourceChangeListenerRegistry resourceChangeListenerRegistry,
PartitionSettings partitionSettings,
DaoRegistry daoRegistry,
EvaluationSettings evaluationSettings) {
ElmCacheResourceChangeListener listener =
new ElmCacheResourceChangeListener(daoRegistry, evaluationSettings.getLibraryCache());
resourceChangeListenerRegistry.registerResourceResourceChangeListener(
"Library", SearchParameterMap.newSynchronous(), listener, 1000);
registerResourceResourceChangeListener(
resourceChangeListenerRegistry, partitionSettings, listener, LIBRARY_RESOURCE_NAME);
return listener;
}

@Bean
public CodeCacheResourceChangeListener codeCacheResourceChangeListener(
IResourceChangeListenerRegistry resourceChangeListenerRegistry,
EvaluationSettings evaluationSettings,
PartitionSettings partitionSettings,
DaoRegistry daoRegistry) {

CodeCacheResourceChangeListener listener =
new CodeCacheResourceChangeListener(daoRegistry, evaluationSettings.getValueSetCache());
// registry
resourceChangeListenerRegistry.registerResourceResourceChangeListener(
"ValueSet", SearchParameterMap.newSynchronous(), listener, 1000);
registerResourceResourceChangeListener(
resourceChangeListenerRegistry, partitionSettings, listener, VALUESET_RESOURCE_NAME);

return listener;
}
Expand All @@ -144,9 +153,10 @@ public CodeCacheResourceChangeListener codeCacheResourceChangeListener(
public IResourceChangeListenerRegistry resourceChangeListenerRegistry(
InMemoryResourceMatcher inMemoryResourceMatcher,
FhirContext fhirContext,
PartitionSettings partitionSettings,
ResourceChangeListenerCacheFactory resourceChangeListenerCacheFactory) {
return new ResourceChangeListenerRegistryImpl(
fhirContext, resourceChangeListenerCacheFactory, inMemoryResourceMatcher);
fhirContext, partitionSettings, resourceChangeListenerCacheFactory, inMemoryResourceMatcher);
}

@Bean
Expand All @@ -158,4 +168,28 @@ IResourceChangeListenerCacheRefresher resourceChangeListenerCacheRefresher() {
public ResourceChangeListenerRegistryInterceptor resourceChangeListenerRegistryInterceptor() {
return new ResourceChangeListenerRegistryInterceptor();
}

@Bean
public PartitionSettings partitionSettings() {
return new PartitionSettings();
}

private void registerResourceResourceChangeListener(
IResourceChangeListenerRegistry resourceChangeListenerRegistry,
PartitionSettings partitionSettings,
IResourceChangeListener listener,
String resourceType) {

resourceChangeListenerRegistry.registerResourceResourceChangeListener(
resourceType,
getRequestPartitionId(partitionSettings),
SearchParameterMap.newSynchronous(),
listener,
1000);
}

@Nonnull
private RequestPartitionId getRequestPartitionId(PartitionSettings partitionSettings) {
return RequestPartitionId.defaultPartition(partitionSettings);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,30 @@
import static java.util.Objects.nonNull;

import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome;
import ca.uhn.fhir.jpa.repository.HapiFhirRepository;
import ca.uhn.fhir.jpa.repository.SearchConverter;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.jpa.repository.searchparam.SearchParameterMapRepositoryRestQueryBuilder;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.repository.impl.MultiMapRepositoryRestQueryBuilder;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.PatchTypeEnum;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.RestfulServerUtils;
import ca.uhn.fhir.rest.server.SimpleBundleProvider;
import com.google.common.collect.Multimap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -37,9 +40,10 @@
* Since hapi-fhir is released quarterly, we want to maintain flexibility to change behaviour
* within our more flexible release cadence.
*/
@SuppressWarnings("UnstableApiUsage")
public class ClinicalIntelligenceHapiFhirRepository extends HapiFhirRepository {

private static final Logger ourLog = LoggerFactory.getLogger(ClinicalIntelligenceHapiFhirRepository.class);
private static final Logger logger = LoggerFactory.getLogger(ClinicalIntelligenceHapiFhirRepository.class);

private final RequestDetails requestDetails;
private final RestfulServer restfulServer;
Expand All @@ -53,8 +57,17 @@ public ClinicalIntelligenceHapiFhirRepository(
this.daoRegistry = daoRegistry;
}

@Override
public <T extends IBaseResource, I extends IIdType> T read(
Class<T> resourceType, I id, Map<String, String> headers) {

return (T) this.daoRegistry
.getResourceDao(resourceType)
.read(id, cloneWithAction(resourceType, headers, RestOperationTypeEnum.READ));
}

/**
* Override {@link HapiFhirRepository#search(Class, Class, Map, Map)} to ensure that the
* Override {@link HapiFhirRepository#search(Class, Class, IRepositoryRestQueryContributor, Map)} to ensure that the
* _count {@link RequestDetails} parameter is passed through to the DAO layer instead of
* dropping it.
* <p/>
Expand All @@ -67,18 +80,15 @@ public ClinicalIntelligenceHapiFhirRepository(
public <B extends IBaseBundle, T extends IBaseResource> B search(
Class<B> bundleType,
Class<T> resourceType,
Multimap<String, List<IQueryParameterType>> searchParameters,
IRepositoryRestQueryContributor queryContributor,
Map<String, String> headers) {

var details = ClinicalIntelligenceRequestDetailsCloner.startWith(requestDetails)
.setAction(RestOperationTypeEnum.SEARCH_TYPE)
.addHeaders(headers)
.create();
var details = cloneWithAction(resourceType, headers, RestOperationTypeEnum.SEARCH_TYPE);

var converter = new SearchConverter();
converter.convertParameters(searchParameters, fhirContext());
details.setParameters(converter.myResultParameters);
var searchParameterMap =
SearchParameterMapRepositoryRestQueryBuilder.buildFromQueryContributor(queryContributor);

details.setParameters(MultiMapRepositoryRestQueryBuilder.toFlatMap(searchParameterMap));
details.setResourceName(daoRegistry.getFhirContext().getResourceType(resourceType));

if (details instanceof SystemRequestDetails) {
Expand All @@ -89,13 +99,55 @@ public <B extends IBaseBundle, T extends IBaseResource> B search(
}

var resourceDao = daoRegistry.getResourceDao(resourceType);
var bundleProvider = resourceDao.search(converter.mySearchParameterMap, details);
var bundleProvider = resourceDao.search(searchParameterMap, details);

bundleProvider = sanitizeBundleProvider(bundleProvider);

return createBundle(details, bundleProvider);
}

public <T extends IBaseResource> MethodOutcome create(T resource, Map<String, String> headers) {
return this.daoRegistry
.getResourceDao(resource)
.create(resource, cloneWithAction(resource, headers, RestOperationTypeEnum.CREATE));
}

public <T extends IBaseResource> MethodOutcome update(T resource, Map<String, String> headers) {
final DaoMethodOutcome update = daoRegistry
.getResourceDao(resource)
.update(resource, cloneWithAction(resource, headers, RestOperationTypeEnum.UPDATE));
boolean created = update.getCreated() != null && update.getCreated();
if (created) {
update.setResponseStatusCode(201);
} else {
update.setResponseStatusCode(200);
}

return update;
}

public <I extends IIdType, P extends IBaseParameters> MethodOutcome patch(
I id, P patchParameters, Map<String, String> headers) {
return this.daoRegistry
.getResourceDao(id.getResourceType())
.patch(
id,
(String) null,
PatchTypeEnum.FHIR_PATCH_JSON,
(String) null,
patchParameters,
cloneWithAction(patchParameters, headers, RestOperationTypeEnum.PATCH));
}

public <T extends IBaseResource, I extends IIdType> MethodOutcome delete(
Class<T> resourceType, I id, Map<String, String> headers) {
return this.daoRegistry
.getResourceDao(resourceType)
.delete(id, cloneWithAction(resourceType, headers, RestOperationTypeEnum.DELETE));
}

// N.B. We don't support request details cloning for link

protected IBundleProvider sanitizeBundleProvider(IBundleProvider bundleProvider) {
return nonNull(bundleProvider) ? bundleProvider : new SimpleBundleProvider();
}
Expand Down Expand Up @@ -125,7 +177,7 @@ protected BundleTypeEnum extractBundleTypeFromRequestParameters(Map<String, Stri
bundleType = BundleTypeEnum.VALUESET_BINDER.fromCodeString(bundleTypeValues[0]);

if (isNull(bundleType)) {
ourLog.error(
logger.error(
"Could not convert value {} to a BundleTypeEnum. Defaulting to {}",
bundleTypeValues[0],
bundleType);
Expand All @@ -135,6 +187,21 @@ protected BundleTypeEnum extractBundleTypeFromRequestParameters(Map<String, Stri
return bundleType;
}

private <T extends IBaseResource> RequestDetails cloneWithAction(
T resource, Map<String, String> headers, RestOperationTypeEnum operationTypeEnum) {
return cloneWithAction(resource.getClass(), headers, operationTypeEnum);
}

private <T extends IBaseResource> RequestDetails cloneWithAction(
Class<T> resourceType, Map<String, String> headers, RestOperationTypeEnum operationTypeEnum) {

return ClinicalIntelligenceRequestDetailsCloner.startWith(
requestDetails, daoRegistry.getFhirContext(), resourceType)
.setAction(operationTypeEnum)
.addHeaders(headers)
.create();
}

private <B extends IBaseBundle> B createBundle(RequestDetails requestDetails, IBundleProvider bundleProvider) {

Integer count = RestfulServerUtils.extractCountParameter(requestDetails);
Expand Down
Loading
Loading