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
Expand Up @@ -48,7 +48,7 @@
import org.opencds.cqf.fhir.utility.adapter.IAdapterFactory;
import org.opencds.cqf.fhir.utility.adapter.IKnowledgeArtifactAdapter;
import org.opencds.cqf.fhir.utility.adapter.IValueSetAdapter;
import org.opencds.cqf.fhir.utility.client.TerminologyServerClient;
import org.opencds.cqf.fhir.utility.client.terminology.FederatedTerminologyProviderRouter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanWrapperImpl;
Expand Down Expand Up @@ -363,7 +363,7 @@ private static void tryExpandValueSet(
// Only update if ValueSet has changed since last expansion
if (wasValueSetChangedSinceLastExpansion(vset)) {
var factory = IAdapterFactory.forFhirVersion(FhirVersionEnum.R4);
var ts = new TerminologyServerClient(context);
var ts = new FederatedTerminologyProviderRouter(context);
var expandHelper = new ExpandHelper(repository, ts);
var endpointAdapter = Optional.ofNullable(terminologyEndpoint).map(factory::createEndpoint);
var valueSetAdapter = (IValueSetAdapter) factory.createKnowledgeArtifactAdapter(vset);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@
import java.util.List;
import java.util.stream.Collectors;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4.model.BooleanType;
import org.hl7.fhir.r4.model.CodeType;
import org.hl7.fhir.r4.model.Endpoint;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Library;
import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent;
import org.hl7.fhir.r4.model.Parameters;
import org.hl7.fhir.r4.model.PrimitiveType;
import org.hl7.fhir.r4.model.StringType;
import org.opencds.cqf.fhir.cr.hapi.common.ILibraryProcessorFactory;
Expand Down Expand Up @@ -60,6 +61,8 @@ public LibraryPackageProvider(ILibraryProcessorFactory libraryProcessorFactory)
* It is invalid to request a 'transaction' bundle and use
* paging. Doing so will result in an error.
* @param include Specifies what contents should only be included in the resulting package.
* @param artifactEndpointConfiguration Configuration information to resolve canonical artifacts.
* Contains parts: artifactRoute, endpointUri, endpoint.
* @param terminologyEndpoint the FHIR {@link Endpoint} Endpoint resource or url to use to access terminology (i.e. valuesets, codesystems, naming systems, concept maps, and membership testing) referenced by the Resource. If no terminology endpoint is supplied, the evaluation will attempt to use the server on which the operation is being performed as the terminology server.
* @param usePut the boolean value to determine if the Bundle returned uses PUT or POST request methods. Defaults to false.
* @param requestDetails the details (such as tenant) of this request. Usually autopopulated by HAPI.
Expand All @@ -72,10 +75,16 @@ public IBaseBundle packageLibrary(
@OperationParam(name = "count", typeName = "integer") IPrimitiveType<Integer> count,
@OperationParam(name = "bundleType") StringType bundleType,
@OperationParam(name = "include") List<CodeType> include,
@OperationParam(name = "terminologyEndpoint") ParametersParameterComponent terminologyEndpoint,
@OperationParam(name = "artifactEndpointConfiguration")
List<Parameters.ParametersParameterComponent> artifactEndpointConfiguration,
@OperationParam(name = "terminologyEndpoint") Parameters.ParametersParameterComponent terminologyEndpoint,
@OperationParam(name = "usePut") BooleanType usePut,
RequestDetails requestDetails)
throws InternalErrorException, FHIRException {
var terminologyEndpointParam = getEndpoint(fhirVersion, terminologyEndpoint);
List<IBase> artifactEndpointConfigurationParam = artifactEndpointConfiguration == null
? null
: artifactEndpointConfiguration.stream().map(p -> (IBase) p).collect(Collectors.<IBase>toList());
return libraryProcessorFactory
.create(requestDetails)
.packageLibrary(
Expand All @@ -91,7 +100,8 @@ public IBaseBundle packageLibrary(
.distinct()
.map(PrimitiveType::getValueAsString)
.collect(Collectors.toList()),
getEndpoint(fhirVersion, terminologyEndpoint),
artifactEndpointConfigurationParam,
terminologyEndpointParam,
usePut == null ? Boolean.FALSE : usePut.booleanValue()));
}

Expand Down Expand Up @@ -135,10 +145,16 @@ public IBaseBundle packageLibrary(
@OperationParam(name = "count", typeName = "integer") IPrimitiveType<Integer> count,
@OperationParam(name = "bundleType") StringType bundleType,
@OperationParam(name = "include") List<CodeType> include,
@OperationParam(name = "terminologyEndpoint") ParametersParameterComponent terminologyEndpoint,
@OperationParam(name = "artifactEndpointConfiguration")
List<Parameters.ParametersParameterComponent> artifactEndpointConfiguration,
@OperationParam(name = "terminologyEndpoint") Parameters.ParametersParameterComponent terminologyEndpoint,
@OperationParam(name = "usePut") BooleanType usePut,
RequestDetails requestDetails)
throws InternalErrorException, FHIRException {
var terminologyEndpointParam = getEndpoint(fhirVersion, terminologyEndpoint);
List<IBase> artifactEndpointConfigurationParam = artifactEndpointConfiguration == null
? null
: artifactEndpointConfiguration.stream().map(p -> (IBase) p).collect(Collectors.<IBase>toList());
return libraryProcessorFactory
.create(requestDetails)
.packageLibrary(
Expand All @@ -157,7 +173,8 @@ public IBaseBundle packageLibrary(
.distinct()
.map(PrimitiveType::getValueAsString)
.collect(Collectors.toList()),
getEndpoint(fhirVersion, terminologyEndpoint),
artifactEndpointConfigurationParam,
terminologyEndpointParam,
usePut == null ? Boolean.FALSE : usePut.booleanValue()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
import org.opencds.cqf.fhir.utility.adapter.IEndpointAdapter;
import org.opencds.cqf.fhir.utility.adapter.IKnowledgeArtifactAdapter;
import org.opencds.cqf.fhir.utility.adapter.IKnowledgeArtifactVisitor;
import org.opencds.cqf.fhir.utility.client.TerminologyServerClient;
import org.opencds.cqf.fhir.utility.client.terminology.ITerminologyProviderRouter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -131,7 +131,7 @@ protected void recursiveGather(
List<String> include,
ImmutableTriple<List<String>, List<String>, List<String>> versionTuple,
IEndpointAdapter terminologyEndpoint,
TerminologyServerClient client,
ITerminologyProviderRouter router,
IBaseOperationOutcome[] messagesWrapper)
throws PreconditionFailedException {
Map<String, String> igDependencyVersions = extractIgDependencyVersions(adapter);
Expand All @@ -142,7 +142,7 @@ protected void recursiveGather(
include,
versionTuple,
terminologyEndpoint,
client,
router,
messagesWrapper,
igDependencyVersions);
}
Expand All @@ -154,7 +154,7 @@ protected void recursiveGather(
List<String> include,
ImmutableTriple<List<String>, List<String>, List<String>> versionTuple,
IEndpointAdapter terminologyEndpoint,
TerminologyServerClient client,
ITerminologyProviderRouter client,
IBaseOperationOutcome[] messagesWrapper,
Map<String, String> igDependencyVersions)
throws PreconditionFailedException {
Expand Down Expand Up @@ -246,11 +246,11 @@ protected <T extends ICompositeType & IBaseHasExtensions> void addRelatedArtifac
}

private IDomainResource tryGetValueSetsFromTxServer(
IDependencyInfo ra, TerminologyServerClient client, IEndpointAdapter endpoint) {
if (client != null
IDependencyInfo ra, ITerminologyProviderRouter router, IEndpointAdapter endpoint) {
if (router != null
&& endpoint != null
&& Canonicals.getResourceType(ra.getReference()).equals("ValueSet")) {
return client.getValueSetResource(endpoint, ra.getReference()).orElse(null);
return router.getValueSetResource(endpoint, ra.getReference()).orElse(null);
}
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@
import org.opencds.cqf.fhir.utility.adapter.IParametersParameterComponentAdapter;
import org.opencds.cqf.fhir.utility.adapter.IValueSetAdapter;
import org.opencds.cqf.fhir.utility.client.ExpandRunner.TerminologyServerExpansionException;
import org.opencds.cqf.fhir.utility.client.TerminologyServerClient;
import org.opencds.cqf.fhir.utility.client.terminology.ArtifactEndpointConfiguration;
import org.opencds.cqf.fhir.utility.client.terminology.ITerminologyProviderRouter;
import org.opencds.cqf.fhir.utility.client.terminology.ITerminologyServerClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -39,7 +41,7 @@ public class ExpandHelper {
private static final Logger log = LoggerFactory.getLogger(ExpandHelper.class);
private final IRepository repository;
private final IAdapterFactory adapterFactory;
private final TerminologyServerClient terminologyServerClient;
private final ITerminologyProviderRouter terminologyServerRouter;
public static final List<String> unsupportedParametersToRemove = List.of(Constants.CANONICAL_VERSION);

// Parameters we care to validate round-trip in the expansion
Expand All @@ -50,10 +52,10 @@ public class ExpandHelper {
"system-version", "used-system-version",
"valueset-version", "used-valueset-version");

public ExpandHelper(IRepository repository, TerminologyServerClient server) {
public ExpandHelper(IRepository repository, ITerminologyProviderRouter server) {
this.repository = repository;
adapterFactory = IAdapterFactory.forFhirContext(this.repository.fhirContext());
terminologyServerClient = server;
terminologyServerRouter = server;
}

private FhirContext fhirContext() {
Expand Down Expand Up @@ -91,9 +93,10 @@ public void expandValueSet(
.filter(e -> e.getUrl().equals(Constants.AUTHORITATIVE_SOURCE_URL))
.findFirst()
.map(url -> ((IPrimitiveType<String>) url.getValue()).getValueAsString())
.map(url -> TerminologyServerClient.getAddressBase(url, fhirContext()))
.map(url -> ITerminologyServerClient.getAddressBase(url, fhirContext()))
.orElse(null);
// If terminologyEndpoint exists and we have no authoritativeSourceUrl or the authoritativeSourceUrl matches the
// If terminologyEndpoint exists, and we have no authoritativeSourceUrl or the authoritativeSourceUrl matches
// the
// terminologyEndpoint address then we will use the terminologyEndpoint for expansion
if (terminologyEndpoint.isPresent()
&& (authoritativeSourceUrl == null
Expand Down Expand Up @@ -144,10 +147,64 @@ else if (valueSet.hasGroupingCompose()) {
expandedList.add(valueSet.getUrl());
}

/**
* Expands a ValueSet using CRMI artifact endpoint configurations for routing.
* Falls back to legacy terminologyEndpoint if no configurations match, then to local expansion.
*
* @param valueSet the ValueSet to expand
* @param expansionParameters expansion parameters
* @param artifactEndpointConfigurations CRMI endpoint configurations for routing
* @param terminologyEndpoint legacy single endpoint (used as fallback)
* @param valueSets list of all ValueSets being processed
* @param expandedList list of already expanded ValueSet URLs
* @param expansionTimestamp timestamp for expansion
*/
public void expandValueSet(
IValueSetAdapter valueSet,
IParametersAdapter expansionParameters,
List<ArtifactEndpointConfiguration> artifactEndpointConfigurations,
Optional<IEndpointAdapter> terminologyEndpoint,
List<IValueSetAdapter> valueSets,
List<String> expandedList,
Date expansionTimestamp) {
// Have we already expanded this ValueSet?
if (expandedList.contains(valueSet.getUrl())) {
return;
}
filterOutUnsupportedParameters(expansionParameters);

// Try CRMI configuration-based routing first if configurations are provided
if (artifactEndpointConfigurations != null && !artifactEndpointConfigurations.isEmpty()) {
try {
var expandedResult = terminologyServerRouter.expandWithConfigurations(
valueSet, artifactEndpointConfigurations, expansionParameters);
if (expandedResult != null) {
var expandedValueSet = (IValueSetAdapter) adapterFactory.createResource(expandedResult);
if (!valueSet.hasVersion()) {
valueSet.setVersion(expandedValueSet.getVersion());
}
valueSet.setExpansion(expandedValueSet.getExpansion());
validateExpansionParameters(valueSet, expansionParameters);
expandedList.add(valueSet.getUrl());
return;
}
} catch (TerminologyServerExpansionException e) {
log.warn(
"Failed to expand value set {} using artifact endpoint configurations. Reason: {}. "
+ "Will attempt fallback expansion.",
valueSet.getUrl(),
e.getMessage());
}
}

// Fall back to legacy single endpoint or local expansion
expandValueSet(valueSet, expansionParameters, terminologyEndpoint, valueSets, expandedList, expansionTimestamp);
}

private void terminologyServerExpand(
IValueSetAdapter valueSet, IParametersAdapter expansionParameters, IEndpointAdapter terminologyEndpoint) {
var expandedValueSet = (IValueSetAdapter) adapterFactory.createResource(
terminologyServerClient.expand(valueSet, terminologyEndpoint, expansionParameters));
terminologyServerRouter.expand(valueSet, terminologyEndpoint, expansionParameters));
// expansions are only valid for a particular version
if (!valueSet.hasVersion()) {
valueSet.setVersion(expandedValueSet.getVersion());
Expand Down Expand Up @@ -261,7 +318,7 @@ private IValueSetAdapter getIncludedValueSet(
.orElseGet(() -> {
if (terminologyEndpoint.isPresent()) {
try {
return terminologyServerClient
return terminologyServerRouter
.getValueSetResource(terminologyEndpoint.get(), reference)
.map(r -> (IValueSetAdapter) adapterFactory.createResource(r))
.orElse(null);
Expand All @@ -286,29 +343,29 @@ private void expandIncluded(
IValueSetAdapter includedVS) {
// update url and version exp params for child expansions
var childExpParams = (IParametersAdapter) adapterFactory.createResource(expansionParameters.copy());
if (childExpParams.hasParameter(TerminologyServerClient.urlParamName)) {
if (childExpParams.hasParameter(ITerminologyServerClient.urlParamName)) {
var newParams = childExpParams.getParameter().stream()
.filter(p -> !p.getName().equals(TerminologyServerClient.urlParamName))
.filter(p -> !p.getName().equals(ITerminologyServerClient.urlParamName))
.collect(Collectors.toList());
if (includedVS.hasUrl()) {
newParams.add(adapterFactory.createParametersParameter((IBaseBackboneElement)
(fhirContext().getVersion().getVersion() == FhirVersionEnum.DSTU3
? Parameters.newUriPart(
fhirContext(), TerminologyServerClient.urlParamName, includedVS.getUrl())
fhirContext(), ITerminologyServerClient.urlParamName, includedVS.getUrl())
: Parameters.newUrlPart(
fhirContext(), TerminologyServerClient.urlParamName, includedVS.getUrl()))));
fhirContext(), ITerminologyServerClient.urlParamName, includedVS.getUrl()))));
}
childExpParams.setParameter(newParams.stream()
.map(IParametersParameterComponentAdapter::get)
.toList());
}
if (childExpParams.hasParameter(TerminologyServerClient.versionParamName)) {
if (childExpParams.hasParameter(ITerminologyServerClient.versionParamName)) {
var newParams = childExpParams.getParameter().stream()
.filter(p -> !p.getName().equals(TerminologyServerClient.versionParamName))
.filter(p -> !p.getName().equals(ITerminologyServerClient.versionParamName))
.collect(Collectors.toList());
if (includedVS.hasVersion()) {
newParams.add(adapterFactory.createParametersParameter((IBaseBackboneElement) Parameters.newStringPart(
fhirContext(), TerminologyServerClient.versionParamName, includedVS.getVersion())));
fhirContext(), ITerminologyServerClient.versionParamName, includedVS.getVersion())));
}
childExpParams.setParameter(newParams.stream()
.map(IParametersParameterComponentAdapter::get)
Expand Down
Loading
Loading