Add terminology mapping and display population interceptors (#2488)
* Start work on code mapping interceptors * Work on generation * Add terminology mapping and display population interceptors * Add changelog * License header updates * Test fixes * Fix typo * Interceptor fixes * License header update * Test fixes * Mapping fixes * Version bump * License header updates * Add null tests * Add null guard * Intermittent fix
This commit is contained in:
parent
009f184ab4
commit
dc41d5ad61
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -24,6 +24,8 @@ import ca.uhn.fhir.context.FhirContext;
|
|||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.util.ParametersUtil;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
@ -84,6 +86,7 @@ public interface IValidationSupport {
|
|||
* @param theValueSetToExpand The valueset that should be expanded
|
||||
* @return The expansion, or null
|
||||
*/
|
||||
@Nullable
|
||||
default ValueSetExpansionOutcome expandValueSet(ValidationSupportContext theValidationSupportContext, @Nullable ValueSetExpansionOptions theExpansionOptions, @Nonnull IBaseResource theValueSetToExpand) {
|
||||
return null;
|
||||
}
|
||||
|
@ -93,6 +96,7 @@ public interface IValidationSupport {
|
|||
* validation support module. This method may return null if it doesn't
|
||||
* make sense for a given module.
|
||||
*/
|
||||
@Nullable
|
||||
default List<IBaseResource> fetchAllConformanceResources() {
|
||||
return null;
|
||||
}
|
||||
|
@ -100,6 +104,7 @@ public interface IValidationSupport {
|
|||
/**
|
||||
* Load and return all possible structure definitions
|
||||
*/
|
||||
@Nullable
|
||||
default <T extends IBaseResource> List<T> fetchAllStructureDefinitions() {
|
||||
return null;
|
||||
}
|
||||
|
@ -110,6 +115,7 @@ public interface IValidationSupport {
|
|||
* @param theSystem The code system
|
||||
* @return The valueset (must not be null, but can be an empty ValueSet)
|
||||
*/
|
||||
@Nullable
|
||||
default IBaseResource fetchCodeSystem(String theSystem) {
|
||||
return null;
|
||||
}
|
||||
|
@ -128,6 +134,7 @@ public interface IValidationSupport {
|
|||
* given URI can be found
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
default <T extends IBaseResource> T fetchResource(@Nullable Class<T> theClass, String theUri) {
|
||||
Validate.notBlank(theUri, "theUri must not be null or blank");
|
||||
|
||||
|
@ -161,6 +168,7 @@ public interface IValidationSupport {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
default IBaseResource fetchStructureDefinition(String theUrl) {
|
||||
return null;
|
||||
}
|
||||
|
@ -182,6 +190,7 @@ public interface IValidationSupport {
|
|||
/**
|
||||
* Fetch the given ValueSet by URL
|
||||
*/
|
||||
@Nullable
|
||||
default IBaseResource fetchValueSet(String theValueSetUrl) {
|
||||
return null;
|
||||
}
|
||||
|
@ -199,6 +208,7 @@ public interface IValidationSupport {
|
|||
* @param theDisplay The display name, if it should also be validated
|
||||
* @return Returns a validation result object
|
||||
*/
|
||||
@Nullable
|
||||
default CodeValidationResult validateCode(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
|
||||
return null;
|
||||
}
|
||||
|
@ -216,6 +226,7 @@ public interface IValidationSupport {
|
|||
* @param theValueSet The ValueSet to validate against. Must not be null, and must be a ValueSet resource.
|
||||
* @return Returns a validation result object, or <code>null</code> if this validation support module can not handle this kind of request
|
||||
*/
|
||||
@Nullable
|
||||
default CodeValidationResult validateCodeInValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) {
|
||||
return null;
|
||||
}
|
||||
|
@ -228,6 +239,7 @@ public interface IValidationSupport {
|
|||
* @param theSystem The CodeSystem URL
|
||||
* @param theCode The code
|
||||
*/
|
||||
@Nullable
|
||||
default LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode) {
|
||||
return null;
|
||||
}
|
||||
|
@ -251,6 +263,7 @@ public interface IValidationSupport {
|
|||
* other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
|
||||
* @return Returns null if this module does not know how to handle this request
|
||||
*/
|
||||
@Nullable
|
||||
default IBaseResource generateSnapshot(ValidationSupportContext theValidationSupportContext, IBaseResource theInput, String theUrl, String theWebUrl, String theProfileName) {
|
||||
return null;
|
||||
}
|
||||
|
@ -268,6 +281,14 @@ public interface IValidationSupport {
|
|||
// nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to translate the given concept from one code system to another
|
||||
*/
|
||||
@Nullable
|
||||
default TranslateConceptResults translateConcept(TranslateCodeRequest theRequest) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
enum IssueSeverity {
|
||||
/**
|
||||
|
@ -289,6 +310,7 @@ public interface IValidationSupport {
|
|||
}
|
||||
|
||||
class ConceptDesignation {
|
||||
|
||||
private String myLanguage;
|
||||
private String myUseSystem;
|
||||
private String myUseCode;
|
||||
|
@ -710,4 +732,62 @@ public interface IValidationSupport {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
class TranslateCodeRequest {
|
||||
private final String mySourceSystemUrl;
|
||||
private final String mySourceCode;
|
||||
private final String myTargetSystemUrl;
|
||||
private final int myHashCode;
|
||||
|
||||
public TranslateCodeRequest(String theSourceSystemUrl, String theSourceCode, String theTargetSystemUrl) {
|
||||
mySourceSystemUrl = theSourceSystemUrl;
|
||||
mySourceCode = theSourceCode;
|
||||
myTargetSystemUrl = theTargetSystemUrl;
|
||||
|
||||
myHashCode = new HashCodeBuilder(17, 37)
|
||||
.append(mySourceSystemUrl)
|
||||
.append(mySourceCode)
|
||||
.append(myTargetSystemUrl)
|
||||
.toHashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object theO) {
|
||||
if (this == theO) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (theO == null || getClass() != theO.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TranslateCodeRequest that = (TranslateCodeRequest) theO;
|
||||
|
||||
return new EqualsBuilder()
|
||||
.append(mySourceSystemUrl, that.mySourceSystemUrl)
|
||||
.append(mySourceCode, that.mySourceCode)
|
||||
.append(myTargetSystemUrl, that.myTargetSystemUrl)
|
||||
.isEquals();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return myHashCode;
|
||||
}
|
||||
|
||||
public String getSourceSystemUrl() {
|
||||
return mySourceSystemUrl;
|
||||
}
|
||||
|
||||
public String getSourceCode() {
|
||||
return mySourceCode;
|
||||
}
|
||||
|
||||
public String getTargetSystemUrl() {
|
||||
return myTargetSystemUrl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,154 @@
|
|||
package ca.uhn.fhir.context.support;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2021 Smile CDR, Inc.
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
public class TranslateConceptResult {
|
||||
private String mySystem;
|
||||
private String myCode;
|
||||
private String myDisplay;
|
||||
private String myEquivalence;
|
||||
private String myConceptMapUrl;
|
||||
private String myValueSet;
|
||||
private String mySystemVersion;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public TranslateConceptResult() {
|
||||
super();
|
||||
}
|
||||
|
||||
public String getSystem() {
|
||||
return mySystem;
|
||||
}
|
||||
|
||||
public TranslateConceptResult setSystem(String theSystem) {
|
||||
mySystem = theSystem;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
|
||||
.append("system", mySystem)
|
||||
.append("code", myCode)
|
||||
.append("display", myDisplay)
|
||||
.append("equivalence", myEquivalence)
|
||||
.append("conceptMapUrl", myConceptMapUrl)
|
||||
.append("valueSet", myValueSet)
|
||||
.append("systemVersion", mySystemVersion)
|
||||
.toString();
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return myCode;
|
||||
}
|
||||
|
||||
public TranslateConceptResult setCode(String theCode) {
|
||||
myCode = theCode;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getDisplay() {
|
||||
return myDisplay;
|
||||
}
|
||||
|
||||
public TranslateConceptResult setDisplay(String theDisplay) {
|
||||
myDisplay = theDisplay;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getEquivalence() {
|
||||
return myEquivalence;
|
||||
}
|
||||
|
||||
public TranslateConceptResult setEquivalence(String theEquivalence) {
|
||||
myEquivalence = theEquivalence;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getSystemVersion() {
|
||||
return mySystemVersion;
|
||||
}
|
||||
|
||||
public void setSystemVersion(String theSystemVersion) {
|
||||
mySystemVersion = theSystemVersion;
|
||||
}
|
||||
|
||||
public String getValueSet() {
|
||||
return myValueSet;
|
||||
}
|
||||
|
||||
public TranslateConceptResult setValueSet(String theValueSet) {
|
||||
myValueSet = theValueSet;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getConceptMapUrl() {
|
||||
return myConceptMapUrl;
|
||||
}
|
||||
|
||||
public TranslateConceptResult setConceptMapUrl(String theConceptMapUrl) {
|
||||
myConceptMapUrl = theConceptMapUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object theO) {
|
||||
if (this == theO) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (theO == null || getClass() != theO.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TranslateConceptResult that = (TranslateConceptResult) theO;
|
||||
|
||||
EqualsBuilder b = new EqualsBuilder();
|
||||
b.append(mySystem, that.mySystem);
|
||||
b.append(myCode, that.myCode);
|
||||
b.append(myDisplay, that.myDisplay);
|
||||
b.append(myEquivalence, that.myEquivalence);
|
||||
b.append(myConceptMapUrl, that.myConceptMapUrl);
|
||||
b.append(myValueSet, that.myValueSet);
|
||||
b.append(mySystemVersion, that.mySystemVersion);
|
||||
return b.isEquals();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
HashCodeBuilder b = new HashCodeBuilder(17, 37);
|
||||
b.append(mySystem);
|
||||
b.append(myCode);
|
||||
b.append(myDisplay);
|
||||
b.append(myEquivalence);
|
||||
b.append(myConceptMapUrl);
|
||||
b.append(myValueSet);
|
||||
b.append(mySystemVersion);
|
||||
return b.toHashCode();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
package ca.uhn.fhir.context.support;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2021 Smile CDR, Inc.
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class TranslateConceptResults {
|
||||
private List<TranslateConceptResult> myResults;
|
||||
private String myMessage;
|
||||
private boolean myResult;
|
||||
|
||||
public TranslateConceptResults() {
|
||||
super();
|
||||
myResults = new ArrayList<>();
|
||||
}
|
||||
|
||||
public List<TranslateConceptResult> getResults() {
|
||||
return myResults;
|
||||
}
|
||||
|
||||
public void setResults(List<TranslateConceptResult> theResults) {
|
||||
myResults = theResults;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return myMessage;
|
||||
}
|
||||
|
||||
public void setMessage(String theMessage) {
|
||||
myMessage = theMessage;
|
||||
}
|
||||
|
||||
public boolean getResult() {
|
||||
return myResult;
|
||||
}
|
||||
|
||||
public void setResult(boolean theMatched) {
|
||||
myResult = theMatched;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return getResults().size();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return getResults().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object theO) {
|
||||
if (this == theO) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (theO == null || getClass() != theO.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TranslateConceptResults that = (TranslateConceptResults) theO;
|
||||
|
||||
EqualsBuilder b = new EqualsBuilder();
|
||||
b.append(myResults, that.myResults);
|
||||
return b.isEquals();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
HashCodeBuilder b = new HashCodeBuilder(17, 37);
|
||||
b.append(myResults);
|
||||
return b.toHashCode();
|
||||
}
|
||||
}
|
|
@ -32,7 +32,7 @@ import java.util.List;
|
|||
* THIS API IS EXPERIMENTAL IN HAPI FHIR - USE WITH CAUTION AS THE PUBLISHED API MAY
|
||||
* CHANGE
|
||||
*
|
||||
* @see FhirTerser#visit(IBaseResource, IModelVisitor2)
|
||||
* @see FhirTerser#visit(IBase, IModelVisitor2)
|
||||
*/
|
||||
public interface IModelVisitor2 {
|
||||
|
||||
|
|
|
@ -131,12 +131,9 @@ ca.uhn.fhir.jpa.dao.LegacySearchBuilder.sourceParamDisabled=The _source paramete
|
|||
ca.uhn.fhir.jpa.dao.LegacySearchBuilder.invalidCodeMissingSystem=Invalid token specified for parameter {0} - No system specified: {1}|{2}
|
||||
ca.uhn.fhir.jpa.dao.LegacySearchBuilder.invalidCodeMissingCode=Invalid token specified for parameter {0} - No code specified: {1}|{2}
|
||||
|
||||
ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoConceptMapDstu3.matchesFound=Matches found!
|
||||
ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoConceptMapDstu3.noMatchesFound=No matches found!
|
||||
ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoConceptMapR4.matchesFound=Matches found!
|
||||
ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoConceptMapR4.noMatchesFound=No matches found!
|
||||
ca.uhn.fhir.jpa.dao.r5.FhirResourceDaoConceptMapR5.matchesFound=Matches found!
|
||||
ca.uhn.fhir.jpa.dao.r5.FhirResourceDaoConceptMapR5.noMatchesFound=No matches found!
|
||||
ca.uhn.fhir.jpa.term.TermConceptMappingSvcImpl.matchesFound=Matches found
|
||||
ca.uhn.fhir.jpa.term.TermConceptMappingSvcImpl.noMatchesFound=No Matches found
|
||||
|
||||
ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoSearchParameterR4.invalidSearchParamExpression=The expression "{0}" can not be evaluated and may be invalid: {1}
|
||||
|
||||
ca.uhn.fhir.jpa.dao.predicate.PredicateBuilderToken.textModifierDisabledForSearchParam=The :text modifier is disabled for this search parameter
|
||||
|
|
|
@ -3,14 +3,14 @@
|
|||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-bom</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>HAPI FHIR BOM</name>
|
||||
|
||||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-cli</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../../hapi-deployable-pom</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@ -78,13 +78,13 @@
|
|||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-structures-dstu2</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-jpaserver-subscription</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
@ -101,7 +101,7 @@
|
|||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-testpage-overlay</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<classifier>classes</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
|
|
@ -21,6 +21,7 @@ package ca.uhn.hapi.fhir.docs;
|
|||
*/
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.*;
|
||||
|
@ -223,4 +224,25 @@ public class ServletExamples {
|
|||
}
|
||||
// END SNIPPET: corsInterceptor
|
||||
|
||||
|
||||
@WebServlet(urlPatterns = { "/fhir/*" }, displayName = "FHIR Server")
|
||||
public class RestfulServerWithResponseTerminologyTranslationInterceptor extends RestfulServer {
|
||||
|
||||
private IValidationSupport myValidationSupport;
|
||||
|
||||
@Override
|
||||
protected void initialize() throws ServletException {
|
||||
// START SNIPPET: ResponseTerminologyTranslationInterceptor
|
||||
|
||||
// Create an interceptor that will map from a proprietary CodeSystem to LOINC
|
||||
ResponseTerminologyTranslationInterceptor interceptor = new ResponseTerminologyTranslationInterceptor(myValidationSupport);
|
||||
interceptor.addMappingSpecification("http://examplelabs.org", "http://loinc.org");
|
||||
|
||||
// Register the interceptor
|
||||
registerInterceptor(interceptor);
|
||||
|
||||
// END SNIPPET: ResponseTerminologyTranslationInterceptor
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
type: add
|
||||
issue: 2488
|
||||
title: "Two new server interceptors have been added that can be used to map codes and populate code display names respectively
|
||||
using the server terminology services."
|
|
@ -193,6 +193,54 @@ If you wish to override the value of `Resource.meta.source` using the value supp
|
|||
* [CaptureResourceSourceFromHeaderInterceptor JavaDoc](/apidocs/hapi-fhir-server/ca/uhn/fhir/rest/server/interceptor/CaptureResourceSourceFromHeaderInterceptor.html)
|
||||
* [CaptureResourceSourceFromHeaderInterceptor Source](https://github.com/hapifhir/hapi-fhir/blob/master/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/CaptureResourceSourceFromHeaderInterceptor.java)
|
||||
|
||||
|
||||
# Terminology: Map Response Terminology
|
||||
|
||||
A common problem when implementing FHIR APIs is the challenge of how to return coded data using standard vocabularies when your source data is not modelled using these vocabularies. For example, suppose you want to implement support for an Implementation Guide that mandates the use of [LOINC](https://loinc.org) but your source data uses local/proprietary observation codes.
|
||||
|
||||
One solution is to simply apply mappings and add them to the FHIR data you are storing in your repository as you are storing it. This solution, often called *Mapping on the Way In*, will work but it has potential pitfalls including:
|
||||
|
||||
* All mappings must be known at the time the data is being stored.
|
||||
* If mappings change because of mistakes or new information, updating existing data is difficult.
|
||||
|
||||
A potentially better solution is to apply *Mapping on the Way Out*, meaning that your mappings are stored in a central spot and applied at runtime to data as it is leaving your system. HAPI FHIR supplies an interceptor called the ResponseTerminologyTranslationInterceptor that can help with this.
|
||||
|
||||
* [ResponseTerminologyTranslationInterceptor JavaDoc](/apidocs/hapi-fhir-server/ca/uhn/fhir/rest/server/interceptor/ResponseTerminologyTranslationInterceptor.html)
|
||||
* [ResponseTerminologyTranslationInterceptor Source](https://github.com/hapifhir/hapi-fhir/blob/master/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseTerminologyTranslationInterceptor.java)
|
||||
|
||||
This interceptor uses ConceptMap resources that are stored in your system, looking up mappings for CodeableConcept codings in your resources and adding them to the responses.
|
||||
|
||||
The following code snippet shows a simple example of how to create and configure this interceptor.
|
||||
|
||||
```java
|
||||
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/ServletExamples.java|ResponseTerminologyTranslationInterceptor}}
|
||||
```
|
||||
|
||||
## Limitations
|
||||
|
||||
The following limitations will hopefully be resolved in the future:
|
||||
|
||||
This interceptor currently only works when registered against a RestfulServer backed by the HAPI FHIR JPA server.
|
||||
|
||||
This interceptor only modifies responses to FHIR read/vread/search/history operations. Responses to these operations are not modified if they are found within a FHIR transaction operation.
|
||||
|
||||
|
||||
# Terminology: Populate Code Display Names
|
||||
|
||||
The HAPI FHIR ResponseTerminologyDisplayPopulationInterceptor interceptor looks for Coding elements within responses where the `Coding.system` and `Coding.code` values are populated but the `Coding.display` is not. The interceptor will attempt to resolve the correct display using the validation support module and will add it to the Coding display value if one is found.
|
||||
|
||||
* [ResponseTerminologyDisplayPopulationInterceptor JavaDoc](/apidocs/hapi-fhir-server/ca/uhn/fhir/rest/server/interceptor/ResponseTerminologyDisplayPopulationInterceptor.html)
|
||||
* [ResponseTerminologyDisplayPopulationInterceptor Source](https://github.com/hapifhir/hapi-fhir/blob/master/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseTerminologyDisplayPopulationInterceptor.java)
|
||||
|
||||
This interceptor uses ConceptMap resources that are stored in your system, looking up mappings for CodeableConcept codings in your resources and adding them to the responses.
|
||||
|
||||
## Limitations
|
||||
|
||||
The following limitation will hopefully be resolved in the future:
|
||||
|
||||
This interceptor only modifies responses to FHIR read/vread/search/history operations. Responses to these operations are not modified if they are found within a FHIR transaction operation.
|
||||
|
||||
|
||||
# Utility: ResponseSizeCapturingInterceptor
|
||||
|
||||
The ResponseSizeCapturingInterceptor can be used to capture the number of characters written in each HTTP FHIR response.
|
||||
|
@ -229,6 +277,8 @@ The UserRequestRetryVersionConflictsInterceptor allows clients to request that t
|
|||
The RepositoryValidatingInterceptor can be used to enforce validation rules on data stored in a HAPI FHIR JPA Repository. See [Repository Validating Interceptor](/docs/validation/repository_validating_interceptor.html) for more information.
|
||||
|
||||
|
||||
|
||||
|
||||
# Data Standardization
|
||||
|
||||
```StandardizingInterceptor``` handles data standardization (s13n) requirements. This interceptor applies standardization rules on all FHIR primitives as configured in the ```s13n.json``` file that should be made available on the classpath. This file contains FHIRPath definitions together with the standardizers that should be applied to that path. It comes with six per-build standardizers: NAME_FAMILY, NAME_GIVEN, EMAIL, TITLE, PHONE and TEXT. Custom standardizers can be developed by implementing ```ca.uhn.fhir.rest.server.interceptor.s13n.standardizers.IStandardizer``` interface.
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import org.hl7.fhir.r4.model.Bundle;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
@ -93,6 +94,7 @@ public class DaoConfig {
|
|||
/**
|
||||
* update setter javadoc if default changes
|
||||
*/
|
||||
@Nonnull
|
||||
private Long myTranslationCachesExpireAfterWriteInMinutes = DEFAULT_TRANSLATION_CACHES_EXPIRE_AFTER_WRITE_IN_MINUTES;
|
||||
/**
|
||||
* update setter javadoc if default changes
|
||||
|
@ -884,6 +886,7 @@ public class DaoConfig {
|
|||
* Specifies the duration in minutes for which values will be retained after being
|
||||
* written to the terminology translation cache. Defaults to 60.
|
||||
*/
|
||||
@Nonnull
|
||||
public Long getTranslationCachesExpireAfterWriteInMinutes() {
|
||||
return myTranslationCachesExpireAfterWriteInMinutes;
|
||||
}
|
||||
|
|
|
@ -21,10 +21,10 @@ package ca.uhn.fhir.jpa.api.dao;
|
|||
*/
|
||||
|
||||
import ca.uhn.fhir.jpa.api.model.TranslationRequest;
|
||||
import ca.uhn.fhir.jpa.api.model.TranslationResult;
|
||||
import ca.uhn.fhir.context.support.TranslateConceptResults;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
public interface IFhirResourceDaoConceptMap<T extends IBaseResource> extends IFhirResourceDao<T> {
|
||||
TranslationResult translate(TranslationRequest theTranslationRequest, RequestDetails theRequestDetails);
|
||||
TranslateConceptResults translate(TranslationRequest theTranslationRequest, RequestDetails theRequestDetails);
|
||||
}
|
||||
|
|
|
@ -1,74 +0,0 @@
|
|||
package ca.uhn.fhir.jpa.api.model;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA API
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2021 Smile CDR, Inc.
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import org.hl7.fhir.r4.model.CodeType;
|
||||
import org.hl7.fhir.r4.model.Coding;
|
||||
import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent;
|
||||
import org.hl7.fhir.r4.model.UriType;
|
||||
|
||||
public class TranslationMatch {
|
||||
private Coding myConcept;
|
||||
private CodeType myEquivalence;
|
||||
private UriType mySource;
|
||||
|
||||
public TranslationMatch() {
|
||||
super();
|
||||
}
|
||||
|
||||
public Coding getConcept() {
|
||||
return myConcept;
|
||||
}
|
||||
|
||||
public void setConcept(Coding theConcept) {
|
||||
myConcept = theConcept;
|
||||
}
|
||||
|
||||
public CodeType getEquivalence() {
|
||||
return myEquivalence;
|
||||
}
|
||||
|
||||
public void setEquivalence(CodeType theEquivalence) {
|
||||
myEquivalence = theEquivalence;
|
||||
}
|
||||
|
||||
public UriType getSource() {
|
||||
return mySource;
|
||||
}
|
||||
|
||||
public void setSource(UriType theSource) {
|
||||
mySource = theSource;
|
||||
}
|
||||
|
||||
public void toParameterParts(ParametersParameterComponent theParam) {
|
||||
if (myEquivalence != null) {
|
||||
theParam.addPart().setName("equivalence").setValue(myEquivalence);
|
||||
}
|
||||
|
||||
if (myConcept != null) {
|
||||
theParam.addPart().setName("concept").setValue(myConcept);
|
||||
}
|
||||
|
||||
if (mySource != null) {
|
||||
theParam.addPart().setName("source").setValue(mySource);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
package ca.uhn.fhir.jpa.api.model;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA API
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2021 Smile CDR, Inc.
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import org.hl7.fhir.r4.model.BooleanType;
|
||||
import org.hl7.fhir.r4.model.Parameters;
|
||||
import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class TranslationResult {
|
||||
private List<TranslationMatch> myMatches;
|
||||
private StringType myMessage;
|
||||
private BooleanType myResult;
|
||||
|
||||
public TranslationResult() {
|
||||
super();
|
||||
|
||||
myMatches = new ArrayList<>();
|
||||
}
|
||||
|
||||
public List<TranslationMatch> getMatches() {
|
||||
return myMatches;
|
||||
}
|
||||
|
||||
public void setMatches(List<TranslationMatch> theMatches) {
|
||||
myMatches = theMatches;
|
||||
}
|
||||
|
||||
public boolean addMatch(TranslationMatch theMatch) {
|
||||
return myMatches.add(theMatch);
|
||||
}
|
||||
|
||||
public StringType getMessage() {
|
||||
return myMessage;
|
||||
}
|
||||
|
||||
public void setMessage(StringType theMessage) {
|
||||
myMessage = theMessage;
|
||||
}
|
||||
|
||||
public BooleanType getResult() {
|
||||
return myResult;
|
||||
}
|
||||
|
||||
public void setResult(BooleanType theMatched) {
|
||||
myResult = theMatched;
|
||||
}
|
||||
|
||||
public Parameters toParameters() {
|
||||
Parameters retVal = new Parameters();
|
||||
|
||||
if (myResult != null) {
|
||||
retVal.addParameter().setName("result").setValue(myResult);
|
||||
}
|
||||
|
||||
if (myMessage != null) {
|
||||
retVal.addParameter().setName("message").setValue(myMessage);
|
||||
}
|
||||
|
||||
for (TranslationMatch translationMatch : myMatches) {
|
||||
ParametersParameterComponent matchParam = retVal.addParameter().setName("match");
|
||||
translationMatch.toParameterParts(matchParam);
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package ca.uhn.fhir.jpa.config;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.i18n.HapiLocalizer;
|
||||
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
|
||||
import ca.uhn.fhir.interceptor.api.IInterceptorService;
|
||||
|
@ -60,6 +61,7 @@ import ca.uhn.fhir.jpa.graphql.JpaStorageServices;
|
|||
import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor;
|
||||
import ca.uhn.fhir.jpa.interceptor.JpaConsentContextServices;
|
||||
import ca.uhn.fhir.jpa.interceptor.OverridePathBasedReferentialIntegrityForDeletesInterceptor;
|
||||
import ca.uhn.fhir.rest.server.interceptor.ResponseTerminologyTranslationInterceptor;
|
||||
import ca.uhn.fhir.jpa.interceptor.validation.RepositoryValidatingRuleBuilder;
|
||||
import ca.uhn.fhir.jpa.model.sched.ISchedulerService;
|
||||
import ca.uhn.fhir.jpa.packages.IHapiPackageCacheManager;
|
||||
|
@ -119,6 +121,8 @@ import ca.uhn.fhir.jpa.searchparam.extractor.IResourceLinkResolver;
|
|||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamProvider;
|
||||
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
|
||||
import ca.uhn.fhir.jpa.sp.SearchParamPresenceSvcImpl;
|
||||
import ca.uhn.fhir.jpa.term.TermConceptMappingSvcImpl;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermConceptMappingSvc;
|
||||
import ca.uhn.fhir.jpa.util.MemoryCacheService;
|
||||
import ca.uhn.fhir.jpa.validation.JpaResourceLoader;
|
||||
import ca.uhn.fhir.jpa.validation.ValidationSettings;
|
||||
|
@ -251,6 +255,13 @@ public abstract class BaseConfig {
|
|||
return new CascadingDeleteInterceptor(theFhirContext, theDaoRegistry, theInterceptorBroadcaster);
|
||||
}
|
||||
|
||||
|
||||
@Lazy
|
||||
@Bean
|
||||
public ResponseTerminologyTranslationInterceptor responseTerminologyTranslationInterceptor(IValidationSupport theValidationSupport) {
|
||||
return new ResponseTerminologyTranslationInterceptor(theValidationSupport);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method should be overridden to provide an actual completed
|
||||
* bean, but it provides a partially completed entity manager
|
||||
|
@ -336,6 +347,11 @@ public abstract class BaseConfig {
|
|||
return new DatabaseSearchResultCacheSvcImpl();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ITermConceptMappingSvc termConceptMappingSvc() {
|
||||
return new TermConceptMappingSvcImpl();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ThreadPoolTaskExecutor searchCoordinatorThreadFactory() {
|
||||
final ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
|
||||
|
|
|
@ -82,7 +82,12 @@ public abstract class BaseConfigDstu3Plus extends BaseConfig {
|
|||
@Primary
|
||||
@Bean
|
||||
public IValidationSupport validationSupportChain() {
|
||||
return new CachingValidationSupport(jpaValidationSupportChain());
|
||||
|
||||
// Short timeout for code translation because TermConceptMappingSvcImpl has its own caching
|
||||
CachingValidationSupport.CacheTimeouts cacheTimeouts = CachingValidationSupport.CacheTimeouts.defaultValues()
|
||||
.setTranslateCodeMillis(1000);
|
||||
|
||||
return new CachingValidationSupport(jpaValidationSupportChain(), cacheTimeouts);
|
||||
}
|
||||
|
||||
@Bean(name = "myInstanceValidator")
|
||||
|
|
|
@ -21,147 +21,38 @@ package ca.uhn.fhir.jpa.dao.dstu3;
|
|||
*/
|
||||
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoConceptMap;
|
||||
import ca.uhn.fhir.jpa.api.model.TranslationMatch;
|
||||
import ca.uhn.fhir.jpa.api.model.TranslationRequest;
|
||||
import ca.uhn.fhir.jpa.api.model.TranslationResult;
|
||||
import ca.uhn.fhir.context.support.TranslateConceptResults;
|
||||
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElement;
|
||||
import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElementTarget;
|
||||
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermConceptMappingSvc;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import org.hl7.fhir.dstu3.model.ConceptMap;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4.model.BooleanType;
|
||||
import org.hl7.fhir.r4.model.CodeType;
|
||||
import org.hl7.fhir.r4.model.Coding;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
import org.hl7.fhir.r4.model.UriType;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.hl7.fhir.convertors.conv30_40.ConceptMap30_40.convertConceptMap;
|
||||
|
||||
public class FhirResourceDaoConceptMapDstu3 extends BaseHapiFhirResourceDao<ConceptMap> implements IFhirResourceDaoConceptMap<ConceptMap> {
|
||||
@Autowired
|
||||
private ITermReadSvc myHapiTerminologySvc;
|
||||
private ITermConceptMappingSvc myTermConceptMappingSvc;
|
||||
|
||||
@Override
|
||||
public TranslationResult translate(TranslationRequest theTranslationRequest, RequestDetails theRequestDetails) {
|
||||
public TranslateConceptResults translate(TranslationRequest theTranslationRequest, RequestDetails theRequestDetails) {
|
||||
if (theTranslationRequest.hasReverse() && theTranslationRequest.getReverseAsBoolean()) {
|
||||
return buildReverseTranslationResult(myHapiTerminologySvc.translateWithReverse(theTranslationRequest));
|
||||
return myTermConceptMappingSvc.translateWithReverse(theTranslationRequest);
|
||||
}
|
||||
|
||||
return buildTranslationResult(myHapiTerminologySvc.translate(theTranslationRequest));
|
||||
return myTermConceptMappingSvc.translate(theTranslationRequest);
|
||||
}
|
||||
|
||||
private TranslationResult buildTranslationResult(List<TermConceptMapGroupElementTarget> theTargets) {
|
||||
TranslationResult retVal = new TranslationResult();
|
||||
|
||||
String msg;
|
||||
if (theTargets.isEmpty()) {
|
||||
|
||||
retVal.setResult(new BooleanType(false));
|
||||
|
||||
msg = getContext().getLocalizer().getMessage(
|
||||
FhirResourceDaoConceptMapDstu3.class,
|
||||
"noMatchesFound");
|
||||
|
||||
retVal.setMessage(new StringType(msg));
|
||||
|
||||
} else {
|
||||
|
||||
retVal.setResult(new BooleanType(true));
|
||||
|
||||
msg = getContext().getLocalizer().getMessage(
|
||||
FhirResourceDaoConceptMapDstu3.class,
|
||||
"matchesFound");
|
||||
|
||||
retVal.setMessage(new StringType(msg));
|
||||
|
||||
TranslationMatch translationMatch;
|
||||
Set<TermConceptMapGroupElementTarget> targetsToReturn = new HashSet<>();
|
||||
for (TermConceptMapGroupElementTarget target : theTargets) {
|
||||
if (targetsToReturn.add(target)) {
|
||||
translationMatch = new TranslationMatch();
|
||||
|
||||
if (target.getEquivalence() != null) {
|
||||
translationMatch.setEquivalence(new CodeType(target.getEquivalence().toCode()));
|
||||
}
|
||||
|
||||
translationMatch.setConcept(
|
||||
new Coding()
|
||||
.setCode(target.getCode())
|
||||
.setSystem(target.getSystem())
|
||||
.setVersion(target.getSystemVersion())
|
||||
.setDisplay(target.getDisplay())
|
||||
);
|
||||
|
||||
translationMatch.setSource(new UriType(target.getConceptMapUrl()));
|
||||
|
||||
retVal.addMatch(translationMatch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private TranslationResult buildReverseTranslationResult(List<TermConceptMapGroupElement> theElements) {
|
||||
TranslationResult retVal = new TranslationResult();
|
||||
|
||||
String msg;
|
||||
if (theElements.isEmpty()) {
|
||||
|
||||
retVal.setResult(new BooleanType(false));
|
||||
|
||||
msg = getContext().getLocalizer().getMessage(
|
||||
FhirResourceDaoConceptMapDstu3.class,
|
||||
"noMatchesFound");
|
||||
|
||||
retVal.setMessage(new StringType(msg));
|
||||
|
||||
} else {
|
||||
|
||||
retVal.setResult(new BooleanType(true));
|
||||
|
||||
msg = getContext().getLocalizer().getMessage(
|
||||
FhirResourceDaoConceptMapDstu3.class,
|
||||
"matchesFound");
|
||||
|
||||
retVal.setMessage(new StringType(msg));
|
||||
|
||||
TranslationMatch translationMatch;
|
||||
Set<TermConceptMapGroupElement> elementsToReturn = new HashSet<>();
|
||||
for (TermConceptMapGroupElement element : theElements) {
|
||||
if (elementsToReturn.add(element)) {
|
||||
translationMatch = new TranslationMatch();
|
||||
|
||||
translationMatch.setConcept(
|
||||
new Coding()
|
||||
.setCode(element.getCode())
|
||||
.setSystem(element.getSystem())
|
||||
.setVersion(element.getSystemVersion())
|
||||
.setDisplay(element.getDisplay())
|
||||
);
|
||||
|
||||
translationMatch.setSource(new UriType(element.getConceptMapUrl()));
|
||||
|
||||
retVal.addMatch(translationMatch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceTable updateEntity(RequestDetails theRequestDetails, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
|
||||
|
@ -173,12 +64,12 @@ public class FhirResourceDaoConceptMapDstu3 extends BaseHapiFhirResourceDao<Conc
|
|||
try {
|
||||
ConceptMap conceptMap = (ConceptMap) theResource;
|
||||
org.hl7.fhir.r4.model.ConceptMap converted = convertConceptMap(conceptMap);
|
||||
myHapiTerminologySvc.storeTermConceptMapAndChildren(retVal, converted);
|
||||
myTermConceptMappingSvc.storeTermConceptMapAndChildren(retVal, converted);
|
||||
} catch (FHIRException fe) {
|
||||
throw new InternalErrorException(fe);
|
||||
}
|
||||
} else {
|
||||
myHapiTerminologySvc.deleteConceptMapAndChildren(retVal);
|
||||
myTermConceptMappingSvc.deleteConceptMapAndChildren(retVal);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,153 +21,31 @@ package ca.uhn.fhir.jpa.dao.r4;
|
|||
*/
|
||||
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoConceptMap;
|
||||
import ca.uhn.fhir.jpa.api.model.TranslationMatch;
|
||||
import ca.uhn.fhir.jpa.api.model.TranslationRequest;
|
||||
import ca.uhn.fhir.jpa.api.model.TranslationResult;
|
||||
import ca.uhn.fhir.context.support.TranslateConceptResults;
|
||||
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElement;
|
||||
import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElementTarget;
|
||||
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermConceptMappingSvc;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
|
||||
import org.hl7.fhir.convertors.VersionConvertor_40_50;
|
||||
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4.model.BooleanType;
|
||||
import org.hl7.fhir.r4.model.CodeType;
|
||||
import org.hl7.fhir.r4.model.Coding;
|
||||
import org.hl7.fhir.r4.model.ConceptMap;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
import org.hl7.fhir.r4.model.UriType;
|
||||
import org.hl7.fhir.r4.model.Enumerations.ConceptMapEquivalence;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class FhirResourceDaoConceptMapR4 extends BaseHapiFhirResourceDao<ConceptMap> implements IFhirResourceDaoConceptMap<ConceptMap> {
|
||||
@Autowired
|
||||
private ITermReadSvc myHapiTerminologySvc;
|
||||
private ITermConceptMappingSvc myTermConceptMappingSvc;
|
||||
|
||||
@Override
|
||||
public TranslationResult translate(TranslationRequest theTranslationRequest, RequestDetails theRequestDetails) {
|
||||
public TranslateConceptResults translate(TranslationRequest theTranslationRequest, RequestDetails theRequestDetails) {
|
||||
if (theTranslationRequest.hasReverse() && theTranslationRequest.getReverseAsBoolean()) {
|
||||
return buildReverseTranslationResult(myHapiTerminologySvc.translateWithReverse(theTranslationRequest));
|
||||
return myTermConceptMappingSvc.translateWithReverse(theTranslationRequest);
|
||||
}
|
||||
|
||||
return buildTranslationResult(myHapiTerminologySvc.translate(theTranslationRequest));
|
||||
}
|
||||
|
||||
private TranslationResult buildTranslationResult(List<TermConceptMapGroupElementTarget> theTargets) {
|
||||
TranslationResult retVal = new TranslationResult();
|
||||
|
||||
String msg;
|
||||
if (theTargets.isEmpty()) {
|
||||
|
||||
retVal.setResult(new BooleanType(false));
|
||||
|
||||
msg = getContext().getLocalizer().getMessage(
|
||||
FhirResourceDaoConceptMapR4.class,
|
||||
"noMatchesFound");
|
||||
|
||||
retVal.setMessage(new StringType(msg));
|
||||
|
||||
} else {
|
||||
|
||||
retVal.setResult(new BooleanType(true));
|
||||
|
||||
msg = getContext().getLocalizer().getMessage(
|
||||
FhirResourceDaoConceptMapR4.class,
|
||||
"matchesFound");
|
||||
|
||||
retVal.setMessage(new StringType(msg));
|
||||
|
||||
TranslationMatch translationMatch;
|
||||
Set<TermConceptMapGroupElementTarget> targetsToReturn = new HashSet<>();
|
||||
for (TermConceptMapGroupElementTarget target : theTargets) {
|
||||
if (targetsToReturn.add(target)) {
|
||||
translationMatch = new TranslationMatch();
|
||||
|
||||
if (target.getEquivalence() != null) {
|
||||
translationMatch.setEquivalence(new CodeType(target.getEquivalence().toCode()));
|
||||
}
|
||||
|
||||
translationMatch.setConcept(
|
||||
new Coding()
|
||||
.setCode(target.getCode())
|
||||
.setSystem(target.getSystem())
|
||||
.setVersion(target.getSystemVersion())
|
||||
.setDisplay(target.getDisplay())
|
||||
);
|
||||
|
||||
translationMatch.setSource(new UriType(target.getConceptMapUrl()));
|
||||
|
||||
retVal.addMatch(translationMatch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private TranslationResult buildReverseTranslationResult(List<TermConceptMapGroupElement> theElements) {
|
||||
TranslationResult retVal = new TranslationResult();
|
||||
|
||||
String msg;
|
||||
if (theElements.isEmpty()) {
|
||||
|
||||
retVal.setResult(new BooleanType(false));
|
||||
|
||||
msg = getContext().getLocalizer().getMessage(
|
||||
FhirResourceDaoConceptMapR4.class,
|
||||
"noMatchesFound");
|
||||
|
||||
retVal.setMessage(new StringType(msg));
|
||||
|
||||
} else {
|
||||
|
||||
retVal.setResult(new BooleanType(true));
|
||||
|
||||
msg = getContext().getLocalizer().getMessage(
|
||||
FhirResourceDaoConceptMapR4.class,
|
||||
"matchesFound");
|
||||
|
||||
retVal.setMessage(new StringType(msg));
|
||||
|
||||
TranslationMatch translationMatch;
|
||||
Set<TermConceptMapGroupElement> elementsToReturn = new HashSet<>();
|
||||
for (TermConceptMapGroupElement element : theElements) {
|
||||
if (elementsToReturn.add(element)) {
|
||||
translationMatch = new TranslationMatch();
|
||||
|
||||
translationMatch.setConcept(
|
||||
new Coding()
|
||||
.setCode(element.getCode())
|
||||
.setSystem(element.getSystem())
|
||||
.setVersion(element.getSystemVersion())
|
||||
.setDisplay(element.getDisplay())
|
||||
);
|
||||
|
||||
translationMatch.setSource(new UriType(element.getConceptMapUrl()));
|
||||
|
||||
if (element.getConceptMapGroupElementTargets().size() == 1) {
|
||||
|
||||
ConceptMapEquivalence eq = element.getConceptMapGroupElementTargets().get(0).getEquivalence();
|
||||
if (eq != null) {
|
||||
translationMatch.setEquivalence(new CodeType(eq.toCode()));
|
||||
}
|
||||
}
|
||||
|
||||
retVal.addMatch(translationMatch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
return myTermConceptMappingSvc.translate(theTranslationRequest);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -178,9 +56,9 @@ public class FhirResourceDaoConceptMapR4 extends BaseHapiFhirResourceDao<Concept
|
|||
if (!retVal.isUnchangedInCurrentOperation()) {
|
||||
if (retVal.getDeleted() == null) {
|
||||
ConceptMap conceptMap = (ConceptMap) theResource;
|
||||
myHapiTerminologySvc.storeTermConceptMapAndChildren(retVal, conceptMap);
|
||||
myTermConceptMappingSvc.storeTermConceptMapAndChildren(retVal, conceptMap);
|
||||
} else {
|
||||
myHapiTerminologySvc.deleteConceptMapAndChildren(retVal);
|
||||
myTermConceptMappingSvc.deleteConceptMapAndChildren(retVal);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,22 +1,6 @@
|
|||
package ca.uhn.fhir.jpa.dao.r5;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hl7.fhir.convertors.VersionConvertor_40_50;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4.model.Enumerations.ConceptMapEquivalence;
|
||||
import org.hl7.fhir.r5.model.BooleanType;
|
||||
import org.hl7.fhir.r5.model.CodeType;
|
||||
import org.hl7.fhir.r5.model.Coding;
|
||||
import org.hl7.fhir.r5.model.ConceptMap;
|
||||
import org.hl7.fhir.r5.model.StringType;
|
||||
import org.hl7.fhir.r5.model.UriType;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/*
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
|
@ -37,137 +21,31 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||
*/
|
||||
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoConceptMap;
|
||||
import ca.uhn.fhir.jpa.api.model.TranslationMatch;
|
||||
import ca.uhn.fhir.jpa.api.model.TranslationRequest;
|
||||
import ca.uhn.fhir.jpa.api.model.TranslationResult;
|
||||
import ca.uhn.fhir.context.support.TranslateConceptResults;
|
||||
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElement;
|
||||
import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElementTarget;
|
||||
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermConceptMappingSvc;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r5.model.ConceptMap;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public class FhirResourceDaoConceptMapR5 extends BaseHapiFhirResourceDao<ConceptMap> implements IFhirResourceDaoConceptMap<ConceptMap> {
|
||||
@Autowired
|
||||
private ITermReadSvc myHapiTerminologySvc;
|
||||
private ITermConceptMappingSvc myTermConceptMappingSvc;
|
||||
|
||||
@Override
|
||||
public TranslationResult translate(TranslationRequest theTranslationRequest, RequestDetails theRequestDetails) {
|
||||
public TranslateConceptResults translate(TranslationRequest theTranslationRequest, RequestDetails theRequestDetails) {
|
||||
if (theTranslationRequest.hasReverse() && theTranslationRequest.getReverseAsBoolean()) {
|
||||
return buildReverseTranslationResult(myHapiTerminologySvc.translateWithReverse(theTranslationRequest));
|
||||
return myTermConceptMappingSvc.translateWithReverse(theTranslationRequest);
|
||||
}
|
||||
|
||||
return buildTranslationResult(myHapiTerminologySvc.translate(theTranslationRequest));
|
||||
}
|
||||
|
||||
private TranslationResult buildTranslationResult(List<TermConceptMapGroupElementTarget> theTargets) {
|
||||
TranslationResult retVal = new TranslationResult();
|
||||
|
||||
String msg;
|
||||
if (theTargets.isEmpty()) {
|
||||
|
||||
retVal.setResult(VersionConvertor_40_50.convertBoolean(new BooleanType(false)));
|
||||
|
||||
msg = getContext().getLocalizer().getMessage(
|
||||
FhirResourceDaoConceptMapR5.class,
|
||||
"noMatchesFound");
|
||||
|
||||
retVal.setMessage(VersionConvertor_40_50.convertString(new StringType(msg)));
|
||||
|
||||
} else {
|
||||
|
||||
retVal.setResult(VersionConvertor_40_50.convertBoolean(new BooleanType(true)));
|
||||
|
||||
msg = getContext().getLocalizer().getMessage(
|
||||
FhirResourceDaoConceptMapR5.class,
|
||||
"matchesFound");
|
||||
|
||||
retVal.setMessage(VersionConvertor_40_50.convertString(new StringType(msg)));
|
||||
|
||||
TranslationMatch translationMatch;
|
||||
Set<TermConceptMapGroupElementTarget> targetsToReturn = new HashSet<>();
|
||||
for (TermConceptMapGroupElementTarget target : theTargets) {
|
||||
if (targetsToReturn.add(target)) {
|
||||
translationMatch = new TranslationMatch();
|
||||
|
||||
if (target.getEquivalence() != null) {
|
||||
translationMatch.setEquivalence(VersionConvertor_40_50.convertCode(new CodeType(target.getEquivalence().toCode())));
|
||||
}
|
||||
|
||||
translationMatch.setConcept(VersionConvertor_40_50.convertCoding(
|
||||
new Coding()
|
||||
.setCode(target.getCode())
|
||||
.setSystem(target.getSystem())
|
||||
.setVersion(target.getSystemVersion())
|
||||
.setDisplay(target.getDisplay())
|
||||
));
|
||||
|
||||
translationMatch.setSource(VersionConvertor_40_50.convertUri(new UriType(target.getConceptMapUrl())));
|
||||
|
||||
retVal.addMatch(translationMatch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private TranslationResult buildReverseTranslationResult(List<TermConceptMapGroupElement> theElements) {
|
||||
TranslationResult retVal = new TranslationResult();
|
||||
|
||||
String msg;
|
||||
if (theElements.isEmpty()) {
|
||||
|
||||
retVal.setResult(VersionConvertor_40_50.convertBoolean(new BooleanType(false)));
|
||||
|
||||
msg = getContext().getLocalizer().getMessage(
|
||||
FhirResourceDaoConceptMapR5.class,
|
||||
"noMatchesFound");
|
||||
|
||||
retVal.setMessage(VersionConvertor_40_50.convertString(new StringType(msg)));
|
||||
|
||||
} else {
|
||||
|
||||
retVal.setResult(VersionConvertor_40_50.convertBoolean(new BooleanType(true)));
|
||||
|
||||
msg = getContext().getLocalizer().getMessage(
|
||||
FhirResourceDaoConceptMapR5.class,
|
||||
"matchesFound");
|
||||
|
||||
retVal.setMessage(VersionConvertor_40_50.convertString(new StringType(msg)));
|
||||
|
||||
TranslationMatch translationMatch;
|
||||
Set<TermConceptMapGroupElement> elementsToReturn = new HashSet<>();
|
||||
for (TermConceptMapGroupElement element : theElements) {
|
||||
if (elementsToReturn.add(element)) {
|
||||
translationMatch = new TranslationMatch();
|
||||
|
||||
translationMatch.setConcept(VersionConvertor_40_50.convertCoding(
|
||||
new Coding()
|
||||
.setCode(element.getCode())
|
||||
.setSystem(element.getSystem())
|
||||
.setVersion(element.getSystemVersion())
|
||||
.setDisplay(element.getDisplay())
|
||||
));
|
||||
|
||||
translationMatch.setSource(VersionConvertor_40_50.convertUri(new UriType(element.getConceptMapUrl())));
|
||||
|
||||
if (element.getConceptMapGroupElementTargets().size() == 1) {
|
||||
|
||||
ConceptMapEquivalence eq = element.getConceptMapGroupElementTargets().get(0).getEquivalence();
|
||||
if (eq != null) {
|
||||
translationMatch.setEquivalence(VersionConvertor_40_50.convertCode(new CodeType(eq.toCode())));
|
||||
}
|
||||
}
|
||||
|
||||
retVal.addMatch(translationMatch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
return myTermConceptMappingSvc.translate(theTranslationRequest);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -178,9 +56,9 @@ public class FhirResourceDaoConceptMapR5 extends BaseHapiFhirResourceDao<Concept
|
|||
|
||||
if (retVal.getDeleted() == null) {
|
||||
ConceptMap conceptMap = (ConceptMap) theResource;
|
||||
myHapiTerminologySvc.storeTermConceptMapAndChildren(retVal, org.hl7.fhir.convertors.conv40_50.ConceptMap40_50.convertConceptMap(conceptMap));
|
||||
myTermConceptMappingSvc.storeTermConceptMapAndChildren(retVal, org.hl7.fhir.convertors.conv40_50.ConceptMap40_50.convertConceptMap(conceptMap));
|
||||
} else {
|
||||
myHapiTerminologySvc.deleteConceptMapAndChildren(retVal);
|
||||
myTermConceptMappingSvc.deleteConceptMapAndChildren(retVal);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -43,6 +43,13 @@ public class TermConceptMap implements Serializable {
|
|||
static final int MAX_URL_LENGTH = 200;
|
||||
public static final int MAX_VER_LENGTH = 200;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public TermConceptMap() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Id()
|
||||
@SequenceGenerator(name = "SEQ_CONCEPT_MAP_PID", sequenceName = "SEQ_CONCEPT_MAP_PID")
|
||||
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_CONCEPT_MAP_PID")
|
||||
|
|
|
@ -22,8 +22,9 @@ package ca.uhn.fhir.jpa.provider.dstu3;
|
|||
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoConceptMap;
|
||||
import ca.uhn.fhir.jpa.api.model.TranslationRequest;
|
||||
import ca.uhn.fhir.jpa.api.model.TranslationResult;
|
||||
import ca.uhn.fhir.context.support.TranslateConceptResults;
|
||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||
import ca.uhn.fhir.jpa.term.TermConceptMappingSvcImpl;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||
|
@ -151,10 +152,10 @@ public class BaseJpaResourceProviderConceptMapDstu3 extends JpaResourceProviderD
|
|||
startRequest(theServletRequest);
|
||||
try {
|
||||
IFhirResourceDaoConceptMap<ConceptMap> dao = (IFhirResourceDaoConceptMap<ConceptMap>) getDao();
|
||||
TranslationResult result = dao.translate(translationRequest, theRequestDetails);
|
||||
TranslateConceptResults result = dao.translate(translationRequest, theRequestDetails);
|
||||
|
||||
// Convert from R4 to DSTU3
|
||||
return convertParameters(result.toParameters());
|
||||
return convertParameters(TermConceptMappingSvcImpl.toParameters(result));
|
||||
} catch (FHIRException fe) {
|
||||
throw new InternalErrorException(fe);
|
||||
} finally {
|
||||
|
|
|
@ -22,8 +22,9 @@ package ca.uhn.fhir.jpa.provider.r4;
|
|||
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoConceptMap;
|
||||
import ca.uhn.fhir.jpa.api.model.TranslationRequest;
|
||||
import ca.uhn.fhir.jpa.api.model.TranslationResult;
|
||||
import ca.uhn.fhir.context.support.TranslateConceptResults;
|
||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||
import ca.uhn.fhir.jpa.term.TermConceptMappingSvcImpl;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||
|
@ -141,8 +142,8 @@ public class BaseJpaResourceProviderConceptMapR4 extends JpaResourceProviderR4<C
|
|||
startRequest(theServletRequest);
|
||||
try {
|
||||
IFhirResourceDaoConceptMap<ConceptMap> dao = (IFhirResourceDaoConceptMap<ConceptMap>) getDao();
|
||||
TranslationResult result = dao.translate(translationRequest, theRequestDetails);
|
||||
return result.toParameters();
|
||||
TranslateConceptResults result = dao.translate(translationRequest, theRequestDetails);
|
||||
return TermConceptMappingSvcImpl.toParameters(result);
|
||||
} finally {
|
||||
endRequest(theServletRequest);
|
||||
}
|
||||
|
|
|
@ -22,8 +22,9 @@ package ca.uhn.fhir.jpa.provider.r5;
|
|||
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoConceptMap;
|
||||
import ca.uhn.fhir.jpa.api.model.TranslationRequest;
|
||||
import ca.uhn.fhir.jpa.api.model.TranslationResult;
|
||||
import ca.uhn.fhir.context.support.TranslateConceptResults;
|
||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||
import ca.uhn.fhir.jpa.term.TermConceptMappingSvcImpl;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||
|
@ -142,8 +143,8 @@ public class BaseJpaResourceProviderConceptMapR5 extends JpaResourceProviderR5<C
|
|||
startRequest(theServletRequest);
|
||||
try {
|
||||
IFhirResourceDaoConceptMap<ConceptMap> dao = (IFhirResourceDaoConceptMap<ConceptMap>) getDao();
|
||||
TranslationResult result = dao.translate(translationRequest, theRequestDetails);
|
||||
org.hl7.fhir.r4.model.Parameters parameters = result.toParameters();
|
||||
TranslateConceptResults result = dao.translate(translationRequest, theRequestDetails);
|
||||
org.hl7.fhir.r4.model.Parameters parameters = TermConceptMappingSvcImpl.toParameters(result);
|
||||
return org.hl7.fhir.convertors.conv40_50.Parameters40_50.convertParameters(parameters);
|
||||
} finally {
|
||||
endRequest(theServletRequest);
|
||||
|
|
|
@ -29,18 +29,12 @@ import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
|||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||
import ca.uhn.fhir.jpa.api.dao.IDao;
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem;
|
||||
import ca.uhn.fhir.jpa.api.model.TranslationQuery;
|
||||
import ca.uhn.fhir.jpa.api.model.TranslationRequest;
|
||||
import ca.uhn.fhir.jpa.config.HibernatePropertiesProvider;
|
||||
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
|
||||
import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemVersionDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.ITermConceptDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.ITermConceptDesignationDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.ITermConceptMapDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.ITermConceptMapGroupDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.ITermConceptMapGroupElementDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.ITermConceptMapGroupElementTargetDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.ITermConceptPropertyDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.ITermValueSetConceptDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.ITermValueSetConceptDesignationDao;
|
||||
|
@ -51,10 +45,6 @@ import ca.uhn.fhir.jpa.entity.TermCodeSystem;
|
|||
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
|
||||
import ca.uhn.fhir.jpa.entity.TermConcept;
|
||||
import ca.uhn.fhir.jpa.entity.TermConceptDesignation;
|
||||
import ca.uhn.fhir.jpa.entity.TermConceptMap;
|
||||
import ca.uhn.fhir.jpa.entity.TermConceptMapGroup;
|
||||
import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElement;
|
||||
import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElementTarget;
|
||||
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink;
|
||||
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
|
||||
import ca.uhn.fhir.jpa.entity.TermConceptProperty;
|
||||
|
@ -72,12 +62,12 @@ import ca.uhn.fhir.jpa.model.sched.ScheduledJobDefinition;
|
|||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||
import ca.uhn.fhir.jpa.search.builder.SearchBuilder;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermConceptMappingSvc;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermLoaderSvc;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
|
||||
import ca.uhn.fhir.jpa.term.ex.ExpansionTooCostlyException;
|
||||
import ca.uhn.fhir.jpa.util.LogicUtil;
|
||||
import ca.uhn.fhir.jpa.util.ScrollableResultsIterator;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
|
@ -102,8 +92,6 @@ import org.apache.commons.lang3.Validate;
|
|||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.search.RegexpQuery;
|
||||
import org.hibernate.ScrollMode;
|
||||
import org.hibernate.ScrollableResults;
|
||||
import org.hibernate.search.backend.elasticsearch.ElasticsearchExtension;
|
||||
import org.hibernate.search.backend.lucene.LuceneExtension;
|
||||
import org.hibernate.search.engine.search.predicate.dsl.BooleanPredicateClausesStep;
|
||||
|
@ -114,7 +102,6 @@ import org.hibernate.search.mapper.orm.Search;
|
|||
import org.hibernate.search.mapper.orm.session.SearchSession;
|
||||
import org.hl7.fhir.common.hapi.validation.support.CommonCodeSystemsTerminologyService;
|
||||
import org.hl7.fhir.common.hapi.validation.support.InMemoryTerminologyServerValidationSupport;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||
import org.hl7.fhir.instance.model.api.IBaseCoding;
|
||||
import org.hl7.fhir.instance.model.api.IBaseDatatype;
|
||||
|
@ -126,11 +113,9 @@ import org.hl7.fhir.r4.model.CanonicalType;
|
|||
import org.hl7.fhir.r4.model.CodeSystem;
|
||||
import org.hl7.fhir.r4.model.CodeableConcept;
|
||||
import org.hl7.fhir.r4.model.Coding;
|
||||
import org.hl7.fhir.r4.model.ConceptMap;
|
||||
import org.hl7.fhir.r4.model.DomainResource;
|
||||
import org.hl7.fhir.r4.model.Enumerations;
|
||||
import org.hl7.fhir.r4.model.Extension;
|
||||
import org.hl7.fhir.r4.model.IdType;
|
||||
import org.hl7.fhir.r4.model.IntegerType;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
import org.hl7.fhir.r4.model.ValueSet;
|
||||
|
@ -139,7 +124,6 @@ import org.quartz.JobExecutionContext;
|
|||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
|
@ -170,7 +154,6 @@ import java.util.Collection;
|
|||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -199,10 +182,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseTermReadSvcImpl.class);
|
||||
private static final ValueSetExpansionOptions DEFAULT_EXPANSION_OPTIONS = new ValueSetExpansionOptions();
|
||||
private static final TermCodeSystemVersion NO_CURRENT_VERSION = new TermCodeSystemVersion().setId(-1L);
|
||||
private static boolean ourLastResultsFromTranslationCache; // For testing.
|
||||
private static boolean ourLastResultsFromTranslationWithReverseCache; // For testing.
|
||||
private static Runnable myInvokeOnNextCallForUnitTest;
|
||||
private final int myFetchSize = DEFAULT_FETCH_SIZE;
|
||||
private final Cache<String, TermCodeSystemVersion> myCodeSystemCurrentVersionCache = Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).build();
|
||||
@Autowired
|
||||
protected DaoRegistry myDaoRegistry;
|
||||
|
@ -211,14 +191,6 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
@Autowired
|
||||
protected ITermConceptDao myConceptDao;
|
||||
@Autowired
|
||||
protected ITermConceptMapDao myConceptMapDao;
|
||||
@Autowired
|
||||
protected ITermConceptMapGroupDao myConceptMapGroupDao;
|
||||
@Autowired
|
||||
protected ITermConceptMapGroupElementDao myConceptMapGroupElementDao;
|
||||
@Autowired
|
||||
protected ITermConceptMapGroupElementTargetDao myConceptMapGroupElementTargetDao;
|
||||
@Autowired
|
||||
protected ITermConceptPropertyDao myConceptPropertyDao;
|
||||
@Autowired
|
||||
protected ITermConceptDesignationDao myConceptDesignationDao;
|
||||
|
@ -236,8 +208,6 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
private ITermCodeSystemVersionDao myCodeSystemVersionDao;
|
||||
@Autowired
|
||||
private DaoConfig myDaoConfig;
|
||||
private Cache<TranslationQuery, List<TermConceptMapGroupElementTarget>> myTranslationCache;
|
||||
private Cache<TranslationQuery, List<TermConceptMapGroupElement>> myTranslationWithReverseCache;
|
||||
private TransactionTemplate myTxTemplate;
|
||||
@Autowired
|
||||
private PlatformTransactionManager myTransactionManager;
|
||||
|
@ -257,6 +227,9 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
private ITermCodeSystemStorageSvc myConceptStorageSvc;
|
||||
@Autowired
|
||||
private ApplicationContext myApplicationContext;
|
||||
@Autowired
|
||||
private ITermConceptMappingSvc myTermConceptMappingSvc;
|
||||
|
||||
private volatile IValidationSupport myJpaValidationSupport;
|
||||
private volatile IValidationSupport myValidationSupport;
|
||||
|
||||
|
@ -351,44 +324,9 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
*/
|
||||
@VisibleForTesting
|
||||
public void clearCaches() {
|
||||
myTranslationCache.invalidateAll();
|
||||
myTranslationWithReverseCache.invalidateAll();
|
||||
myCodeSystemCurrentVersionCache.invalidateAll();
|
||||
}
|
||||
|
||||
public void deleteConceptMap(ResourceTable theResourceTable) {
|
||||
// Get existing entity so it can be deleted.
|
||||
Optional<TermConceptMap> optionalExistingTermConceptMapById = myConceptMapDao.findTermConceptMapByResourcePid(theResourceTable.getId());
|
||||
|
||||
if (optionalExistingTermConceptMapById.isPresent()) {
|
||||
TermConceptMap existingTermConceptMap = optionalExistingTermConceptMapById.get();
|
||||
|
||||
ourLog.info("Deleting existing TermConceptMap[{}] and its children...", existingTermConceptMap.getId());
|
||||
for (TermConceptMapGroup group : existingTermConceptMap.getConceptMapGroups()) {
|
||||
|
||||
for (TermConceptMapGroupElement element : group.getConceptMapGroupElements()) {
|
||||
|
||||
for (TermConceptMapGroupElementTarget target : element.getConceptMapGroupElementTargets()) {
|
||||
|
||||
myConceptMapGroupElementTargetDao.deleteTermConceptMapGroupElementTargetById(target.getId());
|
||||
}
|
||||
|
||||
myConceptMapGroupElementDao.deleteTermConceptMapGroupElementById(element.getId());
|
||||
}
|
||||
|
||||
myConceptMapGroupDao.deleteTermConceptMapGroupById(group.getId());
|
||||
}
|
||||
|
||||
myConceptMapDao.deleteTermConceptMapById(existingTermConceptMap.getId());
|
||||
ourLog.info("Done deleting existing TermConceptMap[{}] and its children.", existingTermConceptMap.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void deleteConceptMapAndChildren(ResourceTable theResourceTable) {
|
||||
deleteConceptMap(theResourceTable);
|
||||
}
|
||||
|
||||
public void deleteValueSetForResource(ResourceTable theResourceTable) {
|
||||
// Get existing entity so it can be deleted.
|
||||
|
@ -533,13 +471,12 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
theAccumulator.addMessage(msg);
|
||||
if (isOracleDialect()) {
|
||||
expandConceptsOracle(theAccumulator, termValueSet, theFilter, theAdd);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
expandConcepts(theAccumulator, termValueSet, theFilter, theAdd);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isOracleDialect(){
|
||||
private boolean isOracleDialect() {
|
||||
return myHibernatePropertiesProvider.getDialect() instanceof org.hibernate.dialect.Oracle12cDialect;
|
||||
}
|
||||
|
||||
|
@ -794,7 +731,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
}
|
||||
|
||||
// Allow to search by the end of the phrase. E.g. "working proficiency" will match "Limited working proficiency"
|
||||
for (int start = 0; start <= tokens.size() - 1; ++ start) {
|
||||
for (int start = 0; start <= tokens.size() - 1; ++start) {
|
||||
for (int end = start + 1; end <= tokens.size(); ++end) {
|
||||
String sublist = String.join(" ", tokens.subList(start, end));
|
||||
if (startsWithIgnoreCase(sublist, theFilterDisplay))
|
||||
|
@ -1137,7 +1074,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
int resultsInBatch = termConcepts.size();
|
||||
int firstResult = theQueryIndex * maxResultsPerBatch;// TODO GGG HS we lose the ability to check the index of the first result, so just best-guessing it here.
|
||||
int delta = 0;
|
||||
for (TermConcept concept: termConcepts) {
|
||||
for (TermConcept concept : termConcepts) {
|
||||
count.incrementAndGet();
|
||||
countForBatch.incrementAndGet();
|
||||
if (theAdd && expansionStep != null) {
|
||||
|
@ -1455,7 +1392,6 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
}
|
||||
|
||||
|
||||
|
||||
private void addDisplayFilterInexact(SearchPredicateFactory f, BooleanPredicateClausesStep<?> bool, ValueSet.ConceptSetFilterComponent nextFilter) {
|
||||
bool.must(f.phrase()
|
||||
.field("myDisplay").boost(4.0f)
|
||||
|
@ -1489,7 +1425,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
addLoincFilterDescendantEqual(theSystem, f, b, theFilter);
|
||||
break;
|
||||
case IN:
|
||||
addLoincFilterDescendantIn(theSystem, f,b , theFilter);
|
||||
addLoincFilterDescendantIn(theSystem, f, b, theFilter);
|
||||
break;
|
||||
default:
|
||||
throw new InvalidRequestException("Don't know how to handle op=" + theFilter.getOp() + " on property " + theFilter.getProperty());
|
||||
|
@ -1545,7 +1481,6 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
}
|
||||
|
||||
|
||||
|
||||
private void logFilteringValueOnProperty(String theValue, String theProperty) {
|
||||
ourLog.debug(" * Filtering with value={} on property {}", theValue, theProperty);
|
||||
}
|
||||
|
@ -1859,26 +1794,9 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
RuleBasedTransactionAttribute rules = new RuleBasedTransactionAttribute();
|
||||
rules.getRollbackRules().add(new NoRollbackRuleAttribute(ExpansionTooCostlyException.class));
|
||||
myTxTemplate = new TransactionTemplate(myTransactionManager, rules);
|
||||
buildTranslationCaches();
|
||||
scheduleJob();
|
||||
}
|
||||
|
||||
private void buildTranslationCaches() {
|
||||
Long timeout = myDaoConfig.getTranslationCachesExpireAfterWriteInMinutes();
|
||||
|
||||
myTranslationCache =
|
||||
Caffeine.newBuilder()
|
||||
.maximumSize(10000)
|
||||
.expireAfterWrite(timeout, TimeUnit.MINUTES)
|
||||
.build();
|
||||
|
||||
myTranslationWithReverseCache =
|
||||
Caffeine.newBuilder()
|
||||
.maximumSize(10000)
|
||||
.expireAfterWrite(timeout, TimeUnit.MINUTES)
|
||||
.build();
|
||||
}
|
||||
|
||||
public void scheduleJob() {
|
||||
// Register scheduled job to pre-expand ValueSets
|
||||
// In the future it would be great to make this a cluster-aware task somehow
|
||||
|
@ -1888,163 +1806,6 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
mySchedulerService.scheduleClusteredJob(10 * DateUtils.MILLIS_PER_MINUTE, vsJobDefinition);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void storeTermConceptMapAndChildren(ResourceTable theResourceTable, ConceptMap theConceptMap) {
|
||||
|
||||
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);
|
||||
termConceptMap.setUrl(theConceptMap.getUrl());
|
||||
termConceptMap.setVersion(theConceptMap.getVersion());
|
||||
|
||||
String source = theConceptMap.hasSourceUriType() ? theConceptMap.getSourceUriType().getValueAsString() : null;
|
||||
String target = theConceptMap.hasTargetUriType() ? theConceptMap.getTargetUriType().getValueAsString() : null;
|
||||
|
||||
/*
|
||||
* If this is a mapping between "resources" instead of purely between
|
||||
* "concepts" (this is a weird concept that is technically possible, at least as of
|
||||
* FHIR R4), don't try to store the mappings.
|
||||
*
|
||||
* See here for a description of what that is:
|
||||
* http://hl7.org/fhir/conceptmap.html#bnr
|
||||
*/
|
||||
if ("StructureDefinition".equals(new IdType(source).getResourceType()) ||
|
||||
"StructureDefinition".equals(new IdType(target).getResourceType())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (source == null && theConceptMap.hasSourceCanonicalType()) {
|
||||
source = theConceptMap.getSourceCanonicalType().getValueAsString();
|
||||
}
|
||||
if (target == null && theConceptMap.hasTargetCanonicalType()) {
|
||||
target = theConceptMap.getTargetCanonicalType().getValueAsString();
|
||||
}
|
||||
|
||||
/*
|
||||
* For now we always delete old versions. At some point, it would be nice to allow configuration to keep old versions.
|
||||
*/
|
||||
deleteConceptMap(theResourceTable);
|
||||
|
||||
/*
|
||||
* Do the upload.
|
||||
*/
|
||||
String conceptMapUrl = termConceptMap.getUrl();
|
||||
String conceptMapVersion = termConceptMap.getVersion();
|
||||
Optional<TermConceptMap> optionalExistingTermConceptMapByUrl;
|
||||
if (isBlank(conceptMapVersion)) {
|
||||
optionalExistingTermConceptMapByUrl = myConceptMapDao.findTermConceptMapByUrlAndNullVersion(conceptMapUrl);
|
||||
} else {
|
||||
optionalExistingTermConceptMapByUrl = myConceptMapDao.findTermConceptMapByUrlAndVersion(conceptMapUrl, conceptMapVersion);
|
||||
}
|
||||
if (!optionalExistingTermConceptMapByUrl.isPresent()) {
|
||||
try {
|
||||
if (isNotBlank(source)) {
|
||||
termConceptMap.setSource(source);
|
||||
}
|
||||
if (isNotBlank(target)) {
|
||||
termConceptMap.setTarget(target);
|
||||
}
|
||||
} catch (FHIRException fe) {
|
||||
throw new InternalErrorException(fe);
|
||||
}
|
||||
termConceptMap = myConceptMapDao.save(termConceptMap);
|
||||
int codesSaved = 0;
|
||||
|
||||
if (theConceptMap.hasGroup()) {
|
||||
TermConceptMapGroup termConceptMapGroup;
|
||||
for (ConceptMap.ConceptMapGroupComponent group : theConceptMap.getGroup()) {
|
||||
|
||||
String groupSource = group.getSource();
|
||||
if (isBlank(groupSource)) {
|
||||
groupSource = source;
|
||||
}
|
||||
if (isBlank(groupSource)) {
|
||||
throw new UnprocessableEntityException("ConceptMap[url='" + theConceptMap.getUrl() + "'] contains at least one group without a value in ConceptMap.group.source");
|
||||
}
|
||||
|
||||
String groupTarget = group.getTarget();
|
||||
if (isBlank(groupTarget)) {
|
||||
groupTarget = target;
|
||||
}
|
||||
if (isBlank(groupTarget)) {
|
||||
throw new UnprocessableEntityException("ConceptMap[url='" + theConceptMap.getUrl() + "'] contains at least one group without a value in ConceptMap.group.target");
|
||||
}
|
||||
|
||||
termConceptMapGroup = new TermConceptMapGroup();
|
||||
termConceptMapGroup.setConceptMap(termConceptMap);
|
||||
termConceptMapGroup.setSource(groupSource);
|
||||
termConceptMapGroup.setSourceVersion(group.getSourceVersion());
|
||||
termConceptMapGroup.setTarget(groupTarget);
|
||||
termConceptMapGroup.setTargetVersion(group.getTargetVersion());
|
||||
myConceptMapGroupDao.save(termConceptMapGroup);
|
||||
|
||||
if (group.hasElement()) {
|
||||
TermConceptMapGroupElement termConceptMapGroupElement;
|
||||
for (ConceptMap.SourceElementComponent element : group.getElement()) {
|
||||
if (isBlank(element.getCode())) {
|
||||
continue;
|
||||
}
|
||||
termConceptMapGroupElement = new TermConceptMapGroupElement();
|
||||
termConceptMapGroupElement.setConceptMapGroup(termConceptMapGroup);
|
||||
termConceptMapGroupElement.setCode(element.getCode());
|
||||
termConceptMapGroupElement.setDisplay(element.getDisplay());
|
||||
myConceptMapGroupElementDao.save(termConceptMapGroupElement);
|
||||
|
||||
if (element.hasTarget()) {
|
||||
TermConceptMapGroupElementTarget termConceptMapGroupElementTarget;
|
||||
for (ConceptMap.TargetElementComponent elementTarget : element.getTarget()) {
|
||||
if (isBlank(elementTarget.getCode())) {
|
||||
continue;
|
||||
}
|
||||
termConceptMapGroupElementTarget = new TermConceptMapGroupElementTarget();
|
||||
termConceptMapGroupElementTarget.setConceptMapGroupElement(termConceptMapGroupElement);
|
||||
termConceptMapGroupElementTarget.setCode(elementTarget.getCode());
|
||||
termConceptMapGroupElementTarget.setDisplay(elementTarget.getDisplay());
|
||||
termConceptMapGroupElementTarget.setEquivalence(elementTarget.getEquivalence());
|
||||
myConceptMapGroupElementTargetDao.save(termConceptMapGroupElementTarget);
|
||||
|
||||
if (++codesSaved % 250 == 0) {
|
||||
ourLog.info("Have saved {} codes in ConceptMap", codesSaved);
|
||||
myConceptMapGroupElementTargetDao.flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
TermConceptMap existingTermConceptMap = optionalExistingTermConceptMapByUrl.get();
|
||||
|
||||
if (isBlank(conceptMapVersion)) {
|
||||
String msg = myContext.getLocalizer().getMessage(
|
||||
BaseTermReadSvcImpl.class,
|
||||
"cannotCreateDuplicateConceptMapUrl",
|
||||
conceptMapUrl,
|
||||
existingTermConceptMap.getResource().getIdDt().toUnqualifiedVersionless().getValue());
|
||||
throw new UnprocessableEntityException(msg);
|
||||
|
||||
} else {
|
||||
String msg = myContext.getLocalizer().getMessage(
|
||||
BaseTermReadSvcImpl.class,
|
||||
"cannotCreateDuplicateConceptMapUrlAndVersion",
|
||||
conceptMapUrl, conceptMapVersion,
|
||||
existingTermConceptMap.getResource().getIdDt().toUnqualifiedVersionless().getValue());
|
||||
throw new UnprocessableEntityException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
ourLog.info("Done storing TermConceptMap[{}] for {}", termConceptMap.getId(), theConceptMap.getIdElement().toVersionless().getValueAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void preExpandDeferredValueSetsToTerminologyTables() {
|
||||
|
@ -2244,15 +2005,6 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
}
|
||||
}
|
||||
|
||||
private boolean isPlaceholder(DomainResource theResource) {
|
||||
boolean retVal = false;
|
||||
Extension extension = theResource.getExtensionByUrl(HapiExtensions.EXT_RESOURCE_PLACEHOLDER);
|
||||
if (extension != null && extension.hasValue() && extension.getValue() instanceof BooleanType) {
|
||||
retVal = ((BooleanType) extension.getValue()).booleanValue();
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public IFhirResourceDaoCodeSystem.SubsumesResult subsumes(IPrimitiveType<String> theCodeA, IPrimitiveType<String> theCodeB,
|
||||
|
@ -2362,7 +2114,6 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private ArrayList<FhirVersionIndependentConcept> toVersionIndependentConcepts(String theSystem, Set<TermConcept> codes) {
|
||||
ArrayList<FhirVersionIndependentConcept> retVal = new ArrayList<>(codes.size());
|
||||
for (TermConcept next : codes) {
|
||||
|
@ -2371,254 +2122,6 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.REQUIRED)
|
||||
public List<TermConceptMapGroupElementTarget> translate(TranslationRequest theTranslationRequest) {
|
||||
List<TermConceptMapGroupElementTarget> retVal = new ArrayList<>();
|
||||
|
||||
CriteriaBuilder criteriaBuilder = myEntityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<TermConceptMapGroupElementTarget> query = criteriaBuilder.createQuery(TermConceptMapGroupElementTarget.class);
|
||||
Root<TermConceptMapGroupElementTarget> root = query.from(TermConceptMapGroupElementTarget.class);
|
||||
|
||||
Join<TermConceptMapGroupElementTarget, TermConceptMapGroupElement> elementJoin = root.join("myConceptMapGroupElement");
|
||||
Join<TermConceptMapGroupElement, TermConceptMapGroup> groupJoin = elementJoin.join("myConceptMapGroup");
|
||||
Join<TermConceptMapGroup, TermConceptMap> conceptMapJoin = groupJoin.join("myConceptMap");
|
||||
|
||||
List<TranslationQuery> translationQueries = theTranslationRequest.getTranslationQueries();
|
||||
List<TermConceptMapGroupElementTarget> cachedTargets;
|
||||
ArrayList<Predicate> predicates;
|
||||
Coding coding;
|
||||
|
||||
//-- get the latest ConceptMapVersion if theTranslationRequest has ConceptMap url but no ConceptMap version
|
||||
String latestConceptMapVersion = null;
|
||||
if (theTranslationRequest.hasUrl() && !theTranslationRequest.hasConceptMapVersion())
|
||||
latestConceptMapVersion = getLatestConceptMapVersion(theTranslationRequest);
|
||||
|
||||
for (TranslationQuery translationQuery : translationQueries) {
|
||||
cachedTargets = myTranslationCache.getIfPresent(translationQuery);
|
||||
if (cachedTargets == null) {
|
||||
final List<TermConceptMapGroupElementTarget> targets = new ArrayList<>();
|
||||
|
||||
predicates = new ArrayList<>();
|
||||
|
||||
coding = translationQuery.getCoding();
|
||||
if (coding.hasCode()) {
|
||||
predicates.add(criteriaBuilder.equal(elementJoin.get("myCode"), coding.getCode()));
|
||||
} else {
|
||||
throw new InvalidRequestException("A code must be provided for translation to occur.");
|
||||
}
|
||||
|
||||
if (coding.hasSystem()) {
|
||||
predicates.add(criteriaBuilder.equal(groupJoin.get("mySource"), coding.getSystem()));
|
||||
}
|
||||
|
||||
if (coding.hasVersion()) {
|
||||
predicates.add(criteriaBuilder.equal(groupJoin.get("mySourceVersion"), coding.getVersion()));
|
||||
}
|
||||
|
||||
if (translationQuery.hasTargetSystem()) {
|
||||
predicates.add(criteriaBuilder.equal(groupJoin.get("myTarget"), translationQuery.getTargetSystem().getValueAsString()));
|
||||
}
|
||||
|
||||
if (translationQuery.hasUrl()) {
|
||||
predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myUrl"), translationQuery.getUrl().getValueAsString()));
|
||||
if (translationQuery.hasConceptMapVersion()) {
|
||||
// both url and conceptMapVersion
|
||||
predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myVersion"), translationQuery.getConceptMapVersion().getValueAsString()));
|
||||
} else {
|
||||
if (StringUtils.isNotBlank(latestConceptMapVersion)) {
|
||||
// only url and use latestConceptMapVersion
|
||||
predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myVersion"), latestConceptMapVersion));
|
||||
} else {
|
||||
predicates.add(criteriaBuilder.isNull(conceptMapJoin.get("myVersion")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (translationQuery.hasSource()) {
|
||||
predicates.add(criteriaBuilder.equal(conceptMapJoin.get("mySource"), translationQuery.getSource().getValueAsString()));
|
||||
}
|
||||
|
||||
if (translationQuery.hasTarget()) {
|
||||
predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myTarget"), translationQuery.getTarget().getValueAsString()));
|
||||
}
|
||||
|
||||
if (translationQuery.hasResourceId()) {
|
||||
predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myResourcePid"), translationQuery.getResourceId()));
|
||||
}
|
||||
|
||||
Predicate outerPredicate = criteriaBuilder.and(predicates.toArray(new Predicate[0]));
|
||||
query.where(outerPredicate);
|
||||
|
||||
// Use scrollable results.
|
||||
final TypedQuery<TermConceptMapGroupElementTarget> typedQuery = myEntityManager.createQuery(query.select(root));
|
||||
org.hibernate.query.Query<TermConceptMapGroupElementTarget> hibernateQuery = (org.hibernate.query.Query<TermConceptMapGroupElementTarget>) typedQuery;
|
||||
hibernateQuery.setFetchSize(myFetchSize);
|
||||
ScrollableResults scrollableResults = hibernateQuery.scroll(ScrollMode.FORWARD_ONLY);
|
||||
try (ScrollableResultsIterator<TermConceptMapGroupElementTarget> scrollableResultsIterator = new ScrollableResultsIterator<>(scrollableResults)) {
|
||||
|
||||
while (scrollableResultsIterator.hasNext()) {
|
||||
targets.add(scrollableResultsIterator.next());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ourLastResultsFromTranslationCache = false; // For testing.
|
||||
myTranslationCache.get(translationQuery, k -> targets);
|
||||
retVal.addAll(targets);
|
||||
} else {
|
||||
ourLastResultsFromTranslationCache = true; // For testing.
|
||||
retVal.addAll(cachedTargets);
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.REQUIRED)
|
||||
public List<TermConceptMapGroupElement> translateWithReverse(TranslationRequest theTranslationRequest) {
|
||||
List<TermConceptMapGroupElement> retVal = new ArrayList<>();
|
||||
|
||||
CriteriaBuilder criteriaBuilder = myEntityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<TermConceptMapGroupElement> query = criteriaBuilder.createQuery(TermConceptMapGroupElement.class);
|
||||
Root<TermConceptMapGroupElement> root = query.from(TermConceptMapGroupElement.class);
|
||||
|
||||
Join<TermConceptMapGroupElement, TermConceptMapGroupElementTarget> targetJoin = root.join("myConceptMapGroupElementTargets");
|
||||
Join<TermConceptMapGroupElement, TermConceptMapGroup> groupJoin = root.join("myConceptMapGroup");
|
||||
Join<TermConceptMapGroup, TermConceptMap> conceptMapJoin = groupJoin.join("myConceptMap");
|
||||
|
||||
List<TranslationQuery> translationQueries = theTranslationRequest.getTranslationQueries();
|
||||
List<TermConceptMapGroupElement> cachedElements;
|
||||
ArrayList<Predicate> predicates;
|
||||
Coding coding;
|
||||
|
||||
//-- get the latest ConceptMapVersion if theTranslationRequest has ConceptMap url but no ConceptMap version
|
||||
String latestConceptMapVersion = null;
|
||||
if (theTranslationRequest.hasUrl() && !theTranslationRequest.hasConceptMapVersion())
|
||||
latestConceptMapVersion = getLatestConceptMapVersion(theTranslationRequest);
|
||||
|
||||
for (TranslationQuery translationQuery : translationQueries) {
|
||||
cachedElements = myTranslationWithReverseCache.getIfPresent(translationQuery);
|
||||
if (cachedElements == null) {
|
||||
final List<TermConceptMapGroupElement> elements = new ArrayList<>();
|
||||
|
||||
predicates = new ArrayList<>();
|
||||
|
||||
coding = translationQuery.getCoding();
|
||||
String targetCode;
|
||||
String targetCodeSystem = null;
|
||||
if (coding.hasCode()) {
|
||||
predicates.add(criteriaBuilder.equal(targetJoin.get("myCode"), coding.getCode()));
|
||||
targetCode = coding.getCode();
|
||||
} else {
|
||||
throw new InvalidRequestException("A code must be provided for translation to occur.");
|
||||
}
|
||||
|
||||
if (coding.hasSystem()) {
|
||||
predicates.add(criteriaBuilder.equal(groupJoin.get("myTarget"), coding.getSystem()));
|
||||
targetCodeSystem = coding.getSystem();
|
||||
}
|
||||
|
||||
if (coding.hasVersion()) {
|
||||
predicates.add(criteriaBuilder.equal(groupJoin.get("myTargetVersion"), coding.getVersion()));
|
||||
}
|
||||
|
||||
if (translationQuery.hasUrl()) {
|
||||
predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myUrl"), translationQuery.getUrl().getValueAsString()));
|
||||
if (translationQuery.hasConceptMapVersion()) {
|
||||
// both url and conceptMapVersion
|
||||
predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myVersion"), translationQuery.getConceptMapVersion().getValueAsString()));
|
||||
} else {
|
||||
if (StringUtils.isNotBlank(latestConceptMapVersion)) {
|
||||
// only url and use latestConceptMapVersion
|
||||
predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myVersion"), latestConceptMapVersion));
|
||||
} else {
|
||||
predicates.add(criteriaBuilder.isNull(conceptMapJoin.get("myVersion")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (translationQuery.hasTargetSystem()) {
|
||||
predicates.add(criteriaBuilder.equal(groupJoin.get("mySource"), translationQuery.getTargetSystem().getValueAsString()));
|
||||
}
|
||||
|
||||
if (translationQuery.hasSource()) {
|
||||
predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myTarget"), translationQuery.getSource().getValueAsString()));
|
||||
}
|
||||
|
||||
if (translationQuery.hasTarget()) {
|
||||
predicates.add(criteriaBuilder.equal(conceptMapJoin.get("mySource"), translationQuery.getTarget().getValueAsString()));
|
||||
}
|
||||
|
||||
if (translationQuery.hasResourceId()) {
|
||||
predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myResourcePid"), translationQuery.getResourceId()));
|
||||
}
|
||||
|
||||
Predicate outerPredicate = criteriaBuilder.and(predicates.toArray(new Predicate[0]));
|
||||
query.where(outerPredicate);
|
||||
|
||||
// Use scrollable results.
|
||||
final TypedQuery<TermConceptMapGroupElement> typedQuery = myEntityManager.createQuery(query.select(root));
|
||||
org.hibernate.query.Query<TermConceptMapGroupElement> hibernateQuery = (org.hibernate.query.Query<TermConceptMapGroupElement>) typedQuery;
|
||||
hibernateQuery.setFetchSize(myFetchSize);
|
||||
ScrollableResults scrollableResults = hibernateQuery.scroll(ScrollMode.FORWARD_ONLY);
|
||||
try (ScrollableResultsIterator<TermConceptMapGroupElement> scrollableResultsIterator = new ScrollableResultsIterator<>(scrollableResults)) {
|
||||
|
||||
while (scrollableResultsIterator.hasNext()) {
|
||||
TermConceptMapGroupElement nextElement = scrollableResultsIterator.next();
|
||||
// TODO: The invocation of the size() below does not seem to be necessary but for some reason, removing it causes tests in TerminologySvcImplR4Test to fail.
|
||||
nextElement.getConceptMapGroupElementTargets().size();
|
||||
myEntityManager.detach(nextElement);
|
||||
|
||||
if (isNotBlank(targetCode) && isNotBlank(targetCodeSystem)) {
|
||||
for (Iterator<TermConceptMapGroupElementTarget> iter = nextElement.getConceptMapGroupElementTargets().iterator(); iter.hasNext(); ) {
|
||||
TermConceptMapGroupElementTarget next = iter.next();
|
||||
if (StringUtils.equals(targetCodeSystem, next.getSystem())) {
|
||||
if (StringUtils.equals(targetCode, next.getCode())) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
|
||||
elements.add(nextElement);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ourLastResultsFromTranslationWithReverseCache = false; // For testing.
|
||||
myTranslationWithReverseCache.get(translationQuery, k -> elements);
|
||||
retVal.addAll(elements);
|
||||
} else {
|
||||
ourLastResultsFromTranslationWithReverseCache = true; // For testing.
|
||||
retVal.addAll(cachedElements);
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
void throwInvalidValueSet(String theValueSet) {
|
||||
throw new ResourceNotFoundException("Unknown ValueSet: " + UrlUtil.escapeUrlParam(theValueSet));
|
||||
}
|
||||
|
||||
// Special case for the translate operation with url and without
|
||||
// conceptMapVersion, find the latest conecptMapVersion
|
||||
private String getLatestConceptMapVersion(TranslationRequest theTranslationRequest) {
|
||||
|
||||
Pageable page = PageRequest.of(0, 1);
|
||||
List<TermConceptMap> theConceptMapList = myConceptMapDao.getTermConceptMapEntitiesByUrlOrderByMostRecentUpdate(page,
|
||||
theTranslationRequest.getUrl().asStringValue());
|
||||
if (!theConceptMapList.isEmpty()) {
|
||||
return theConceptMapList.get(0).getVersion();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CodeValidationResult validateCodeInValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) {
|
||||
|
@ -2659,7 +2162,6 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
return createFailureCodeValidationResult(theCodeSystem, theCode);
|
||||
}
|
||||
|
||||
|
||||
IValidationSupport.CodeValidationResult validateCodeInValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theValidationOptions, String theValueSetUrl, String theCodeSystem, String theCode, String theDisplay) {
|
||||
IBaseResource valueSet = theValidationSupportContext.getRootValidationSupport().fetchValueSet(theValueSetUrl);
|
||||
|
||||
|
@ -2962,6 +2464,15 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
}
|
||||
}
|
||||
|
||||
static boolean isPlaceholder(DomainResource theResource) {
|
||||
boolean retVal = false;
|
||||
Extension extension = theResource.getExtensionByUrl(HapiExtensions.EXT_RESOURCE_PLACEHOLDER);
|
||||
if (extension != null && extension.hasValue() && extension.getValue() instanceof BooleanType) {
|
||||
retVal = ((BooleanType) extension.getValue()).booleanValue();
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is only used for unit tests to test failure conditions
|
||||
*/
|
||||
|
@ -3039,35 +2550,4 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
return termConcept;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is present only for unit tests, do not call from client code
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public static void clearOurLastResultsFromTranslationCache() {
|
||||
ourLastResultsFromTranslationCache = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is present only for unit tests, do not call from client code
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public static void clearOurLastResultsFromTranslationWithReverseCache() {
|
||||
ourLastResultsFromTranslationWithReverseCache = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is present only for unit tests, do not call from client code
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static boolean isOurLastResultsFromTranslationCache() {
|
||||
return ourLastResultsFromTranslationCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is present only for unit tests, do not call from client code
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static boolean isOurLastResultsFromTranslationWithReverseCache() {
|
||||
return ourLastResultsFromTranslationWithReverseCache;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,698 @@
|
|||
package ca.uhn.fhir.jpa.term;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2021 Smile CDR, Inc.
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.support.TranslateConceptResult;
|
||||
import ca.uhn.fhir.context.support.TranslateConceptResults;
|
||||
import ca.uhn.fhir.jpa.api.model.TranslationQuery;
|
||||
import ca.uhn.fhir.jpa.api.model.TranslationRequest;
|
||||
import ca.uhn.fhir.jpa.dao.data.ITermConceptMapDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.ITermConceptMapGroupDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.ITermConceptMapGroupElementDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.ITermConceptMapGroupElementTargetDao;
|
||||
import ca.uhn.fhir.jpa.entity.TermConceptMap;
|
||||
import ca.uhn.fhir.jpa.entity.TermConceptMapGroup;
|
||||
import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElement;
|
||||
import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElementTarget;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermConceptMappingSvc;
|
||||
import ca.uhn.fhir.jpa.util.MemoryCacheService;
|
||||
import ca.uhn.fhir.jpa.util.ScrollableResultsIterator;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||
import ca.uhn.fhir.util.ValidateUtil;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.hibernate.ScrollMode;
|
||||
import org.hibernate.ScrollableResults;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.r4.model.BooleanType;
|
||||
import org.hl7.fhir.r4.model.CodeType;
|
||||
import org.hl7.fhir.r4.model.CodeableConcept;
|
||||
import org.hl7.fhir.r4.model.Coding;
|
||||
import org.hl7.fhir.r4.model.ConceptMap;
|
||||
import org.hl7.fhir.r4.model.IdType;
|
||||
import org.hl7.fhir.r4.model.Parameters;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
import org.hl7.fhir.r4.model.UriType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
import javax.persistence.PersistenceContextType;
|
||||
import javax.persistence.TypedQuery;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Join;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import static ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.isPlaceholder;
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public class TermConceptMappingSvcImpl implements ITermConceptMappingSvc {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(TermConceptMappingSvcImpl.class);
|
||||
private static boolean ourLastResultsFromTranslationCache; // For testing.
|
||||
private static boolean ourLastResultsFromTranslationWithReverseCache; // For testing.
|
||||
private final int myFetchSize = BaseTermReadSvcImpl.DEFAULT_FETCH_SIZE;
|
||||
@Autowired
|
||||
protected ITermConceptMapDao myConceptMapDao;
|
||||
@Autowired
|
||||
protected ITermConceptMapGroupDao myConceptMapGroupDao;
|
||||
@Autowired
|
||||
protected ITermConceptMapGroupElementDao myConceptMapGroupElementDao;
|
||||
@Autowired
|
||||
protected ITermConceptMapGroupElementTargetDao myConceptMapGroupElementTargetDao;
|
||||
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
|
||||
protected EntityManager myEntityManager;
|
||||
@Autowired
|
||||
private FhirContext myContext;
|
||||
@Autowired
|
||||
private MemoryCacheService myMemoryCacheService;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void deleteConceptMapAndChildren(ResourceTable theResourceTable) {
|
||||
deleteConceptMap(theResourceTable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FhirContext getFhirContext() {
|
||||
return myContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public TranslateConceptResults translateConcept(TranslateCodeRequest theRequest) {
|
||||
|
||||
CodeableConcept sourceCodeableConcept = new CodeableConcept();
|
||||
sourceCodeableConcept
|
||||
.addCoding()
|
||||
.setSystem(theRequest.getSourceSystemUrl())
|
||||
.setCode(theRequest.getSourceCode());
|
||||
|
||||
TranslationRequest request = new TranslationRequest();
|
||||
request.setCodeableConcept(sourceCodeableConcept);
|
||||
request.setTargetSystem(new UriType(theRequest.getTargetSystemUrl()));
|
||||
|
||||
return translate(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void storeTermConceptMapAndChildren(ResourceTable theResourceTable, ConceptMap theConceptMap) {
|
||||
|
||||
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);
|
||||
termConceptMap.setUrl(theConceptMap.getUrl());
|
||||
termConceptMap.setVersion(theConceptMap.getVersion());
|
||||
|
||||
String source = theConceptMap.hasSourceUriType() ? theConceptMap.getSourceUriType().getValueAsString() : null;
|
||||
String target = theConceptMap.hasTargetUriType() ? theConceptMap.getTargetUriType().getValueAsString() : null;
|
||||
|
||||
/*
|
||||
* If this is a mapping between "resources" instead of purely between
|
||||
* "concepts" (this is a weird concept that is technically possible, at least as of
|
||||
* FHIR R4), don't try to store the mappings.
|
||||
*
|
||||
* See here for a description of what that is:
|
||||
* http://hl7.org/fhir/conceptmap.html#bnr
|
||||
*/
|
||||
if ("StructureDefinition".equals(new IdType(source).getResourceType()) ||
|
||||
"StructureDefinition".equals(new IdType(target).getResourceType())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (source == null && theConceptMap.hasSourceCanonicalType()) {
|
||||
source = theConceptMap.getSourceCanonicalType().getValueAsString();
|
||||
}
|
||||
if (target == null && theConceptMap.hasTargetCanonicalType()) {
|
||||
target = theConceptMap.getTargetCanonicalType().getValueAsString();
|
||||
}
|
||||
|
||||
/*
|
||||
* For now we always delete old versions. At some point, it would be nice to allow configuration to keep old versions.
|
||||
*/
|
||||
deleteConceptMap(theResourceTable);
|
||||
|
||||
/*
|
||||
* Do the upload.
|
||||
*/
|
||||
String conceptMapUrl = termConceptMap.getUrl();
|
||||
String conceptMapVersion = termConceptMap.getVersion();
|
||||
Optional<TermConceptMap> optionalExistingTermConceptMapByUrl;
|
||||
if (isBlank(conceptMapVersion)) {
|
||||
optionalExistingTermConceptMapByUrl = myConceptMapDao.findTermConceptMapByUrlAndNullVersion(conceptMapUrl);
|
||||
} else {
|
||||
optionalExistingTermConceptMapByUrl = myConceptMapDao.findTermConceptMapByUrlAndVersion(conceptMapUrl, conceptMapVersion);
|
||||
}
|
||||
if (!optionalExistingTermConceptMapByUrl.isPresent()) {
|
||||
try {
|
||||
if (isNotBlank(source)) {
|
||||
termConceptMap.setSource(source);
|
||||
}
|
||||
if (isNotBlank(target)) {
|
||||
termConceptMap.setTarget(target);
|
||||
}
|
||||
} catch (FHIRException fe) {
|
||||
throw new InternalErrorException(fe);
|
||||
}
|
||||
termConceptMap = myConceptMapDao.save(termConceptMap);
|
||||
int codesSaved = 0;
|
||||
|
||||
if (theConceptMap.hasGroup()) {
|
||||
TermConceptMapGroup termConceptMapGroup;
|
||||
for (ConceptMap.ConceptMapGroupComponent group : theConceptMap.getGroup()) {
|
||||
|
||||
String groupSource = group.getSource();
|
||||
if (isBlank(groupSource)) {
|
||||
groupSource = source;
|
||||
}
|
||||
if (isBlank(groupSource)) {
|
||||
throw new UnprocessableEntityException("ConceptMap[url='" + theConceptMap.getUrl() + "'] contains at least one group without a value in ConceptMap.group.source");
|
||||
}
|
||||
|
||||
String groupTarget = group.getTarget();
|
||||
if (isBlank(groupTarget)) {
|
||||
groupTarget = target;
|
||||
}
|
||||
if (isBlank(groupTarget)) {
|
||||
throw new UnprocessableEntityException("ConceptMap[url='" + theConceptMap.getUrl() + "'] contains at least one group without a value in ConceptMap.group.target");
|
||||
}
|
||||
|
||||
termConceptMapGroup = new TermConceptMapGroup();
|
||||
termConceptMapGroup.setConceptMap(termConceptMap);
|
||||
termConceptMapGroup.setSource(groupSource);
|
||||
termConceptMapGroup.setSourceVersion(group.getSourceVersion());
|
||||
termConceptMapGroup.setTarget(groupTarget);
|
||||
termConceptMapGroup.setTargetVersion(group.getTargetVersion());
|
||||
myConceptMapGroupDao.save(termConceptMapGroup);
|
||||
|
||||
if (group.hasElement()) {
|
||||
TermConceptMapGroupElement termConceptMapGroupElement;
|
||||
for (ConceptMap.SourceElementComponent element : group.getElement()) {
|
||||
if (isBlank(element.getCode())) {
|
||||
continue;
|
||||
}
|
||||
termConceptMapGroupElement = new TermConceptMapGroupElement();
|
||||
termConceptMapGroupElement.setConceptMapGroup(termConceptMapGroup);
|
||||
termConceptMapGroupElement.setCode(element.getCode());
|
||||
termConceptMapGroupElement.setDisplay(element.getDisplay());
|
||||
myConceptMapGroupElementDao.save(termConceptMapGroupElement);
|
||||
|
||||
if (element.hasTarget()) {
|
||||
TermConceptMapGroupElementTarget termConceptMapGroupElementTarget;
|
||||
for (ConceptMap.TargetElementComponent elementTarget : element.getTarget()) {
|
||||
if (isBlank(elementTarget.getCode())) {
|
||||
continue;
|
||||
}
|
||||
termConceptMapGroupElementTarget = new TermConceptMapGroupElementTarget();
|
||||
termConceptMapGroupElementTarget.setConceptMapGroupElement(termConceptMapGroupElement);
|
||||
termConceptMapGroupElementTarget.setCode(elementTarget.getCode());
|
||||
termConceptMapGroupElementTarget.setDisplay(elementTarget.getDisplay());
|
||||
termConceptMapGroupElementTarget.setEquivalence(elementTarget.getEquivalence());
|
||||
myConceptMapGroupElementTargetDao.save(termConceptMapGroupElementTarget);
|
||||
|
||||
if (++codesSaved % 250 == 0) {
|
||||
ourLog.info("Have saved {} codes in ConceptMap", codesSaved);
|
||||
myConceptMapGroupElementTargetDao.flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
TermConceptMap existingTermConceptMap = optionalExistingTermConceptMapByUrl.get();
|
||||
|
||||
if (isBlank(conceptMapVersion)) {
|
||||
String msg = myContext.getLocalizer().getMessage(
|
||||
BaseTermReadSvcImpl.class,
|
||||
"cannotCreateDuplicateConceptMapUrl",
|
||||
conceptMapUrl,
|
||||
existingTermConceptMap.getResource().getIdDt().toUnqualifiedVersionless().getValue());
|
||||
throw new UnprocessableEntityException(msg);
|
||||
|
||||
} else {
|
||||
String msg = myContext.getLocalizer().getMessage(
|
||||
BaseTermReadSvcImpl.class,
|
||||
"cannotCreateDuplicateConceptMapUrlAndVersion",
|
||||
conceptMapUrl, conceptMapVersion,
|
||||
existingTermConceptMap.getResource().getIdDt().toUnqualifiedVersionless().getValue());
|
||||
throw new UnprocessableEntityException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
ourLog.info("Done storing TermConceptMap[{}] for {}", termConceptMap.getId(), theConceptMap.getIdElement().toVersionless().getValueAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.REQUIRED)
|
||||
public TranslateConceptResults translate(TranslationRequest theTranslationRequest) {
|
||||
TranslateConceptResults retVal = new TranslateConceptResults();
|
||||
|
||||
CriteriaBuilder criteriaBuilder = myEntityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<TermConceptMapGroupElementTarget> query = criteriaBuilder.createQuery(TermConceptMapGroupElementTarget.class);
|
||||
Root<TermConceptMapGroupElementTarget> root = query.from(TermConceptMapGroupElementTarget.class);
|
||||
|
||||
Join<TermConceptMapGroupElementTarget, TermConceptMapGroupElement> elementJoin = root.join("myConceptMapGroupElement");
|
||||
Join<TermConceptMapGroupElement, TermConceptMapGroup> groupJoin = elementJoin.join("myConceptMapGroup");
|
||||
Join<TermConceptMapGroup, TermConceptMap> conceptMapJoin = groupJoin.join("myConceptMap");
|
||||
|
||||
List<TranslationQuery> translationQueries = theTranslationRequest.getTranslationQueries();
|
||||
List<TranslateConceptResult> cachedTargets;
|
||||
ArrayList<Predicate> predicates;
|
||||
Coding coding;
|
||||
|
||||
//-- get the latest ConceptMapVersion if theTranslationRequest has ConceptMap url but no ConceptMap version
|
||||
String latestConceptMapVersion = null;
|
||||
if (theTranslationRequest.hasUrl() && !theTranslationRequest.hasConceptMapVersion())
|
||||
latestConceptMapVersion = getLatestConceptMapVersion(theTranslationRequest);
|
||||
|
||||
for (TranslationQuery translationQuery : translationQueries) {
|
||||
cachedTargets = myMemoryCacheService.getIfPresent(MemoryCacheService.CacheEnum.CONCEPT_TRANSLATION, translationQuery);
|
||||
if (cachedTargets == null) {
|
||||
final List<TranslateConceptResult> targets = new ArrayList<>();
|
||||
|
||||
predicates = new ArrayList<>();
|
||||
|
||||
coding = translationQuery.getCoding();
|
||||
if (coding.hasCode()) {
|
||||
predicates.add(criteriaBuilder.equal(elementJoin.get("myCode"), coding.getCode()));
|
||||
} else {
|
||||
throw new InvalidRequestException("A code must be provided for translation to occur.");
|
||||
}
|
||||
|
||||
if (coding.hasSystem()) {
|
||||
predicates.add(criteriaBuilder.equal(groupJoin.get("mySource"), coding.getSystem()));
|
||||
}
|
||||
|
||||
if (coding.hasVersion()) {
|
||||
predicates.add(criteriaBuilder.equal(groupJoin.get("mySourceVersion"), coding.getVersion()));
|
||||
}
|
||||
|
||||
if (translationQuery.hasTargetSystem()) {
|
||||
predicates.add(criteriaBuilder.equal(groupJoin.get("myTarget"), translationQuery.getTargetSystem().getValueAsString()));
|
||||
}
|
||||
|
||||
if (translationQuery.hasUrl()) {
|
||||
predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myUrl"), translationQuery.getUrl().getValueAsString()));
|
||||
if (translationQuery.hasConceptMapVersion()) {
|
||||
// both url and conceptMapVersion
|
||||
predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myVersion"), translationQuery.getConceptMapVersion().getValueAsString()));
|
||||
} else {
|
||||
if (StringUtils.isNotBlank(latestConceptMapVersion)) {
|
||||
// only url and use latestConceptMapVersion
|
||||
predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myVersion"), latestConceptMapVersion));
|
||||
} else {
|
||||
predicates.add(criteriaBuilder.isNull(conceptMapJoin.get("myVersion")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (translationQuery.hasSource()) {
|
||||
predicates.add(criteriaBuilder.equal(conceptMapJoin.get("mySource"), translationQuery.getSource().getValueAsString()));
|
||||
}
|
||||
|
||||
if (translationQuery.hasTarget()) {
|
||||
predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myTarget"), translationQuery.getTarget().getValueAsString()));
|
||||
}
|
||||
|
||||
if (translationQuery.hasResourceId()) {
|
||||
predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myResourcePid"), translationQuery.getResourceId()));
|
||||
}
|
||||
|
||||
Predicate outerPredicate = criteriaBuilder.and(predicates.toArray(new Predicate[0]));
|
||||
query.where(outerPredicate);
|
||||
|
||||
// Use scrollable results.
|
||||
final TypedQuery<TermConceptMapGroupElementTarget> typedQuery = myEntityManager.createQuery(query.select(root));
|
||||
org.hibernate.query.Query<TermConceptMapGroupElementTarget> hibernateQuery = (org.hibernate.query.Query<TermConceptMapGroupElementTarget>) typedQuery;
|
||||
hibernateQuery.setFetchSize(myFetchSize);
|
||||
ScrollableResults scrollableResults = hibernateQuery.scroll(ScrollMode.FORWARD_ONLY);
|
||||
try (ScrollableResultsIterator<TermConceptMapGroupElementTarget> scrollableResultsIterator = new ScrollableResultsIterator<>(scrollableResults)) {
|
||||
|
||||
Set<TermConceptMapGroupElementTarget> matches = new HashSet<>();
|
||||
while (scrollableResultsIterator.hasNext()) {
|
||||
TermConceptMapGroupElementTarget next = scrollableResultsIterator.next();
|
||||
if (matches.add(next)) {
|
||||
|
||||
TranslateConceptResult translationMatch = new TranslateConceptResult();
|
||||
if (next.getEquivalence() != null) {
|
||||
translationMatch.setEquivalence(next.getEquivalence().toCode());
|
||||
}
|
||||
|
||||
translationMatch.setCode(next.getCode());
|
||||
translationMatch.setSystem(next.getSystem());
|
||||
translationMatch.setSystemVersion(next.getSystemVersion());
|
||||
translationMatch.setDisplay(next.getDisplay());
|
||||
translationMatch.setValueSet(next.getValueSet());
|
||||
translationMatch.setSystemVersion(next.getSystemVersion());
|
||||
translationMatch.setConceptMapUrl(next.getConceptMapUrl());
|
||||
|
||||
targets.add(translationMatch);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ourLastResultsFromTranslationCache = false; // For testing.
|
||||
myMemoryCacheService.put(MemoryCacheService.CacheEnum.CONCEPT_TRANSLATION, translationQuery, targets);
|
||||
retVal.getResults().addAll(targets);
|
||||
} else {
|
||||
ourLastResultsFromTranslationCache = true; // For testing.
|
||||
retVal.getResults().addAll(cachedTargets);
|
||||
}
|
||||
}
|
||||
|
||||
buildTranslationResult(retVal);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.REQUIRED)
|
||||
public TranslateConceptResults translateWithReverse(TranslationRequest theTranslationRequest) {
|
||||
TranslateConceptResults retVal = new TranslateConceptResults();
|
||||
|
||||
CriteriaBuilder criteriaBuilder = myEntityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<TermConceptMapGroupElement> query = criteriaBuilder.createQuery(TermConceptMapGroupElement.class);
|
||||
Root<TermConceptMapGroupElement> root = query.from(TermConceptMapGroupElement.class);
|
||||
|
||||
Join<TermConceptMapGroupElement, TermConceptMapGroupElementTarget> targetJoin = root.join("myConceptMapGroupElementTargets");
|
||||
Join<TermConceptMapGroupElement, TermConceptMapGroup> groupJoin = root.join("myConceptMapGroup");
|
||||
Join<TermConceptMapGroup, TermConceptMap> conceptMapJoin = groupJoin.join("myConceptMap");
|
||||
|
||||
List<TranslationQuery> translationQueries = theTranslationRequest.getTranslationQueries();
|
||||
List<TranslateConceptResult> cachedElements;
|
||||
ArrayList<Predicate> predicates;
|
||||
Coding coding;
|
||||
|
||||
//-- get the latest ConceptMapVersion if theTranslationRequest has ConceptMap url but no ConceptMap version
|
||||
String latestConceptMapVersion = null;
|
||||
if (theTranslationRequest.hasUrl() && !theTranslationRequest.hasConceptMapVersion())
|
||||
latestConceptMapVersion = getLatestConceptMapVersion(theTranslationRequest);
|
||||
|
||||
for (TranslationQuery translationQuery : translationQueries) {
|
||||
cachedElements = myMemoryCacheService.getIfPresent(MemoryCacheService.CacheEnum.CONCEPT_TRANSLATION_REVERSE, translationQuery);
|
||||
if (cachedElements == null) {
|
||||
final List<TranslateConceptResult> elements = new ArrayList<>();
|
||||
|
||||
predicates = new ArrayList<>();
|
||||
|
||||
coding = translationQuery.getCoding();
|
||||
String targetCode;
|
||||
String targetCodeSystem = null;
|
||||
if (coding.hasCode()) {
|
||||
predicates.add(criteriaBuilder.equal(targetJoin.get("myCode"), coding.getCode()));
|
||||
targetCode = coding.getCode();
|
||||
} else {
|
||||
throw new InvalidRequestException("A code must be provided for translation to occur.");
|
||||
}
|
||||
|
||||
if (coding.hasSystem()) {
|
||||
predicates.add(criteriaBuilder.equal(groupJoin.get("myTarget"), coding.getSystem()));
|
||||
targetCodeSystem = coding.getSystem();
|
||||
}
|
||||
|
||||
if (coding.hasVersion()) {
|
||||
predicates.add(criteriaBuilder.equal(groupJoin.get("myTargetVersion"), coding.getVersion()));
|
||||
}
|
||||
|
||||
if (translationQuery.hasUrl()) {
|
||||
predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myUrl"), translationQuery.getUrl().getValueAsString()));
|
||||
if (translationQuery.hasConceptMapVersion()) {
|
||||
// both url and conceptMapVersion
|
||||
predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myVersion"), translationQuery.getConceptMapVersion().getValueAsString()));
|
||||
} else {
|
||||
if (StringUtils.isNotBlank(latestConceptMapVersion)) {
|
||||
// only url and use latestConceptMapVersion
|
||||
predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myVersion"), latestConceptMapVersion));
|
||||
} else {
|
||||
predicates.add(criteriaBuilder.isNull(conceptMapJoin.get("myVersion")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (translationQuery.hasTargetSystem()) {
|
||||
predicates.add(criteriaBuilder.equal(groupJoin.get("mySource"), translationQuery.getTargetSystem().getValueAsString()));
|
||||
}
|
||||
|
||||
if (translationQuery.hasSource()) {
|
||||
predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myTarget"), translationQuery.getSource().getValueAsString()));
|
||||
}
|
||||
|
||||
if (translationQuery.hasTarget()) {
|
||||
predicates.add(criteriaBuilder.equal(conceptMapJoin.get("mySource"), translationQuery.getTarget().getValueAsString()));
|
||||
}
|
||||
|
||||
if (translationQuery.hasResourceId()) {
|
||||
predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myResourcePid"), translationQuery.getResourceId()));
|
||||
}
|
||||
|
||||
Predicate outerPredicate = criteriaBuilder.and(predicates.toArray(new Predicate[0]));
|
||||
query.where(outerPredicate);
|
||||
|
||||
// Use scrollable results.
|
||||
final TypedQuery<TermConceptMapGroupElement> typedQuery = myEntityManager.createQuery(query.select(root));
|
||||
org.hibernate.query.Query<TermConceptMapGroupElement> hibernateQuery = (org.hibernate.query.Query<TermConceptMapGroupElement>) typedQuery;
|
||||
hibernateQuery.setFetchSize(myFetchSize);
|
||||
ScrollableResults scrollableResults = hibernateQuery.scroll(ScrollMode.FORWARD_ONLY);
|
||||
try (ScrollableResultsIterator<TermConceptMapGroupElement> scrollableResultsIterator = new ScrollableResultsIterator<>(scrollableResults)) {
|
||||
|
||||
Set<TermConceptMapGroupElementTarget> matches = new HashSet<>();
|
||||
while (scrollableResultsIterator.hasNext()) {
|
||||
TermConceptMapGroupElement nextElement = scrollableResultsIterator.next();
|
||||
|
||||
// TODO: The invocation of the size() below does not seem to be necessary but for some reason, removing it causes tests in TerminologySvcImplR4Test to fail.
|
||||
nextElement.getConceptMapGroupElementTargets().size();
|
||||
|
||||
myEntityManager.detach(nextElement);
|
||||
|
||||
if (isNotBlank(targetCode)) {
|
||||
for (TermConceptMapGroupElementTarget next : nextElement.getConceptMapGroupElementTargets()) {
|
||||
if (matches.add(next)) {
|
||||
if (isBlank(targetCodeSystem) || StringUtils.equals(targetCodeSystem, next.getSystem())) {
|
||||
if (StringUtils.equals(targetCode, next.getCode())) {
|
||||
TranslateConceptResult translationMatch = new TranslateConceptResult();
|
||||
translationMatch.setCode(nextElement.getCode());
|
||||
translationMatch.setSystem(nextElement.getSystem());
|
||||
translationMatch.setSystemVersion(nextElement.getSystemVersion());
|
||||
translationMatch.setDisplay(nextElement.getDisplay());
|
||||
translationMatch.setValueSet(nextElement.getValueSet());
|
||||
translationMatch.setSystemVersion(nextElement.getSystemVersion());
|
||||
translationMatch.setConceptMapUrl(nextElement.getConceptMapUrl());
|
||||
if (next.getEquivalence() != null) {
|
||||
translationMatch.setEquivalence(next.getEquivalence().toCode());
|
||||
}
|
||||
|
||||
if (alreadyContainsMapping(elements, translationMatch) || alreadyContainsMapping(retVal.getResults(), translationMatch)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
elements.add(translationMatch);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ourLastResultsFromTranslationWithReverseCache = false; // For testing.
|
||||
myMemoryCacheService.put(MemoryCacheService.CacheEnum.CONCEPT_TRANSLATION_REVERSE, translationQuery, elements);
|
||||
retVal.getResults().addAll(elements);
|
||||
} else {
|
||||
ourLastResultsFromTranslationWithReverseCache = true; // For testing.
|
||||
retVal.getResults().addAll(cachedElements);
|
||||
}
|
||||
}
|
||||
|
||||
buildTranslationResult(retVal);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private boolean alreadyContainsMapping(List<TranslateConceptResult> elements, TranslateConceptResult translationMatch) {
|
||||
for (TranslateConceptResult nextExistingElement : elements) {
|
||||
if (nextExistingElement.getSystem().equals(translationMatch.getSystem())) {
|
||||
if (nextExistingElement.getSystemVersion().equals(translationMatch.getSystemVersion())) {
|
||||
if (nextExistingElement.getCode().equals(translationMatch.getCode())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void deleteConceptMap(ResourceTable theResourceTable) {
|
||||
// Get existing entity so it can be deleted.
|
||||
Optional<TermConceptMap> optionalExistingTermConceptMapById = myConceptMapDao.findTermConceptMapByResourcePid(theResourceTable.getId());
|
||||
|
||||
if (optionalExistingTermConceptMapById.isPresent()) {
|
||||
TermConceptMap existingTermConceptMap = optionalExistingTermConceptMapById.get();
|
||||
|
||||
ourLog.info("Deleting existing TermConceptMap[{}] and its children...", existingTermConceptMap.getId());
|
||||
for (TermConceptMapGroup group : existingTermConceptMap.getConceptMapGroups()) {
|
||||
|
||||
for (TermConceptMapGroupElement element : group.getConceptMapGroupElements()) {
|
||||
|
||||
for (TermConceptMapGroupElementTarget target : element.getConceptMapGroupElementTargets()) {
|
||||
|
||||
myConceptMapGroupElementTargetDao.deleteTermConceptMapGroupElementTargetById(target.getId());
|
||||
}
|
||||
|
||||
myConceptMapGroupElementDao.deleteTermConceptMapGroupElementById(element.getId());
|
||||
}
|
||||
|
||||
myConceptMapGroupDao.deleteTermConceptMapGroupById(group.getId());
|
||||
}
|
||||
|
||||
myConceptMapDao.deleteTermConceptMapById(existingTermConceptMap.getId());
|
||||
ourLog.info("Done deleting existing TermConceptMap[{}] and its children.", existingTermConceptMap.getId());
|
||||
}
|
||||
}
|
||||
|
||||
// Special case for the translate operation with url and without
|
||||
// conceptMapVersion, find the latest conecptMapVersion
|
||||
private String getLatestConceptMapVersion(TranslationRequest theTranslationRequest) {
|
||||
|
||||
Pageable page = PageRequest.of(0, 1);
|
||||
List<TermConceptMap> theConceptMapList = myConceptMapDao.getTermConceptMapEntitiesByUrlOrderByMostRecentUpdate(page,
|
||||
theTranslationRequest.getUrl().asStringValue());
|
||||
if (!theConceptMapList.isEmpty()) {
|
||||
return theConceptMapList.get(0).getVersion();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void buildTranslationResult(TranslateConceptResults theTranslationResult) {
|
||||
|
||||
String msg;
|
||||
if (theTranslationResult.getResults().isEmpty()) {
|
||||
theTranslationResult.setResult(false);
|
||||
msg = myContext.getLocalizer().getMessage(TermConceptMappingSvcImpl.class, "noMatchesFound");
|
||||
theTranslationResult.setMessage(msg);
|
||||
} else {
|
||||
theTranslationResult.setResult(true);
|
||||
msg = myContext.getLocalizer().getMessage(TermConceptMappingSvcImpl.class, "matchesFound");
|
||||
theTranslationResult.setMessage(msg);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method is present only for unit tests, do not call from client code
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public static void clearOurLastResultsFromTranslationCache() {
|
||||
ourLastResultsFromTranslationCache = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is present only for unit tests, do not call from client code
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public static void clearOurLastResultsFromTranslationWithReverseCache() {
|
||||
ourLastResultsFromTranslationWithReverseCache = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is present only for unit tests, do not call from client code
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static boolean isOurLastResultsFromTranslationCache() {
|
||||
return ourLastResultsFromTranslationCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is present only for unit tests, do not call from client code
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static boolean isOurLastResultsFromTranslationWithReverseCache() {
|
||||
return ourLastResultsFromTranslationWithReverseCache;
|
||||
}
|
||||
|
||||
public static Parameters toParameters(TranslateConceptResults theTranslationResult) {
|
||||
Parameters retVal = new Parameters();
|
||||
|
||||
retVal.addParameter().setName("result").setValue(new BooleanType(theTranslationResult.getResult()));
|
||||
|
||||
if (theTranslationResult.getMessage() != null) {
|
||||
retVal.addParameter().setName("message").setValue(new StringType(theTranslationResult.getMessage()));
|
||||
}
|
||||
|
||||
for (TranslateConceptResult translationMatch : theTranslationResult.getResults()) {
|
||||
Parameters.ParametersParameterComponent matchParam = retVal.addParameter().setName("match");
|
||||
populateTranslateMatchParts(translationMatch, matchParam);
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private static void populateTranslateMatchParts(TranslateConceptResult theTranslationMatch, Parameters.ParametersParameterComponent theParam) {
|
||||
if (theTranslationMatch.getEquivalence() != null) {
|
||||
theParam.addPart().setName("equivalence").setValue(new CodeType(theTranslationMatch.getEquivalence()));
|
||||
}
|
||||
|
||||
if (isNotBlank(theTranslationMatch.getSystem()) || isNotBlank(theTranslationMatch.getCode()) || isNotBlank(theTranslationMatch.getDisplay())) {
|
||||
Coding value = new Coding(theTranslationMatch.getSystem(), theTranslationMatch.getCode(), theTranslationMatch.getDisplay());
|
||||
|
||||
if (isNotBlank(theTranslationMatch.getSystemVersion())) {
|
||||
value.setVersion(theTranslationMatch.getSystemVersion());
|
||||
}
|
||||
|
||||
theParam.addPart().setName("concept").setValue(value);
|
||||
}
|
||||
|
||||
if (isNotBlank(theTranslationMatch.getConceptMapUrl())) {
|
||||
theParam.addPart().setName("source").setValue(new UriType(theTranslationMatch.getConceptMapUrl()));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,6 +22,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
|
|||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static org.hl7.fhir.convertors.conv30_40.ValueSet30_40.convertValueSet;
|
||||
|
@ -61,7 +62,7 @@ public class TermReadSvcDstu3 extends BaseTermReadSvcImpl implements IValidation
|
|||
|
||||
|
||||
@Override
|
||||
public ValueSetExpansionOutcome expandValueSet(ValidationSupportContext theValidationSupportContext, ValueSetExpansionOptions theExpansionOptions, IBaseResource theValueSetToExpand) {
|
||||
public ValueSetExpansionOutcome expandValueSet(ValidationSupportContext theValidationSupportContext, ValueSetExpansionOptions theExpansionOptions, @Nonnull IBaseResource theValueSetToExpand) {
|
||||
try {
|
||||
org.hl7.fhir.r4.model.ValueSet valueSetToExpandR4;
|
||||
valueSetToExpandR4 = toCanonicalValueSet(theValueSetToExpand);
|
||||
|
|
|
@ -18,6 +18,7 @@ import org.hl7.fhir.r4.model.ValueSet;
|
|||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.transaction.Transactional;
|
||||
|
||||
/*
|
||||
|
@ -59,7 +60,7 @@ public class TermReadSvcR4 extends BaseTermReadSvcImpl implements ITermReadSvcR4
|
|||
|
||||
@Transactional(dontRollbackOn = {ExpansionTooCostlyException.class})
|
||||
@Override
|
||||
public IValidationSupport.ValueSetExpansionOutcome expandValueSet(ValidationSupportContext theValidationSupportContext, ValueSetExpansionOptions theExpansionOptions, IBaseResource theValueSetToExpand) {
|
||||
public IValidationSupport.ValueSetExpansionOutcome expandValueSet(ValidationSupportContext theValidationSupportContext, ValueSetExpansionOptions theExpansionOptions, @Nonnull IBaseResource theValueSetToExpand) {
|
||||
ValueSet expanded = super.expandValueSet(theExpansionOptions, (ValueSet) theValueSetToExpand);
|
||||
return new IValidationSupport.ValueSetExpansionOutcome(expanded);
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.hl7.fhir.utilities.validation.ValidationOptions;
|
|||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.transaction.Transactional;
|
||||
|
||||
|
@ -56,7 +57,7 @@ public class TermReadSvcR5 extends BaseTermReadSvcImpl implements IValidationSup
|
|||
|
||||
@Override
|
||||
@Transactional(dontRollbackOn = {ExpansionTooCostlyException.class})
|
||||
public ValueSetExpansionOutcome expandValueSet(ValidationSupportContext theValidationSupportContext, ValueSetExpansionOptions theExpansionOptions, IBaseResource theValueSetToExpand) {
|
||||
public ValueSetExpansionOutcome expandValueSet(ValidationSupportContext theValidationSupportContext, ValueSetExpansionOptions theExpansionOptions, @Nonnull IBaseResource theValueSetToExpand) {
|
||||
ValueSet valueSetToExpand = (ValueSet) theValueSetToExpand;
|
||||
org.hl7.fhir.r4.model.ValueSet expandedR4 = super.expandValueSet(theExpansionOptions, org.hl7.fhir.convertors.conv40_50.ValueSet40_50.convertValueSet(valueSetToExpand));
|
||||
return new ValueSetExpansionOutcome(org.hl7.fhir.convertors.conv40_50.ValueSet40_50.convertValueSet(expandedR4));
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
package ca.uhn.fhir.jpa.term.api;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2021 Smile CDR, Inc.
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.jpa.api.model.TranslationRequest;
|
||||
import ca.uhn.fhir.context.support.TranslateConceptResults;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import org.hl7.fhir.r4.model.ConceptMap;
|
||||
|
||||
public interface ITermConceptMappingSvc extends IValidationSupport {
|
||||
|
||||
|
||||
TranslateConceptResults translate(TranslationRequest theTranslationRequest);
|
||||
|
||||
TranslateConceptResults translateWithReverse(TranslationRequest theTranslationRequest);
|
||||
|
||||
void deleteConceptMapAndChildren(ResourceTable theResourceTable);
|
||||
|
||||
void storeTermConceptMapAndChildren(ResourceTable theResourceTable, ConceptMap theConceptMap);
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -91,18 +91,10 @@ public interface ITermReadSvc extends IValidationSupport {
|
|||
|
||||
CodeSystem fetchCanonicalCodeSystemFromCompleteContext(String theSystem);
|
||||
|
||||
void deleteConceptMapAndChildren(ResourceTable theResourceTable);
|
||||
|
||||
void deleteValueSetAndChildren(ResourceTable theResourceTable);
|
||||
|
||||
void storeTermConceptMapAndChildren(ResourceTable theResourceTable, ConceptMap theConceptMap);
|
||||
|
||||
void storeTermValueSet(ResourceTable theResourceTable, ValueSet theValueSet);
|
||||
|
||||
List<TermConceptMapGroupElementTarget> translate(TranslationRequest theTranslationRequest);
|
||||
|
||||
List<TermConceptMapGroupElement> translateWithReverse(TranslationRequest theTranslationRequest);
|
||||
|
||||
IFhirResourceDaoCodeSystem.SubsumesResult subsumes(IPrimitiveType<String> theCodeA, IPrimitiveType<String> theCodeB, IPrimitiveType<String> theSystem, IBaseCoding theCodingA, IBaseCoding theCodingB);
|
||||
|
||||
void preExpandDeferredValueSetsToTerminologyTables();
|
||||
|
|
|
@ -20,8 +20,10 @@ package ca.uhn.fhir.jpa.util;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.EnumMap;
|
||||
|
@ -39,6 +41,9 @@ import java.util.function.Function;
|
|||
*/
|
||||
public class MemoryCacheService {
|
||||
|
||||
@Autowired
|
||||
private DaoConfig myDaoConfig;
|
||||
|
||||
private EnumMap<CacheEnum, Cache<?, ?>> myCaches;
|
||||
|
||||
@PostConstruct
|
||||
|
@ -47,7 +52,23 @@ public class MemoryCacheService {
|
|||
myCaches = new EnumMap<>(CacheEnum.class);
|
||||
|
||||
for (CacheEnum next : CacheEnum.values()) {
|
||||
Cache<Object, Object> nextCache = Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).maximumSize(10000).build();
|
||||
|
||||
long timeoutSeconds;
|
||||
switch (next) {
|
||||
case CONCEPT_TRANSLATION:
|
||||
case CONCEPT_TRANSLATION_REVERSE:
|
||||
timeoutSeconds = myDaoConfig.getTranslationCachesExpireAfterWriteInMinutes() * 1000;
|
||||
break;
|
||||
case TAG_DEFINITION:
|
||||
case PERSISTENT_ID:
|
||||
case RESOURCE_LOOKUP:
|
||||
case FORCED_ID:
|
||||
default:
|
||||
timeoutSeconds = 60;
|
||||
break;
|
||||
}
|
||||
|
||||
Cache<Object, Object> nextCache = Caffeine.newBuilder().expireAfterWrite(timeoutSeconds, TimeUnit.MINUTES).maximumSize(10000).build();
|
||||
myCaches.put(next, nextCache);
|
||||
}
|
||||
|
||||
|
@ -85,6 +106,8 @@ public class MemoryCacheService {
|
|||
PERSISTENT_ID,
|
||||
RESOURCE_LOOKUP,
|
||||
FORCED_ID,
|
||||
CONCEPT_TRANSLATION,
|
||||
CONCEPT_TRANSLATION_REVERSE
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ package ca.uhn.fhir.jpa.validation;
|
|||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.jpa.packages.NpmJpaValidationSupport;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermConceptMappingSvc;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
|
||||
import org.hl7.fhir.common.hapi.validation.support.CommonCodeSystemsTerminologyService;
|
||||
import org.hl7.fhir.common.hapi.validation.support.SnapshotGeneratingValidationSupport;
|
||||
|
@ -49,6 +50,8 @@ public class JpaValidationSupportChain extends ValidationSupportChain {
|
|||
private ITermReadSvc myTerminologyService;
|
||||
@Autowired
|
||||
private NpmJpaValidationSupport myNpmJpaValidationSupport;
|
||||
@Autowired
|
||||
private ITermConceptMappingSvc myConceptMappingSvc;
|
||||
|
||||
public JpaValidationSupportChain(FhirContext theFhirContext) {
|
||||
myFhirContext = theFhirContext;
|
||||
|
@ -73,6 +76,7 @@ public class JpaValidationSupportChain extends ValidationSupportChain {
|
|||
addValidationSupport(new InMemoryTerminologyServerValidationSupport(myFhirContext));
|
||||
addValidationSupport(myNpmJpaValidationSupport);
|
||||
addValidationSupport(new CommonCodeSystemsTerminologyService(myFhirContext));
|
||||
addValidationSupport(myConceptMappingSvc);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc;
|
|||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
|
||||
import ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl;
|
||||
import ca.uhn.fhir.jpa.term.TermConceptMappingSvcImpl;
|
||||
import ca.uhn.fhir.jpa.term.TermDeferredStorageSvcImpl;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc;
|
||||
|
@ -363,8 +364,8 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
|
|||
public void afterClearTerminologyCaches() {
|
||||
BaseTermReadSvcImpl baseHapiTerminologySvc = AopTestUtils.getTargetObject(myTermSvc);
|
||||
baseHapiTerminologySvc.clearCaches();
|
||||
BaseTermReadSvcImpl.clearOurLastResultsFromTranslationCache();
|
||||
BaseTermReadSvcImpl.clearOurLastResultsFromTranslationWithReverseCache();
|
||||
TermConceptMappingSvcImpl.clearOurLastResultsFromTranslationCache();
|
||||
TermConceptMappingSvcImpl.clearOurLastResultsFromTranslationWithReverseCache();
|
||||
TermDeferredStorageSvcImpl deferredSvc = AopTestUtils.getTargetObject(myTerminologyDeferredStorageSvc);
|
||||
deferredSvc.clearDeferred();
|
||||
}
|
||||
|
|
|
@ -1,17 +1,12 @@
|
|||
package ca.uhn.fhir.jpa.dao.dstu3;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import ca.uhn.fhir.context.support.TranslateConceptResult;
|
||||
import ca.uhn.fhir.jpa.api.model.TranslationRequest;
|
||||
import ca.uhn.fhir.context.support.TranslateConceptResults;
|
||||
import ca.uhn.fhir.jpa.entity.TermConceptMap;
|
||||
import org.hl7.fhir.dstu3.model.ConceptMap;
|
||||
import org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.r4.model.Coding;
|
||||
import org.hl7.fhir.r4.model.Enumerations;
|
||||
import org.hl7.fhir.r4.model.UriType;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
@ -23,10 +18,13 @@ import org.springframework.transaction.annotation.Transactional;
|
|||
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import ca.uhn.fhir.jpa.api.model.TranslationMatch;
|
||||
import ca.uhn.fhir.jpa.api.model.TranslationRequest;
|
||||
import ca.uhn.fhir.jpa.api.model.TranslationResult;
|
||||
import ca.uhn.fhir.jpa.entity.TermConceptMap;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class FhirResourceDaoDstu3ConceptMapTest extends BaseJpaDstu3Test {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu3ConceptMapTest.class);
|
||||
|
@ -52,9 +50,9 @@ public class FhirResourceDaoDstu3ConceptMapTest extends BaseJpaDstu3Test {
|
|||
.setCode("12345");
|
||||
translationRequest.setTargetSystem(new UriType(CS_URL_3));
|
||||
|
||||
TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null);
|
||||
TranslateConceptResults translationResult = myConceptMapDao.translate(translationRequest, null);
|
||||
|
||||
assertFalse(translationResult.getResult().booleanValue());
|
||||
assertFalse(translationResult.getResult());
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -76,32 +74,28 @@ public class FhirResourceDaoDstu3ConceptMapTest extends BaseJpaDstu3Test {
|
|||
.setCode("12345");
|
||||
translationRequest.setTargetSystem(new UriType(CS_URL_3));
|
||||
|
||||
TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null);
|
||||
TranslateConceptResults translationResult = myConceptMapDao.translate(translationRequest, null);
|
||||
|
||||
assertTrue(translationResult.getResult().booleanValue());
|
||||
assertEquals("Matches found!", translationResult.getMessage().getValueAsString());
|
||||
assertTrue(translationResult.getResult());
|
||||
assertEquals("Matches found", translationResult.getMessage());
|
||||
|
||||
assertEquals(2, translationResult.getMatches().size());
|
||||
assertEquals(2, translationResult.getResults().size());
|
||||
|
||||
TranslationMatch translationMatch = translationResult.getMatches().get(0);
|
||||
assertEquals(Enumerations.ConceptMapEquivalence.EQUAL.toCode(), translationMatch.getEquivalence().getCode());
|
||||
Coding concept = translationMatch.getConcept();
|
||||
assertEquals("56789", concept.getCode());
|
||||
assertEquals("Target Code 56789", concept.getDisplay());
|
||||
assertEquals(CS_URL_3, concept.getSystem());
|
||||
assertEquals("Version 4", concept.getVersion());
|
||||
assertFalse(concept.getUserSelected());
|
||||
assertEquals(CM_URL, translationMatch.getSource().getValueAsString());
|
||||
TranslateConceptResult translationMatch = translationResult.getResults().get(0);
|
||||
assertEquals(Enumerations.ConceptMapEquivalence.EQUAL.toCode(), translationMatch.getEquivalence());
|
||||
assertEquals("56789", translationMatch.getCode());
|
||||
assertEquals("Target Code 56789", translationMatch.getDisplay());
|
||||
assertEquals(CS_URL_3, translationMatch.getSystem());
|
||||
assertEquals("Version 4", translationMatch.getSystemVersion());
|
||||
assertEquals(CM_URL, translationMatch.getConceptMapUrl());
|
||||
|
||||
translationMatch = translationResult.getMatches().get(1);
|
||||
assertEquals(Enumerations.ConceptMapEquivalence.WIDER.toCode(), translationMatch.getEquivalence().getCode());
|
||||
concept = translationMatch.getConcept();
|
||||
assertEquals("67890", concept.getCode());
|
||||
assertEquals("Target Code 67890", concept.getDisplay());
|
||||
assertEquals(CS_URL_3, concept.getSystem());
|
||||
assertEquals("Version 4", concept.getVersion());
|
||||
assertFalse(concept.getUserSelected());
|
||||
assertEquals(CM_URL, translationMatch.getSource().getValueAsString());
|
||||
translationMatch = translationResult.getResults().get(1);
|
||||
assertEquals(Enumerations.ConceptMapEquivalence.WIDER.toCode(), translationMatch.getEquivalence());
|
||||
assertEquals("67890", translationMatch.getCode());
|
||||
assertEquals("Target Code 67890", translationMatch.getDisplay());
|
||||
assertEquals(CS_URL_3, translationMatch.getSystem());
|
||||
assertEquals("Version 4", translationMatch.getSystemVersion());
|
||||
assertEquals(CM_URL, translationMatch.getConceptMapUrl());
|
||||
// </editor-fold>
|
||||
}
|
||||
});
|
||||
|
|
|
@ -74,9 +74,11 @@ import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc;
|
|||
import ca.uhn.fhir.jpa.search.warm.ICacheWarmingSvc;
|
||||
import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryImpl;
|
||||
import ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl;
|
||||
import ca.uhn.fhir.jpa.term.TermConceptMappingSvcImpl;
|
||||
import ca.uhn.fhir.jpa.term.TermDeferredStorageSvcImpl;
|
||||
import ca.uhn.fhir.jpa.term.ValueSetExpansionR4Test;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermConceptMappingSvc;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermLoaderSvc;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
|
||||
|
@ -197,6 +199,8 @@ public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuil
|
|||
private static IValidationSupport ourJpaValidationSupportChainR4;
|
||||
private static IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> ourValueSetDao;
|
||||
|
||||
@Autowired
|
||||
protected ITermConceptMappingSvc myConceptMappingSvc;
|
||||
@Autowired
|
||||
protected IPartitionLookupSvc myPartitionConfigSvc;
|
||||
@Autowired
|
||||
|
@ -510,8 +514,8 @@ public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuil
|
|||
public void afterClearTerminologyCaches() {
|
||||
BaseTermReadSvcImpl baseHapiTerminologySvc = AopTestUtils.getTargetObject(myTermSvc);
|
||||
baseHapiTerminologySvc.clearCaches();
|
||||
BaseTermReadSvcImpl.clearOurLastResultsFromTranslationCache();
|
||||
BaseTermReadSvcImpl.clearOurLastResultsFromTranslationWithReverseCache();
|
||||
TermConceptMappingSvcImpl.clearOurLastResultsFromTranslationCache();
|
||||
TermConceptMappingSvcImpl.clearOurLastResultsFromTranslationWithReverseCache();
|
||||
TermDeferredStorageSvcImpl termDeferredStorageSvc = AopTestUtils.getTargetObject(myTerminologyDeferredStorageSvc);
|
||||
termDeferredStorageSvc.clearDeferred();
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -59,6 +59,7 @@ import ca.uhn.fhir.jpa.search.warm.ICacheWarmingSvc;
|
|||
import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryImpl;
|
||||
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionRegistry;
|
||||
import ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl;
|
||||
import ca.uhn.fhir.jpa.term.TermConceptMappingSvcImpl;
|
||||
import ca.uhn.fhir.jpa.term.TermDeferredStorageSvcImpl;
|
||||
import ca.uhn.fhir.jpa.term.ValueSetExpansionR4Test;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
|
||||
|
@ -437,8 +438,8 @@ public abstract class BaseJpaR5Test extends BaseJpaTest {
|
|||
public void afterClearTerminologyCaches() {
|
||||
BaseTermReadSvcImpl baseHapiTerminologySvc = AopTestUtils.getTargetObject(myTermSvc);
|
||||
baseHapiTerminologySvc.clearCaches();
|
||||
BaseTermReadSvcImpl.clearOurLastResultsFromTranslationCache();
|
||||
BaseTermReadSvcImpl.clearOurLastResultsFromTranslationWithReverseCache();
|
||||
TermConceptMappingSvcImpl.clearOurLastResultsFromTranslationCache();
|
||||
TermConceptMappingSvcImpl.clearOurLastResultsFromTranslationWithReverseCache();
|
||||
TermDeferredStorageSvcImpl deferredStorageSvc = AopTestUtils.getTargetObject(myTermDeferredStorageSvc);
|
||||
deferredStorageSvc.clearDeferred();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
package ca.uhn.fhir.jpa.interceptor;
|
||||
|
||||
import ca.uhn.fhir.interceptor.api.IInterceptorService;
|
||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||
import ca.uhn.fhir.jpa.provider.r4.BaseResourceProviderR4Test;
|
||||
import ca.uhn.fhir.rest.server.interceptor.ResponseTerminologyTranslationInterceptor;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.r4.model.Coding;
|
||||
import org.hl7.fhir.r4.model.Observation;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
public class ResponseTerminologyTranslationInterceptorTest extends BaseResourceProviderR4Test {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResponseTerminologyTranslationInterceptorTest.class);
|
||||
|
||||
@Autowired
|
||||
private DaoRegistry myDaoRegistry;
|
||||
@Autowired
|
||||
private IInterceptorService myInterceptorBroadcaster;
|
||||
@Autowired
|
||||
private ResponseTerminologyTranslationInterceptor myResponseTerminologyTranslationInterceptor;
|
||||
|
||||
@BeforeEach
|
||||
public void beforeEach() {
|
||||
myConceptMapDao.create(createConceptMap());
|
||||
ourRestServer.registerInterceptor(myResponseTerminologyTranslationInterceptor);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void afterEach() {
|
||||
myResponseTerminologyTranslationInterceptor.clearMappingSpecifications();
|
||||
ourRestServer.unregisterInterceptor(myResponseTerminologyTranslationInterceptor);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMapConcept_MappingFound() {
|
||||
myResponseTerminologyTranslationInterceptor.addMappingSpecification(CS_URL, CS_URL_2);
|
||||
|
||||
Observation observation = new Observation();
|
||||
observation.setStatus(Observation.ObservationStatus.AMENDED);
|
||||
observation .getCode()
|
||||
.addCoding(new Coding(CS_URL, "12345", null));
|
||||
IIdType id = myObservationDao.create(observation).getId();
|
||||
|
||||
// Read it back
|
||||
observation = myClient.read().resource(Observation.class).withId(id).execute();
|
||||
|
||||
assertThat(toCodeStrings(observation).toString(), toCodeStrings(observation), Matchers.contains(
|
||||
"[system=http://example.com/my_code_system, code=12345, display=null]",
|
||||
"[system=http://example.com/my_code_system2, code=34567, display=Target Code 34567]"
|
||||
));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMapConcept_MultipleMappingsFound() {
|
||||
myResponseTerminologyTranslationInterceptor.addMappingSpecification(CS_URL, CS_URL_3);
|
||||
|
||||
Observation observation = new Observation();
|
||||
observation.setStatus(Observation.ObservationStatus.AMENDED);
|
||||
observation .getCode()
|
||||
.addCoding(new Coding(CS_URL, "12345", null));
|
||||
IIdType id = myObservationDao.create(observation).getId();
|
||||
|
||||
// Read it back
|
||||
observation = myClient.read().resource(Observation.class).withId(id).execute();
|
||||
|
||||
assertThat(toCodeStrings(observation).toString(), toCodeStrings(observation), Matchers.contains(
|
||||
"[system=http://example.com/my_code_system, code=12345, display=null]",
|
||||
"[system=http://example.com/my_code_system3, code=56789, display=Target Code 56789]",
|
||||
"[system=http://example.com/my_code_system3, code=67890, display=Target Code 67890]"
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't map if we already have a code in the desired target
|
||||
*/
|
||||
@Test
|
||||
public void testMapConcept_MappingNotNeeded() {
|
||||
myResponseTerminologyTranslationInterceptor.addMappingSpecification(CS_URL, CS_URL_2);
|
||||
|
||||
Observation observation = new Observation();
|
||||
observation.setStatus(Observation.ObservationStatus.AMENDED);
|
||||
observation .getCode()
|
||||
.addCoding(new Coding(CS_URL, "12345", null))
|
||||
.addCoding(new Coding(CS_URL_2, "9999", "Display 9999"));
|
||||
IIdType id = myObservationDao.create(observation).getId();
|
||||
|
||||
// Read it back
|
||||
observation = myClient.read().resource(Observation.class).withId(id).execute();
|
||||
|
||||
assertThat(toCodeStrings(observation).toString(), toCodeStrings(observation), Matchers.contains(
|
||||
"[system=http://example.com/my_code_system, code=12345, display=null]",
|
||||
"[system=http://example.com/my_code_system2, code=9999, display=Display 9999]"
|
||||
));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMapConcept_NoMappingExists() {
|
||||
myResponseTerminologyTranslationInterceptor.addMappingSpecification(CS_URL, CS_URL_2);
|
||||
|
||||
Observation observation = new Observation();
|
||||
observation.setStatus(Observation.ObservationStatus.AMENDED);
|
||||
observation .getCode()
|
||||
.addCoding(new Coding(CS_URL, "FOO", null));
|
||||
IIdType id = myObservationDao.create(observation).getId();
|
||||
|
||||
// Read it back
|
||||
observation = myClient.read().resource(Observation.class).withId(id).execute();
|
||||
|
||||
assertThat(toCodeStrings(observation).toString(), toCodeStrings(observation), Matchers.contains(
|
||||
"[system=http://example.com/my_code_system, code=FOO, display=null]"
|
||||
));
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private List<String> toCodeStrings(Observation observation) {
|
||||
return observation.getCode().getCoding().stream().map(t -> "[system=" + t.getSystem() + ", code=" + t.getCode() + ", display=" + t.getDisplay() + "]").collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -95,7 +95,7 @@ public class ResourceProviderDstu3ConceptMapTest extends BaseResourceProviderDst
|
|||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertEquals(2, getNumberOfParametersByName(respParams, "match"));
|
||||
param = getParametersByName(respParams, "match").get(0);
|
||||
|
@ -156,7 +156,7 @@ public class ResourceProviderDstu3ConceptMapTest extends BaseResourceProviderDst
|
|||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertEquals(1, getNumberOfParametersByName(respParams, "match"));
|
||||
param = getParametersByName(respParams, "match").get(0);
|
||||
|
@ -205,7 +205,7 @@ public class ResourceProviderDstu3ConceptMapTest extends BaseResourceProviderDst
|
|||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertEquals(1, getNumberOfParametersByName(respParams, "match"));
|
||||
param = getParametersByName(respParams, "match").get(0);
|
||||
|
@ -255,7 +255,7 @@ public class ResourceProviderDstu3ConceptMapTest extends BaseResourceProviderDst
|
|||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertEquals(1, getNumberOfParametersByName(respParams, "match"));
|
||||
param = getParametersByName(respParams, "match").get(0);
|
||||
|
@ -304,7 +304,7 @@ public class ResourceProviderDstu3ConceptMapTest extends BaseResourceProviderDst
|
|||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertEquals(1, getNumberOfParametersByName(respParams, "match"));
|
||||
param = getParametersByName(respParams, "match").get(0);
|
||||
|
@ -352,7 +352,7 @@ public class ResourceProviderDstu3ConceptMapTest extends BaseResourceProviderDst
|
|||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertEquals(1, getNumberOfParametersByName(respParams, "match"));
|
||||
param = getParametersByName(respParams, "match").get(0);
|
||||
|
@ -400,7 +400,7 @@ public class ResourceProviderDstu3ConceptMapTest extends BaseResourceProviderDst
|
|||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertEquals(1, getNumberOfParametersByName(respParams, "match"));
|
||||
param = getParametersByName(respParams, "match").get(0);
|
||||
|
@ -446,7 +446,7 @@ public class ResourceProviderDstu3ConceptMapTest extends BaseResourceProviderDst
|
|||
assertFalse(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("No matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("No Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
}
|
||||
|
||||
|
@ -480,7 +480,7 @@ public class ResourceProviderDstu3ConceptMapTest extends BaseResourceProviderDst
|
|||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertEquals(1, getNumberOfParametersByName(respParams, "match"));
|
||||
param = getParametersByName(respParams, "match").get(0);
|
||||
|
@ -525,7 +525,7 @@ public class ResourceProviderDstu3ConceptMapTest extends BaseResourceProviderDst
|
|||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertEquals(1, getNumberOfParametersByName(respParams, "match"));
|
||||
param = getParametersByName(respParams, "match").get(0);
|
||||
|
@ -570,7 +570,7 @@ public class ResourceProviderDstu3ConceptMapTest extends BaseResourceProviderDst
|
|||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertEquals(1, getNumberOfParametersByName(respParams, "match"));
|
||||
param = getParametersByName(respParams, "match").get(0);
|
||||
|
@ -615,7 +615,7 @@ public class ResourceProviderDstu3ConceptMapTest extends BaseResourceProviderDst
|
|||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertEquals(1, getNumberOfParametersByName(respParams, "match"));
|
||||
param = getParametersByName(respParams, "match").get(0);
|
||||
|
|
|
@ -567,11 +567,6 @@ public class ConsentInterceptorResourceProviderR4Test extends BaseResourceProvid
|
|||
assertEquals(1, response.getEntry().size());
|
||||
assertNull(response.getTotalElement().getValue());
|
||||
|
||||
// Load next page
|
||||
response = myClient.loadPage().next(response).execute();
|
||||
assertEquals(1, response.getEntry().size());
|
||||
assertNull(response.getTotalElement().getValue());
|
||||
|
||||
StopWatch sw = new StopWatch();
|
||||
while(true) {
|
||||
SearchStatusEnum status = runInTransaction(() -> {
|
||||
|
@ -586,6 +581,11 @@ public class ConsentInterceptorResourceProviderR4Test extends BaseResourceProvid
|
|||
}
|
||||
}
|
||||
|
||||
// Load next page
|
||||
response = myClient.loadPage().next(response).execute();
|
||||
assertEquals(1, response.getEntry().size());
|
||||
assertNull(response.getTotalElement().getValue());
|
||||
|
||||
runInTransaction(() -> {
|
||||
Search search = mySearchEntityDao.findByUuidAndFetchIncludes(searchId).orElseThrow(() -> new IllegalStateException());
|
||||
assertEquals(3, search.getNumFound());
|
||||
|
@ -594,7 +594,12 @@ public class ConsentInterceptorResourceProviderR4Test extends BaseResourceProvid
|
|||
});
|
||||
|
||||
// The paging should have ended now - but the last redacted female result is an empty existing page which should never have been there.
|
||||
assertNull(BundleUtil.getLinkUrlOfType(myFhirCtx, response, "next"));
|
||||
String next = BundleUtil.getLinkUrlOfType(myFhirCtx, response, "next");
|
||||
if (next != null) {
|
||||
response = myClient.loadPage().next(response).execute();
|
||||
fail(myFhirCtx.newJsonParser().encodeResourceToString(response));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -94,7 +94,7 @@ public class ResourceProviderR4ConceptMapTest extends BaseResourceProviderR4Test
|
|||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertEquals(2, getNumberOfParametersByName(respParams, "match"));
|
||||
param = getParametersByName(respParams, "match").get(0);
|
||||
|
@ -152,7 +152,7 @@ public class ResourceProviderR4ConceptMapTest extends BaseResourceProviderR4Test
|
|||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertEquals(1, getNumberOfParametersByName(respParams, "match"));
|
||||
|
||||
|
@ -197,7 +197,7 @@ public class ResourceProviderR4ConceptMapTest extends BaseResourceProviderR4Test
|
|||
assertFalse(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("No matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("No Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertFalse(hasParameterByName(respParams, "match"));
|
||||
}
|
||||
|
@ -230,7 +230,7 @@ public class ResourceProviderR4ConceptMapTest extends BaseResourceProviderR4Test
|
|||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertEquals(3, getNumberOfParametersByName(respParams, "match"));
|
||||
|
||||
|
@ -308,7 +308,7 @@ public class ResourceProviderR4ConceptMapTest extends BaseResourceProviderR4Test
|
|||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertEquals(1, getNumberOfParametersByName(respParams, "match"));
|
||||
|
||||
|
@ -361,7 +361,7 @@ public class ResourceProviderR4ConceptMapTest extends BaseResourceProviderR4Test
|
|||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertEquals(4, getNumberOfParametersByName(respParams, "match"));
|
||||
|
||||
|
@ -452,7 +452,7 @@ public class ResourceProviderR4ConceptMapTest extends BaseResourceProviderR4Test
|
|||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertEquals(3, getNumberOfParametersByName(respParams, "match"));
|
||||
|
||||
|
@ -531,7 +531,7 @@ public class ResourceProviderR4ConceptMapTest extends BaseResourceProviderR4Test
|
|||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertEquals(1, getNumberOfParametersByName(respParams, "match"));
|
||||
|
||||
|
@ -582,7 +582,7 @@ public class ResourceProviderR4ConceptMapTest extends BaseResourceProviderR4Test
|
|||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertEquals(2, getNumberOfParametersByName(respParams, "match"));
|
||||
|
||||
|
@ -647,7 +647,7 @@ public class ResourceProviderR4ConceptMapTest extends BaseResourceProviderR4Test
|
|||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertEquals(1, getNumberOfParametersByName(respParams, "match"));
|
||||
|
||||
|
@ -698,7 +698,7 @@ public class ResourceProviderR4ConceptMapTest extends BaseResourceProviderR4Test
|
|||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertEquals(2, getNumberOfParametersByName(respParams, "match"));
|
||||
|
||||
|
@ -761,7 +761,7 @@ public class ResourceProviderR4ConceptMapTest extends BaseResourceProviderR4Test
|
|||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertEquals(3, getNumberOfParametersByName(respParams, "match"));
|
||||
|
||||
|
@ -838,7 +838,7 @@ public class ResourceProviderR4ConceptMapTest extends BaseResourceProviderR4Test
|
|||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertEquals(3, getNumberOfParametersByName(respParams, "match"));
|
||||
|
||||
|
@ -949,7 +949,7 @@ public class ResourceProviderR4ConceptMapTest extends BaseResourceProviderR4Test
|
|||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertEquals(1, getNumberOfParametersByName(respParams, "match"));
|
||||
param = getParametersByName(respParams, "match").get(0);
|
||||
|
@ -993,7 +993,7 @@ public class ResourceProviderR4ConceptMapTest extends BaseResourceProviderR4Test
|
|||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertEquals(3, getNumberOfParametersByName(respParams, "match"));
|
||||
|
||||
|
@ -1074,7 +1074,7 @@ public class ResourceProviderR4ConceptMapTest extends BaseResourceProviderR4Test
|
|||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertEquals(1, getNumberOfParametersByName(respParams, "match"));
|
||||
|
||||
|
@ -1120,7 +1120,7 @@ public class ResourceProviderR4ConceptMapTest extends BaseResourceProviderR4Test
|
|||
assertFalse(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("No matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("No Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertFalse(hasParameterByName(respParams, "match"));
|
||||
}
|
||||
|
@ -1155,7 +1155,7 @@ public class ResourceProviderR4ConceptMapTest extends BaseResourceProviderR4Test
|
|||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertEquals(2, getNumberOfParametersByName(respParams, "match"));
|
||||
|
||||
|
@ -1221,7 +1221,7 @@ public class ResourceProviderR4ConceptMapTest extends BaseResourceProviderR4Test
|
|||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertEquals(2, getNumberOfParametersByName(respParams, "match"));
|
||||
|
||||
|
@ -1291,7 +1291,7 @@ public class ResourceProviderR4ConceptMapTest extends BaseResourceProviderR4Test
|
|||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertEquals(4, getNumberOfParametersByName(respParams, "match"));
|
||||
|
||||
|
@ -1324,9 +1324,9 @@ public class ResourceProviderR4ConceptMapTest extends BaseResourceProviderR4Test
|
|||
assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString());
|
||||
|
||||
param = getParametersByName(respParams, "match").get(2);
|
||||
assertEquals(2, param.getPart().size());
|
||||
assertEquals(3, param.getPart().size());
|
||||
part = getPartByName(param, "equivalence");
|
||||
assertFalse(part.hasValue());
|
||||
assertEquals("wider", ((CodeType)part.getValue()).getCode());
|
||||
part = getPartByName(param, "concept");
|
||||
coding = (Coding) part.getValue();
|
||||
assertEquals("23456", coding.getCode());
|
||||
|
@ -1384,7 +1384,7 @@ public class ResourceProviderR4ConceptMapTest extends BaseResourceProviderR4Test
|
|||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertEquals(2, getNumberOfParametersByName(respParams, "match"));
|
||||
|
||||
|
@ -1451,7 +1451,7 @@ public class ResourceProviderR4ConceptMapTest extends BaseResourceProviderR4Test
|
|||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertEquals(2, getNumberOfParametersByName(respParams, "match"));
|
||||
|
||||
|
@ -1518,7 +1518,7 @@ public class ResourceProviderR4ConceptMapTest extends BaseResourceProviderR4Test
|
|||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertEquals(1, getNumberOfParametersByName(respParams, "match"));
|
||||
|
||||
|
@ -1571,7 +1571,7 @@ public class ResourceProviderR4ConceptMapTest extends BaseResourceProviderR4Test
|
|||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertEquals(1, getNumberOfParametersByName(respParams, "match"));
|
||||
|
||||
|
@ -1622,7 +1622,7 @@ public class ResourceProviderR4ConceptMapTest extends BaseResourceProviderR4Test
|
|||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertEquals(2, getNumberOfParametersByName(respParams, "match"));
|
||||
|
||||
|
@ -1687,7 +1687,7 @@ public class ResourceProviderR4ConceptMapTest extends BaseResourceProviderR4Test
|
|||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertEquals(2, getNumberOfParametersByName(respParams, "match"));
|
||||
|
||||
|
@ -1783,7 +1783,7 @@ public class ResourceProviderR4ConceptMapTest extends BaseResourceProviderR4Test
|
|||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertEquals(1, getNumberOfParametersByName(respParams, "match"));
|
||||
param = getParametersByName(respParams, "match").get(0);
|
||||
|
@ -1828,7 +1828,7 @@ public class ResourceProviderR4ConceptMapTest extends BaseResourceProviderR4Test
|
|||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertEquals(2, getNumberOfParametersByName(respParams, "match"));
|
||||
|
||||
|
|
|
@ -88,7 +88,7 @@ public class ResourceProviderR5ConceptMapTest extends BaseResourceProviderR5Test
|
|||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertEquals(1, getNumberOfParametersByName(respParams, "match"));
|
||||
param = getParametersByName(respParams, "match").get(0);
|
||||
|
@ -170,7 +170,7 @@ public class ResourceProviderR5ConceptMapTest extends BaseResourceProviderR5Test
|
|||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||
|
||||
param = getParameterByName(respParams, "message");
|
||||
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
|
||||
assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
|
||||
|
||||
assertEquals(1, getNumberOfParametersByName(respParams, "match"));
|
||||
param = getParametersByName(respParams, "match").get(0);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@ -144,13 +144,13 @@
|
|||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-test-utilities</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-jpaserver-test-utilities</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@ -55,13 +55,13 @@
|
|||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-test-utilities</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-jpaserver-test-utilities</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -86,6 +86,7 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
|
|||
//-- add index on HFJ_FORCED_ID
|
||||
version.onTable("HFJ_FORCED_ID").addIndex("20210309.2", "IDX_FORCEID_FID")
|
||||
.unique(false).withColumns("FORCED_ID");
|
||||
|
||||
}
|
||||
|
||||
private void init530() {
|
||||
|
@ -115,9 +116,9 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
|
|||
pkg.addColumn("HASH_IDENTITY_SYS_UNITS").nullable().type(ColumnTypeEnum.LONG);
|
||||
pkg.addColumn("HASH_IDENTITY").nullable().type(ColumnTypeEnum.LONG);
|
||||
pkg.addColumn("SP_VALUE").nullable().type(ColumnTypeEnum.FLOAT);
|
||||
pkg.addIndex("20210109.3", "IDX_SP_QNTY_NRML_HASH").unique(false).withColumns("HASH_IDENTITY","SP_VALUE");
|
||||
pkg.addIndex("20210109.4", "IDX_SP_QNTY_NRML_HASH_UN").unique(false).withColumns("HASH_IDENTITY_AND_UNITS","SP_VALUE");
|
||||
pkg.addIndex("20210109.5", "IDX_SP_QNTY_NRML_HASH_SYSUN").unique(false).withColumns("HASH_IDENTITY_SYS_UNITS","SP_VALUE");
|
||||
pkg.addIndex("20210109.3", "IDX_SP_QNTY_NRML_HASH").unique(false).withColumns("HASH_IDENTITY", "SP_VALUE");
|
||||
pkg.addIndex("20210109.4", "IDX_SP_QNTY_NRML_HASH_UN").unique(false).withColumns("HASH_IDENTITY_AND_UNITS", "SP_VALUE");
|
||||
pkg.addIndex("20210109.5", "IDX_SP_QNTY_NRML_HASH_SYSUN").unique(false).withColumns("HASH_IDENTITY_SYS_UNITS", "SP_VALUE");
|
||||
pkg.addIndex("20210109.6", "IDX_SP_QNTY_NRML_UPDATED").unique(false).withColumns("SP_UPDATED");
|
||||
pkg.addIndex("20210109.7", "IDX_SP_QNTY_NRML_RESID").unique(false).withColumns("RES_ID");
|
||||
|
||||
|
@ -224,7 +225,7 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
|
|||
init510_20200725();
|
||||
|
||||
//EMPI Target Type
|
||||
empiLink.addColumn("20200727.1","TARGET_TYPE").nullable().type(ColumnTypeEnum.STRING, 40);
|
||||
empiLink.addColumn("20200727.1", "TARGET_TYPE").nullable().type(ColumnTypeEnum.STRING, 40);
|
||||
|
||||
//ConceptMap add version for search
|
||||
Builder.BuilderWithTableName trmConceptMap = version.onTable("TRM_CONCEPT_MAP");
|
||||
|
@ -1022,8 +1023,8 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
|
|||
spidxUri
|
||||
.addTask(new CalculateHashesTask(VersionEnum.V3_5_0, "20180903.44")
|
||||
.setColumnName("HASH_IDENTITY")
|
||||
.addCalculator("HASH_IDENTITY", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(new PartitionSettings(), (RequestPartitionId)null, t.getResourceType(), t.getString("SP_NAME")))
|
||||
.addCalculator("HASH_URI", t -> ResourceIndexedSearchParamUri.calculateHashUri(new PartitionSettings(), (RequestPartitionId)null, t.getResourceType(), t.getString("SP_NAME"), t.getString("SP_URI")))
|
||||
.addCalculator("HASH_IDENTITY", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(new PartitionSettings(), (RequestPartitionId) null, t.getResourceType(), t.getString("SP_NAME")))
|
||||
.addCalculator("HASH_URI", t -> ResourceIndexedSearchParamUri.calculateHashUri(new PartitionSettings(), (RequestPartitionId) null, t.getResourceType(), t.getString("SP_NAME"), t.getString("SP_URI")))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1056,7 +1057,7 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
|
|||
Boolean present = columnToBoolean(t.get("SP_PRESENT"));
|
||||
String resType = (String) t.get("RES_TYPE");
|
||||
String paramName = (String) t.get("PARAM_NAME");
|
||||
Long hash = SearchParamPresent.calculateHashPresence(new PartitionSettings(), (RequestPartitionId)null, resType, paramName, present);
|
||||
Long hash = SearchParamPresent.calculateHashPresence(new PartitionSettings(), (RequestPartitionId) null, resType, paramName, present);
|
||||
consolidateSearchParamPresenceIndexesTask.executeSql("HFJ_RES_PARAM_PRESENT", "update HFJ_RES_PARAM_PRESENT set HASH_PRESENCE = ? where PID = ?", hash, pid);
|
||||
});
|
||||
version.addTask(consolidateSearchParamPresenceIndexesTask);
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@ -169,7 +169,7 @@
|
|||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-converter</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
package ca.uhn.fhir.rest.server.interceptor;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR - Server Framework
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2021 Smile CDR, Inc.
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.util.BundleUtil;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class BaseResponseTerminologyInterceptor {
|
||||
protected final IValidationSupport myValidationSupport;
|
||||
protected final FhirContext myContext;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param theValidationSupport The validation support module
|
||||
*/
|
||||
public BaseResponseTerminologyInterceptor(@Nonnull IValidationSupport theValidationSupport) {
|
||||
myValidationSupport = theValidationSupport;
|
||||
Validate.notNull(theValidationSupport, "The validation support must not be null");
|
||||
|
||||
myContext = theValidationSupport.getFhirContext();
|
||||
Validate.notNull(myContext, "The validation support must not return a null context");
|
||||
}
|
||||
|
||||
|
||||
@Nonnull
|
||||
protected List<IBaseResource> toListForProcessing(RequestDetails theRequestDetails, IBaseResource theResource) {
|
||||
|
||||
switch (theRequestDetails.getRestOperationType()) {
|
||||
// Don't apply to these operations
|
||||
case ADD_TAGS:
|
||||
case DELETE_TAGS:
|
||||
case GET_TAGS:
|
||||
case GET_PAGE:
|
||||
case GRAPHQL_REQUEST:
|
||||
case EXTENDED_OPERATION_SERVER:
|
||||
case EXTENDED_OPERATION_TYPE:
|
||||
case EXTENDED_OPERATION_INSTANCE:
|
||||
case CREATE:
|
||||
case DELETE:
|
||||
case TRANSACTION:
|
||||
case UPDATE:
|
||||
case VALIDATE:
|
||||
case METADATA:
|
||||
case META_ADD:
|
||||
case META:
|
||||
case META_DELETE:
|
||||
case PATCH:
|
||||
default:
|
||||
return Collections.emptyList();
|
||||
|
||||
// Do apply to these operations
|
||||
case HISTORY_INSTANCE:
|
||||
case HISTORY_SYSTEM:
|
||||
case HISTORY_TYPE:
|
||||
case SEARCH_SYSTEM:
|
||||
case SEARCH_TYPE:
|
||||
case READ:
|
||||
case VREAD:
|
||||
break;
|
||||
}
|
||||
|
||||
List<IBaseResource> resources;
|
||||
if (theResource instanceof IBaseBundle) {
|
||||
resources = BundleUtil.toListOfResources(myContext, (IBaseBundle) theResource);
|
||||
} else {
|
||||
resources = Collections.singletonList(theResource);
|
||||
}
|
||||
return resources;
|
||||
}
|
||||
|
||||
}
|
|
@ -26,6 +26,9 @@ public class InterceptorOrders {
|
|||
public static final int RESPONSE_HIGHLIGHTER_INTERCEPTOR = 10000;
|
||||
public static final int RESPONSE_SIZE_CAPTURING_INTERCEPTOR_COMPLETED = -1;
|
||||
|
||||
public static final int RESPONSE_TERMINOLOGY_TRANSLATION_INTERCEPTOR = 100;
|
||||
public static final int RESPONSE_TERMINOLOGY_DISPLAY_POPULATION_INTERCEPTOR = 110;
|
||||
|
||||
/**
|
||||
* Non instantiable
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
package ca.uhn.fhir.rest.server.interceptor;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR - Server Framework
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2021 Smile CDR, Inc.
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
||||
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
|
||||
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
|
||||
import ca.uhn.fhir.context.RuntimePrimitiveDatatypeDefinition;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
||||
import ca.uhn.fhir.interceptor.api.Hook;
|
||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.util.FhirTerser;
|
||||
import ca.uhn.fhir.util.IModelVisitor;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static ca.uhn.fhir.rest.server.interceptor.InterceptorOrders.RESPONSE_TERMINOLOGY_DISPLAY_POPULATION_INTERCEPTOR;
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
/**
|
||||
* This interceptor looks for coded data (
|
||||
*
|
||||
* @since 5.4.0
|
||||
*/
|
||||
public class ResponseTerminologyDisplayPopulationInterceptor extends BaseResponseTerminologyInterceptor {
|
||||
|
||||
private final BaseRuntimeChildDefinition myCodingSystemChild;
|
||||
private final BaseRuntimeChildDefinition myCodingCodeChild;
|
||||
private final Class<? extends IBase> myCodingType;
|
||||
private final BaseRuntimeElementCompositeDefinition<?> myCodingDefinitition;
|
||||
private final BaseRuntimeChildDefinition myCodingDisplayChild;
|
||||
private final RuntimePrimitiveDatatypeDefinition myStringDefinition;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param theValidationSupport The validation support module
|
||||
*/
|
||||
public ResponseTerminologyDisplayPopulationInterceptor(IValidationSupport theValidationSupport) {
|
||||
super(theValidationSupport);
|
||||
|
||||
myCodingDefinitition = (BaseRuntimeElementCompositeDefinition<?>) Objects.requireNonNull(myContext.getElementDefinition("Coding"));
|
||||
myCodingType = myCodingDefinitition.getImplementingClass();
|
||||
myCodingSystemChild = myCodingDefinitition.getChildByName("system");
|
||||
myCodingCodeChild = myCodingDefinitition.getChildByName("code");
|
||||
myCodingDisplayChild = myCodingDefinitition.getChildByName("display");
|
||||
|
||||
myStringDefinition = (RuntimePrimitiveDatatypeDefinition) myContext.getElementDefinition("string");
|
||||
}
|
||||
|
||||
@Hook(value = Pointcut.SERVER_OUTGOING_RESPONSE, order = RESPONSE_TERMINOLOGY_DISPLAY_POPULATION_INTERCEPTOR)
|
||||
public void handleResource(RequestDetails theRequestDetails, IBaseResource theResource) {
|
||||
List<IBaseResource> resources = toListForProcessing(theRequestDetails, theResource);
|
||||
|
||||
FhirTerser terser = myContext.newTerser();
|
||||
for (IBaseResource nextResource : resources) {
|
||||
terser.visit(nextResource, new MappingVisitor());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class MappingVisitor implements IModelVisitor {
|
||||
|
||||
@Override
|
||||
public void acceptElement(IBaseResource theResource, IBase theElement, List<String> thePathToElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition<?> theDefinition) {
|
||||
if (myCodingType.isAssignableFrom(theElement.getClass())) {
|
||||
String system = myCodingSystemChild.getAccessor().getFirstValueOrNull(theElement).map(t -> (IPrimitiveType<?>) t).map(t -> t.getValueAsString()).orElse(null);
|
||||
String code = myCodingCodeChild.getAccessor().getFirstValueOrNull(theElement).map(t -> (IPrimitiveType<?>) t).map(t -> t.getValueAsString()).orElse(null);
|
||||
if (isBlank(system) || isBlank(code)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String display = myCodingDisplayChild.getAccessor().getFirstValueOrNull(theElement).map(t -> (IPrimitiveType<?>) t).map(t -> t.getValueAsString()).orElse(null);
|
||||
if (isNotBlank(display)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ValidationSupportContext validationSupportContext = new ValidationSupportContext(myValidationSupport);
|
||||
if (myValidationSupport.isCodeSystemSupported(validationSupportContext, system)) {
|
||||
|
||||
IValidationSupport.LookupCodeResult lookupCodeResult = myValidationSupport.lookupCode(validationSupportContext, system, code);
|
||||
if (lookupCodeResult.isFound()) {
|
||||
String newDisplay = lookupCodeResult.getCodeDisplay();
|
||||
IPrimitiveType<?> newString = myStringDefinition.newInstance(newDisplay);
|
||||
myCodingDisplayChild.getMutator().addValue(theElement, newString);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,191 @@
|
|||
package ca.uhn.fhir.rest.server.interceptor;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR - Server Framework
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2021 Smile CDR, Inc.
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
||||
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
|
||||
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
|
||||
import ca.uhn.fhir.context.RuntimePrimitiveDatatypeDefinition;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.context.support.TranslateConceptResult;
|
||||
import ca.uhn.fhir.context.support.TranslateConceptResults;
|
||||
import ca.uhn.fhir.interceptor.api.Hook;
|
||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.util.FhirTerser;
|
||||
import ca.uhn.fhir.util.IModelVisitor;
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import static ca.uhn.fhir.rest.server.interceptor.InterceptorOrders.RESPONSE_TERMINOLOGY_TRANSLATION_INTERCEPTOR;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
/**
|
||||
* This interceptor leverages ConceptMap resources stored in the repository to automatically map
|
||||
* terminology from one CodeSystem to another at runtime, in resources that are being
|
||||
* returned by the server.
|
||||
* <p>
|
||||
* Mappings are applied only if they are explicitly configured in the interceptor via
|
||||
* the {@link #addMappingSpecification(String, String)} method.
|
||||
* </p>
|
||||
*
|
||||
* @since 5.4.0
|
||||
*/
|
||||
public class ResponseTerminologyTranslationInterceptor extends BaseResponseTerminologyInterceptor {
|
||||
|
||||
private final BaseRuntimeChildDefinition myCodingSystemChild;
|
||||
private final BaseRuntimeChildDefinition myCodingCodeChild;
|
||||
private final BaseRuntimeElementDefinition<IPrimitiveType<?>> myUriDefinition;
|
||||
private final BaseRuntimeElementDefinition<IPrimitiveType<?>> myCodeDefinition;
|
||||
private final Class<? extends IBase> myCodeableConceptType;
|
||||
private final Class<? extends IBase> myCodingType;
|
||||
private final BaseRuntimeChildDefinition myCodeableConceptCodingChild;
|
||||
private final BaseRuntimeElementCompositeDefinition<?> myCodingDefinitition;
|
||||
private final RuntimePrimitiveDatatypeDefinition myStringDefinition;
|
||||
private final BaseRuntimeChildDefinition myCodingDisplayChild;
|
||||
private Map<String, String> myMappingSpecifications = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param theValidationSupport The validation support module
|
||||
*/
|
||||
public ResponseTerminologyTranslationInterceptor(IValidationSupport theValidationSupport) {
|
||||
super(theValidationSupport);
|
||||
|
||||
BaseRuntimeElementCompositeDefinition<?> codeableConceptDef = (BaseRuntimeElementCompositeDefinition<?>) Objects.requireNonNull(myContext.getElementDefinition("CodeableConcept"));
|
||||
myCodeableConceptType = codeableConceptDef.getImplementingClass();
|
||||
myCodeableConceptCodingChild = codeableConceptDef.getChildByName("coding");
|
||||
|
||||
myCodingDefinitition = (BaseRuntimeElementCompositeDefinition<?>) Objects.requireNonNull(myContext.getElementDefinition("Coding"));
|
||||
myCodingType = myCodingDefinitition.getImplementingClass();
|
||||
myCodingSystemChild = myCodingDefinitition.getChildByName("system");
|
||||
myCodingCodeChild = myCodingDefinitition.getChildByName("code");
|
||||
myCodingDisplayChild = myCodingDefinitition.getChildByName("display");
|
||||
|
||||
myUriDefinition = (RuntimePrimitiveDatatypeDefinition) myContext.getElementDefinition("uri");
|
||||
myCodeDefinition = (RuntimePrimitiveDatatypeDefinition) myContext.getElementDefinition("code");
|
||||
myStringDefinition = (RuntimePrimitiveDatatypeDefinition) myContext.getElementDefinition("string");
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a mapping specification using only a source and target CodeSystem URL. Any mappings specified using
|
||||
* this URL
|
||||
*
|
||||
* @param theSourceCodeSystemUrl The source CodeSystem URL
|
||||
* @param theTargetCodeSystemUrl The target CodeSystem URL
|
||||
*/
|
||||
public void addMappingSpecification(String theSourceCodeSystemUrl, String theTargetCodeSystemUrl) {
|
||||
Validate.notBlank(theSourceCodeSystemUrl, "theSourceCodeSystemUrl must not be null or blank");
|
||||
Validate.notBlank(theTargetCodeSystemUrl, "theTargetCodeSystemUrl must not be null or blank");
|
||||
|
||||
myMappingSpecifications.put(theSourceCodeSystemUrl, theTargetCodeSystemUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all mapping specifications
|
||||
*/
|
||||
public void clearMappingSpecifications() {
|
||||
myMappingSpecifications.clear();
|
||||
}
|
||||
|
||||
|
||||
@Hook(value = Pointcut.SERVER_OUTGOING_RESPONSE, order = RESPONSE_TERMINOLOGY_TRANSLATION_INTERCEPTOR)
|
||||
public void handleResource(RequestDetails theRequestDetails, IBaseResource theResource) {
|
||||
List<IBaseResource> resources = toListForProcessing(theRequestDetails, theResource);
|
||||
|
||||
FhirTerser terser = myContext.newTerser();
|
||||
for (IBaseResource nextResource : resources) {
|
||||
terser.visit(nextResource, new MappingVisitor());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private class MappingVisitor implements IModelVisitor {
|
||||
|
||||
@Override
|
||||
public void acceptElement(IBaseResource theResource, IBase theElement, List<String> thePathToElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition<?> theDefinition) {
|
||||
if (myCodeableConceptType.isAssignableFrom(theElement.getClass())) {
|
||||
|
||||
// Find all existing Codings
|
||||
Multimap<String, String> foundSystemsToCodes = ArrayListMultimap.create();
|
||||
List<IBase> nextCodeableConceptCodings = myCodeableConceptCodingChild.getAccessor().getValues(theElement);
|
||||
for (IBase nextCodeableConceptCoding : nextCodeableConceptCodings) {
|
||||
String system = myCodingSystemChild.getAccessor().getFirstValueOrNull(nextCodeableConceptCoding).map(t -> (IPrimitiveType<?>) t).map(t -> t.getValueAsString()).orElse(null);
|
||||
String code = myCodingCodeChild.getAccessor().getFirstValueOrNull(nextCodeableConceptCoding).map(t -> (IPrimitiveType<?>) t).map(t -> t.getValueAsString()).orElse(null);
|
||||
if (isNotBlank(system) && isNotBlank(code) && !foundSystemsToCodes.containsKey(system)) {
|
||||
foundSystemsToCodes.put(system, code);
|
||||
}
|
||||
}
|
||||
|
||||
// Look for mappings
|
||||
for (String nextSourceSystem : foundSystemsToCodes.keySet()) {
|
||||
String wantTargetSystem = myMappingSpecifications.get(nextSourceSystem);
|
||||
if (wantTargetSystem != null) {
|
||||
if (!foundSystemsToCodes.containsKey(wantTargetSystem)) {
|
||||
|
||||
for (String code : foundSystemsToCodes.get(nextSourceSystem)) {
|
||||
TranslateConceptResults translateConceptResults = myValidationSupport.translateConcept(new IValidationSupport.TranslateCodeRequest(nextSourceSystem, code, wantTargetSystem));
|
||||
if (translateConceptResults != null) {
|
||||
List<TranslateConceptResult> mappings = translateConceptResults.getResults();
|
||||
for (TranslateConceptResult nextMapping : mappings) {
|
||||
|
||||
IBase newCoding = createCodingFromMappingTarget(nextMapping);
|
||||
|
||||
// Add coding to existing CodeableConcept
|
||||
myCodeableConceptCodingChild.getMutator().addValue(theElement, newCoding);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private IBase createCodingFromMappingTarget(TranslateConceptResult nextMapping) {
|
||||
IBase newCoding = myCodingDefinitition.newInstance();
|
||||
IPrimitiveType<?> newSystem = myUriDefinition.newInstance(nextMapping.getSystem());
|
||||
myCodingSystemChild.getMutator().addValue(newCoding, newSystem);
|
||||
IPrimitiveType<?> newCode = myCodeDefinition.newInstance(nextMapping.getCode());
|
||||
myCodingCodeChild.getMutator().addValue(newCoding, newCode);
|
||||
if (isNotBlank(nextMapping.getDisplay())) {
|
||||
IPrimitiveType<?> newDisplay = myStringDefinition.newInstance(nextMapping.getDisplay());
|
||||
myCodingDisplayChild.getMutator().addValue(newCoding, newDisplay);
|
||||
}
|
||||
return newCoding;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hapi-fhir-spring-boot-sample-client-apache</artifactId>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hapi-fhir-spring-boot-sample-client-okhttp</artifactId>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hapi-fhir-spring-boot-sample-server-jersey</artifactId>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-spring-boot</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>5.4.0-PRE1-SNAPSHOT</version>
|
||||
<version>5.4.0-PRE2-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package org.hl7.fhir.common.hapi.validation.support;
|
|||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.support.ConceptValidationOptions;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.context.support.TranslateConceptResults;
|
||||
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
||||
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
@ -69,7 +70,7 @@ public class BaseValidationSupportWrapper extends BaseValidationSupport {
|
|||
}
|
||||
|
||||
@Override
|
||||
public IValidationSupport.ValueSetExpansionOutcome expandValueSet(ValidationSupportContext theValidationSupportContext, ValueSetExpansionOptions theExpansionOptions, IBaseResource theValueSetToExpand) {
|
||||
public IValidationSupport.ValueSetExpansionOutcome expandValueSet(ValidationSupportContext theValidationSupportContext, ValueSetExpansionOptions theExpansionOptions, @Nonnull IBaseResource theValueSetToExpand) {
|
||||
return myWrap.expandValueSet(theValidationSupportContext, theExpansionOptions, theValueSetToExpand);
|
||||
}
|
||||
|
||||
|
@ -99,5 +100,8 @@ public class BaseValidationSupportWrapper extends BaseValidationSupport {
|
|||
return myWrap.validateCodeInValueSet(theValidationSupportContext, theValidationOptions, theCodeSystem, theCode, theDisplay, theValueSet);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public TranslateConceptResults translateConcept(TranslateCodeRequest theRequest) {
|
||||
return myWrap.translateConcept(theRequest);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,9 +3,11 @@ package org.hl7.fhir.common.hapi.validation.support;
|
|||
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
||||
import ca.uhn.fhir.context.support.ConceptValidationOptions;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.context.support.TranslateConceptResults;
|
||||
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -28,24 +30,44 @@ public class CachingValidationSupport extends BaseValidationSupportWrapper imple
|
|||
private static final Logger ourLog = LoggerFactory.getLogger(CachingValidationSupport.class);
|
||||
private final Cache<String, Object> myCache;
|
||||
private final Cache<String, Object> myValidateCodeCache;
|
||||
private final Cache<TranslateCodeRequest, Object> myTranslateCodeCache;
|
||||
private final Cache<String, Object> myLookupCodeCache;
|
||||
|
||||
|
||||
/**
|
||||
* Constuctor with default timeouts
|
||||
*
|
||||
* @param theWrap The validation support module to wrap
|
||||
*/
|
||||
public CachingValidationSupport(IValidationSupport theWrap) {
|
||||
this(theWrap, CacheTimeouts.defaultValues());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with configurable timeouts
|
||||
*
|
||||
* @param theWrap The validation support module to wrap
|
||||
* @param theCacheTimeouts The timeouts to use
|
||||
*/
|
||||
public CachingValidationSupport(IValidationSupport theWrap, CacheTimeouts theCacheTimeouts) {
|
||||
super(theWrap.getFhirContext(), theWrap);
|
||||
myValidateCodeCache = Caffeine
|
||||
.newBuilder()
|
||||
.expireAfterWrite(10, TimeUnit.MINUTES)
|
||||
.expireAfterWrite(theCacheTimeouts.getValidateCodeMillis(), TimeUnit.MILLISECONDS)
|
||||
.maximumSize(5000)
|
||||
.build();
|
||||
myLookupCodeCache = Caffeine
|
||||
.newBuilder()
|
||||
.expireAfterWrite(10, TimeUnit.MINUTES)
|
||||
.expireAfterWrite(theCacheTimeouts.getLookupCodeMillis(), TimeUnit.MILLISECONDS)
|
||||
.maximumSize(5000)
|
||||
.build();
|
||||
myTranslateCodeCache = Caffeine
|
||||
.newBuilder()
|
||||
.expireAfterWrite(theCacheTimeouts.getTranslateCodeMillis(), TimeUnit.MILLISECONDS)
|
||||
.maximumSize(5000)
|
||||
.build();
|
||||
myCache = Caffeine
|
||||
.newBuilder()
|
||||
.expireAfterWrite(10, TimeUnit.MINUTES)
|
||||
.expireAfterWrite(theCacheTimeouts.getMiscMillis(), TimeUnit.MILLISECONDS)
|
||||
.maximumSize(5000)
|
||||
.build();
|
||||
}
|
||||
|
@ -88,12 +110,30 @@ public class CachingValidationSupport extends BaseValidationSupportWrapper imple
|
|||
return loadFromCache(myLookupCodeCache, key, t -> super.lookupCode(theValidationSupportContext, theSystem, theCode));
|
||||
}
|
||||
|
||||
@Override
|
||||
public IValidationSupport.CodeValidationResult validateCodeInValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theValidationOptions, String theCodeSystem, String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) {
|
||||
|
||||
BaseRuntimeChildDefinition urlChild = myCtx.getResourceDefinition(theValueSet).getChildByName("url");
|
||||
Optional<String> valueSetUrl = urlChild.getAccessor().getValues(theValueSet).stream().map(t -> ((IPrimitiveType<?>) t).getValueAsString()).filter(t -> isNotBlank(t)).findFirst();
|
||||
if (valueSetUrl.isPresent()) {
|
||||
String key = "validateCodeInValueSet " + theValidationOptions.toString() + " " + defaultString(theCodeSystem, "(null)") + " " + defaultString(theCode, "(null)") + " " + defaultString(theDisplay, "(null)") + " " + valueSetUrl.get();
|
||||
return loadFromCache(myValidateCodeCache, key, t -> super.validateCodeInValueSet(theValidationSupportContext, theValidationOptions, theCodeSystem, theCode, theDisplay, theValueSet));
|
||||
}
|
||||
|
||||
return super.validateCodeInValueSet(theValidationSupportContext, theValidationOptions, theCodeSystem, theCode, theDisplay, theValueSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TranslateConceptResults translateConcept(TranslateCodeRequest theRequest) {
|
||||
return loadFromCache(myTranslateCodeCache, theRequest, k -> super.translateConcept(theRequest));
|
||||
}
|
||||
|
||||
@SuppressWarnings("OptionalAssignedToNull")
|
||||
@Nullable
|
||||
private <T> T loadFromCache(Cache theCache, String theKey, Function<String, T> theLoader) {
|
||||
private <S, T> T loadFromCache(Cache<S, Object> theCache, S theKey, Function<S, T> theLoader) {
|
||||
ourLog.trace("Fetching from cache: {}", theKey);
|
||||
|
||||
Function<String, Optional<T>> loaderWrapper = key -> Optional.ofNullable(theLoader.apply(theKey));
|
||||
Function<S, Optional<T>> loaderWrapper = key -> Optional.ofNullable(theLoader.apply(theKey));
|
||||
Optional<T> result = (Optional<T>) theCache.get(theKey, loaderWrapper);
|
||||
assert result != null;
|
||||
|
||||
|
@ -101,23 +141,65 @@ public class CachingValidationSupport extends BaseValidationSupportWrapper imple
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public IValidationSupport.CodeValidationResult validateCodeInValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theValidationOptions, String theCodeSystem, String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) {
|
||||
|
||||
BaseRuntimeChildDefinition urlChild = myCtx.getResourceDefinition(theValueSet).getChildByName("url");
|
||||
Optional<String> valueSetUrl = urlChild.getAccessor().getValues(theValueSet).stream().map(t -> ((IPrimitiveType<?>) t).getValueAsString()).filter(t->isNotBlank(t)).findFirst();
|
||||
if (valueSetUrl.isPresent()) {
|
||||
String key = "validateCodeInValueSet " + theValidationOptions.toString() + " " + defaultString(theCodeSystem, "(null)") + " " + defaultString(theCode, "(null)") + " " + defaultString(theDisplay, "(null)") + " " + valueSetUrl.get();
|
||||
return loadFromCache(myValidateCodeCache, key, t-> super.validateCodeInValueSet(theValidationSupportContext, theValidationOptions, theCodeSystem, theCode, theDisplay, theValueSet));
|
||||
}
|
||||
|
||||
return super.validateCodeInValueSet(theValidationSupportContext, theValidationOptions, theCodeSystem, theCode, theDisplay, theValueSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateCaches() {
|
||||
myLookupCodeCache.invalidateAll();
|
||||
myCache.invalidateAll();
|
||||
myValidateCodeCache.invalidateAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 5.4.0
|
||||
*/
|
||||
public static class CacheTimeouts {
|
||||
|
||||
private long myTranslateCodeMillis;
|
||||
private long myLookupCodeMillis;
|
||||
private long myValidateCodeMillis;
|
||||
private long myMiscMillis;
|
||||
|
||||
public long getTranslateCodeMillis() {
|
||||
return myTranslateCodeMillis;
|
||||
}
|
||||
|
||||
public CacheTimeouts setTranslateCodeMillis(long theTranslateCodeMillis) {
|
||||
myTranslateCodeMillis = theTranslateCodeMillis;
|
||||
return this;
|
||||
}
|
||||
|
||||
public long getLookupCodeMillis() {
|
||||
return myLookupCodeMillis;
|
||||
}
|
||||
|
||||
public CacheTimeouts setLookupCodeMillis(long theLookupCodeMillis) {
|
||||
myLookupCodeMillis = theLookupCodeMillis;
|
||||
return this;
|
||||
}
|
||||
|
||||
public long getValidateCodeMillis() {
|
||||
return myValidateCodeMillis;
|
||||
}
|
||||
|
||||
public CacheTimeouts setValidateCodeMillis(long theValidateCodeMillis) {
|
||||
myValidateCodeMillis = theValidateCodeMillis;
|
||||
return this;
|
||||
}
|
||||
|
||||
public long getMiscMillis() {
|
||||
return myMiscMillis;
|
||||
}
|
||||
|
||||
public CacheTimeouts setMiscMillis(long theMiscMillis) {
|
||||
myMiscMillis = theMiscMillis;
|
||||
return this;
|
||||
}
|
||||
|
||||
public static CacheTimeouts defaultValues() {
|
||||
return new CacheTimeouts()
|
||||
.setLookupCodeMillis(10 * DateUtils.MILLIS_PER_MINUTE)
|
||||
.setTranslateCodeMillis(10 * DateUtils.MILLIS_PER_MINUTE)
|
||||
.setValidateCodeMillis(10 * DateUtils.MILLIS_PER_MINUTE)
|
||||
.setMiscMillis(10 * DateUtils.MILLIS_PER_MINUTE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
|||
}
|
||||
|
||||
@Override
|
||||
public ValueSetExpansionOutcome expandValueSet(ValidationSupportContext theValidationSupportContext, ValueSetExpansionOptions theExpansionOptions, IBaseResource theValueSetToExpand) {
|
||||
public ValueSetExpansionOutcome expandValueSet(ValidationSupportContext theValidationSupportContext, ValueSetExpansionOptions theExpansionOptions, @Nonnull IBaseResource theValueSetToExpand) {
|
||||
|
||||
org.hl7.fhir.r5.model.ValueSet expansionR5 = expandValueSetToCanonical(theValidationSupportContext, theValueSetToExpand, null, null);
|
||||
if (expansionR5 == null) {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue