Improve versioned URL support for ValueSet expansion (#2994)
* Add test * Test fixes * Work on expansion errors * Fixes * Test fixes * Test fixes * Test fixes * Test fixes * Test fixes * Test fixes * Test fixes * Work on fix * Test fixes * Test fixes * Test fix * Cleanup * Test fixes * Remove accidental commit * Test fixes * Version bump * Add changelog * Fixes * Test fix * Fix conflicts
This commit is contained in:
parent
95e853bdf7
commit
eca725afa4
|
@ -45,6 +45,16 @@ import java.util.Properties;
|
|||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
/**
|
||||
* This class returns the vocabulary that is shipped with the base FHIR
|
||||
* specification.
|
||||
*
|
||||
* Note that this class is version aware. For example, a request for
|
||||
* <code>http://foo-codesystem|123</code> will only return a value if
|
||||
* the built in resource if the version matches. Unversioned URLs
|
||||
* should generally be used, and will return whatever version is
|
||||
* present.
|
||||
*/
|
||||
public class DefaultProfileValidationSupport implements IValidationSupport {
|
||||
|
||||
private static final String URL_PREFIX_STRUCTURE_DEFINITION = "http://hl7.org/fhir/StructureDefinition/";
|
||||
|
@ -179,18 +189,27 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
|
|||
|
||||
// System can take the form "http://url|version"
|
||||
String system = theSystem;
|
||||
if (system.contains("|")) {
|
||||
String version = system.substring(system.indexOf('|') + 1);
|
||||
if (version.matches("^[0-9.]+$")) {
|
||||
system = system.substring(0, system.indexOf('|'));
|
||||
String version = null;
|
||||
int pipeIdx = system.indexOf('|');
|
||||
if (pipeIdx > 0) {
|
||||
version = system.substring(pipeIdx + 1);
|
||||
system = system.substring(0, pipeIdx);
|
||||
}
|
||||
|
||||
IBaseResource candidate;
|
||||
if (codeSystem) {
|
||||
candidate = codeSystems.get(system);
|
||||
} else {
|
||||
candidate = valueSets.get(system);
|
||||
}
|
||||
|
||||
if (candidate != null && isNotBlank(version) && !system.startsWith("http://hl7.org") && !system.startsWith("http://terminology.hl7.org")) {
|
||||
if (!StringUtils.equals(version, myCtx.newTerser().getSinglePrimitiveValueOrNull(candidate, "version"))) {
|
||||
candidate = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (codeSystem) {
|
||||
return codeSystems.get(system);
|
||||
} else {
|
||||
return valueSets.get(system);
|
||||
}
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -205,8 +224,7 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
|
|||
url = URL_PREFIX_STRUCTURE_DEFINITION_BASE + url;
|
||||
}
|
||||
Map<String, IBaseResource> structureDefinitionMap = provideStructureDefinitionMap();
|
||||
IBaseResource retVal = structureDefinitionMap.get(url);
|
||||
return retVal;
|
||||
return structureDefinitionMap.get(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -586,8 +586,8 @@ public interface IValidationSupport {
|
|||
private final IBaseResource myValueSet;
|
||||
private final String myError;
|
||||
|
||||
public ValueSetExpansionOutcome(IBaseResource theValueSet, String theError) {
|
||||
myValueSet = theValueSet;
|
||||
public ValueSetExpansionOutcome(String theError) {
|
||||
myValueSet = null;
|
||||
myError = theError;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,8 +2,12 @@
|
|||
ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.expansionRefersToUnknownCs=Unknown CodeSystem URI "{0}" referenced from ValueSet
|
||||
ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.valueSetNotYetExpanded=ValueSet "{0}" has not yet been pre-expanded. Performing in-memory expansion without parameters. Current status: {1} | {2}
|
||||
ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.valueSetNotYetExpanded_OffsetNotAllowed=ValueSet expansion can not combine "offset" with "ValueSet.compose.exclude" unless the ValueSet has been pre-expanded. ValueSet "{0}" must be pre-expanded for this operation to work.
|
||||
ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.valueSetExpandedUsingPreExpansion=ValueSet was expanded using a pre-calculated expansion
|
||||
|
||||
ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.valueSetExpandedUsingPreExpansion=ValueSet was expanded using an expansion that was pre-calculated at {0}
|
||||
ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.valueSetExpandedUsingInMemoryExpansion=ValueSet with URL "{0}" was expanded using an in-memory expansion
|
||||
ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.validationPerformedAgainstPreExpansion=Code validation occurred using a ValueSet expansion that was pre-calculated at {0}
|
||||
ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.valueSetNotFoundInTerminologyDatabase=ValueSet can not be found in terminology database: {0}
|
||||
ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.valueSetPreExpansionInvalidated=ValueSet with URL "{0}" precaluclated expansion with {1} concept(s) has been invalidated
|
||||
ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.valueSetCantInvalidateNotYetPrecalculated=ValueSet with URL "{0}" already has status: {1}
|
||||
|
||||
|
||||
# Core Library Messages
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
type: add
|
||||
issue: 2994
|
||||
title: "When performing ValueSet expansions and ValueSet-based code validations, the HAPI FHIR terminology service will allow
|
||||
ValueSets to be expanded if they contain an enumeration of codes, even if the corresponding CodeSystem resource can not
|
||||
be found. Response messages have also been improved to give better insight into whether a precalculated expansion
|
||||
was used. In addition, a new operation called `$invalidate-expansion` has been added that allows for manual invalidation
|
||||
of previously calculated ValueSet expansions."
|
|
@ -85,6 +85,7 @@ import ca.uhn.fhir.jpa.partition.RequestPartitionHelperSvc;
|
|||
import ca.uhn.fhir.jpa.provider.DiffProvider;
|
||||
import ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider;
|
||||
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
|
||||
import ca.uhn.fhir.jpa.provider.ValueSetOperationProvider;
|
||||
import ca.uhn.fhir.jpa.reindex.ReindexJobSubmitterImpl;
|
||||
import ca.uhn.fhir.jpa.sched.AutowiringSpringBeanJobFactory;
|
||||
import ca.uhn.fhir.jpa.sched.HapiSchedulerServiceImpl;
|
||||
|
@ -318,6 +319,12 @@ public abstract class BaseConfig {
|
|||
return new SubscriptionTriggeringProvider();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Lazy
|
||||
public ValueSetOperationProvider valueSetOperationProvider() {
|
||||
return new ValueSetOperationProvider();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TransactionProcessor transactionProcessor() {
|
||||
return new TransactionProcessor();
|
||||
|
|
|
@ -88,7 +88,7 @@ public class JpaPersistedResourceValidationSupport implements IValidationSupport
|
|||
private Class<? extends IBaseResource> myValueSetType;
|
||||
private Class<? extends IBaseResource> myQuestionnaireType;
|
||||
private Class<? extends IBaseResource> myImplementationGuideType;
|
||||
private Cache<String, IBaseResource> myLoadCache = Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).build();
|
||||
private Cache<String, IBaseResource> myLoadCache = Caffeine.newBuilder().maximumSize(1000).expireAfterWrite(1, TimeUnit.MINUTES).build();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
|
|
@ -27,6 +27,8 @@ public interface ITermValueSetConceptView {
|
|||
|
||||
String getConceptDisplay();
|
||||
|
||||
String getConceptSystemVersion();
|
||||
|
||||
Long getSourceConceptPid();
|
||||
|
||||
String getSourceConceptDirectParentPids();
|
||||
|
|
|
@ -28,9 +28,27 @@ import org.apache.commons.lang3.builder.ToStringStyle;
|
|||
import org.hibernate.annotations.ColumnDefault;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.persistence.*;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.EnumType;
|
||||
import javax.persistence.Enumerated;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.ForeignKey;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.SequenceGenerator;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
import javax.persistence.Transient;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.left;
|
||||
|
@ -41,13 +59,11 @@ import static org.apache.commons.lang3.StringUtils.length;
|
|||
})
|
||||
@Entity()
|
||||
public class TermValueSet implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static final int MAX_EXPANSION_STATUS_LENGTH = 50;
|
||||
public static final int MAX_NAME_LENGTH = 200;
|
||||
public static final int MAX_URL_LENGTH = 200;
|
||||
public static final int MAX_VER_LENGTH = 200;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
@Id()
|
||||
@SequenceGenerator(name = "SEQ_VALUESET_PID", sequenceName = "SEQ_VALUESET_PID")
|
||||
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_VALUESET_PID")
|
||||
|
@ -85,6 +101,10 @@ public class TermValueSet implements Serializable {
|
|||
@Column(name = "EXPANSION_STATUS", nullable = false, length = MAX_EXPANSION_STATUS_LENGTH)
|
||||
private TermValueSetPreExpansionStatusEnum myExpansionStatus;
|
||||
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
@Column(name = "EXPANDED_AT", nullable = true)
|
||||
private Date myExpansionTimestamp;
|
||||
|
||||
@Transient
|
||||
private transient Integer myHashCode;
|
||||
|
||||
|
@ -95,6 +115,14 @@ public class TermValueSet implements Serializable {
|
|||
myTotalConceptDesignations = 0L;
|
||||
}
|
||||
|
||||
public Date getExpansionTimestamp() {
|
||||
return myExpansionTimestamp;
|
||||
}
|
||||
|
||||
public void setExpansionTimestamp(Date theExpansionTimestamp) {
|
||||
myExpansionTimestamp = theExpansionTimestamp;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return myId;
|
||||
}
|
||||
|
|
|
@ -49,6 +49,7 @@ import java.sql.SQLException;
|
|||
" vsc.SYSTEM_URL AS CONCEPT_SYSTEM_URL, " +
|
||||
" vsc.CODEVAL AS CONCEPT_CODEVAL, " +
|
||||
" vsc.DISPLAY AS CONCEPT_DISPLAY, " +
|
||||
" vsc.SYSTEM_VER AS SYSTEM_VER, " +
|
||||
" vsc.SOURCE_PID AS SOURCE_PID, " +
|
||||
" vsc.SOURCE_DIRECT_PARENT_PIDS AS SOURCE_DIRECT_PARENT_PIDS, " +
|
||||
" vscd.PID AS DESIGNATION_PID, " +
|
||||
|
@ -85,6 +86,9 @@ public class TermValueSetConceptView implements Serializable, ITermValueSetConce
|
|||
@Column(name = "CONCEPT_DISPLAY", length = TermConcept.MAX_DESC_LENGTH)
|
||||
private String myConceptDisplay;
|
||||
|
||||
@Column(name="SYSTEM_VER", length = TermCodeSystemVersion.MAX_VERSION_LENGTH)
|
||||
private String myConceptSystemVersion;
|
||||
|
||||
@Column(name = "DESIGNATION_PID")
|
||||
private Long myDesignationPid;
|
||||
|
||||
|
@ -176,4 +180,10 @@ public class TermValueSetConceptView implements Serializable, ITermValueSetConce
|
|||
public String getDesignationVal() {
|
||||
return myDesignationVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConceptSystemVersion() {
|
||||
return myConceptSystemVersion;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -49,6 +49,7 @@ import java.sql.SQLException;
|
|||
" vsc.SYSTEM_URL AS CONCEPT_SYSTEM_URL, " +
|
||||
" vsc.CODEVAL AS CONCEPT_CODEVAL, " +
|
||||
" vsc.DISPLAY AS CONCEPT_DISPLAY, " +
|
||||
" vsc.SYSTEM_VER AS SYSTEM_VER, " +
|
||||
" vsc.SOURCE_PID AS SOURCE_PID, " +
|
||||
" vsc.SOURCE_DIRECT_PARENT_PIDS AS SOURCE_DIRECT_PARENT_PIDS, " +
|
||||
" vscd.PID AS DESIGNATION_PID, " +
|
||||
|
@ -85,6 +86,9 @@ public class TermValueSetConceptViewOracle implements Serializable, ITermValueSe
|
|||
@Column(name = "CONCEPT_DISPLAY", length = TermConcept.MAX_DESC_LENGTH)
|
||||
private String myConceptDisplay;
|
||||
|
||||
@Column(name="SYSTEM_VER", length = TermCodeSystemVersion.MAX_VERSION_LENGTH)
|
||||
private String myConceptSystemVersion;
|
||||
|
||||
@Column(name = "DESIGNATION_PID")
|
||||
private Long myDesignationPid;
|
||||
|
||||
|
@ -130,6 +134,11 @@ public class TermValueSetConceptViewOracle implements Serializable, ITermValueSe
|
|||
return myConceptDisplay;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConceptSystemVersion() {
|
||||
return myConceptSystemVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getSourceConceptPid() {
|
||||
return mySourceConceptPid;
|
||||
|
|
|
@ -128,6 +128,10 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
|
|||
version.onTable("HFJ_RESOURCE")
|
||||
.dropIndex("20210908.1", "IDX_RES_LANG");
|
||||
|
||||
version.onTable("TRM_VALUESET")
|
||||
.addColumn("20210915.1", "EXPANDED_AT")
|
||||
.nullable()
|
||||
.type(ColumnTypeEnum.DATE_TIMESTAMP);
|
||||
}
|
||||
|
||||
private void init540() {
|
||||
|
|
|
@ -0,0 +1,229 @@
|
|||
package ca.uhn.fhir.jpa.provider;
|
||||
|
||||
/*-
|
||||
* #%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.context.support.ValueSetExpansionOptions;
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoValueSet;
|
||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
|
||||
import ca.uhn.fhir.util.ParametersUtil;
|
||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.ICompositeType;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public class ValueSetOperationProvider extends BaseJpaProvider {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(ValueSetOperationProvider.class);
|
||||
@Autowired
|
||||
private DaoConfig myDaoConfig;
|
||||
@Autowired
|
||||
private DaoRegistry myDaoRegistry;
|
||||
@Autowired
|
||||
private ITermReadSvc myTermReadSvc;
|
||||
|
||||
@Operation(name = JpaConstants.OPERATION_EXPAND, idempotent = true, typeName = "ValueSet")
|
||||
public IBaseResource expand(
|
||||
HttpServletRequest theServletRequest,
|
||||
@IdParam(optional = true) IIdType theId,
|
||||
@OperationParam(name = "valueSet", min = 0, max = 1) IBaseResource theValueSet,
|
||||
@OperationParam(name = "url", min = 0, max = 1, typeName = "uri") IPrimitiveType<String> theUrl,
|
||||
@OperationParam(name = "valueSetVersion", min = 0, max = 1, typeName = "string") IPrimitiveType<String> theValueSetVersion,
|
||||
@OperationParam(name = "filter", min = 0, max = 1, typeName = "string") IPrimitiveType<String> theFilter,
|
||||
@OperationParam(name = "offset", min = 0, max = 1, typeName = "integer") IPrimitiveType<Integer> theOffset,
|
||||
@OperationParam(name = "count", min = 0, max = 1, typeName = "integer") IPrimitiveType<Integer> theCount,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPAND_PARAM_INCLUDE_HIERARCHY, min = 0, max = 1, typeName = "boolean") IPrimitiveType<Boolean> theIncludeHierarchy,
|
||||
RequestDetails theRequestDetails) {
|
||||
|
||||
boolean haveId = theId != null && theId.hasIdPart();
|
||||
boolean haveIdentifier = theUrl != null && isNotBlank(theUrl.getValue());
|
||||
boolean haveValueSet = theValueSet != null && !theValueSet.isEmpty();
|
||||
boolean haveValueSetVersion = theValueSetVersion != null && !theValueSetVersion.isEmpty();
|
||||
|
||||
if (!haveId && !haveIdentifier && !haveValueSet) {
|
||||
throw new InvalidRequestException("$expand operation at the type level (no ID specified) requires a url or a valueSet as a part of the request.");
|
||||
}
|
||||
|
||||
if (moreThanOneTrue(haveId, haveIdentifier, haveValueSet)) {
|
||||
throw new InvalidRequestException("$expand must EITHER be invoked at the instance level, or have a url specified, or have a ValueSet specified. Can not combine these options.");
|
||||
}
|
||||
|
||||
ValueSetExpansionOptions options = createValueSetExpansionOptions(myDaoConfig, theOffset, theCount, theIncludeHierarchy, theFilter);
|
||||
|
||||
startRequest(theServletRequest);
|
||||
try {
|
||||
|
||||
IFhirResourceDaoValueSet<IBaseResource, ICompositeType, ICompositeType> dao = getDao();
|
||||
if (haveId) {
|
||||
return dao.expand(theId, options, theRequestDetails);
|
||||
} else if (haveIdentifier) {
|
||||
if (haveValueSetVersion) {
|
||||
return dao.expandByIdentifier(theUrl.getValue() + "|" + theValueSetVersion.getValue(), options);
|
||||
} else {
|
||||
return dao.expandByIdentifier(theUrl.getValue(), options);
|
||||
}
|
||||
} else {
|
||||
return dao.expand(theValueSet, options);
|
||||
}
|
||||
} finally {
|
||||
endRequest(theServletRequest);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private IFhirResourceDaoValueSet<IBaseResource, ICompositeType, ICompositeType> getDao() {
|
||||
return (IFhirResourceDaoValueSet<IBaseResource, ICompositeType, ICompositeType>) myDaoRegistry.getResourceDao("ValueSet");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Operation(name = JpaConstants.OPERATION_VALIDATE_CODE, idempotent = true, typeName = "ValueSet", returnParameters = {
|
||||
@OperationParam(name = "result", typeName = "boolean", min = 1),
|
||||
@OperationParam(name = "message", typeName = "string"),
|
||||
@OperationParam(name = "display", typeName = "string")
|
||||
})
|
||||
public IBaseParameters validateCode(
|
||||
HttpServletRequest theServletRequest,
|
||||
@IdParam(optional = true) IIdType theId,
|
||||
@OperationParam(name = "url", min = 0, max = 1, typeName = "uri") IPrimitiveType<String> theValueSetUrl,
|
||||
@OperationParam(name = "valueSetVersion", min = 0, max = 1, typeName = "string") IPrimitiveType<String> theValueSetVersion,
|
||||
@OperationParam(name = "code", min = 0, max = 1) IPrimitiveType<String> theCode,
|
||||
@OperationParam(name = "system", min = 0, max = 1, typeName = "uri") IPrimitiveType<String> theSystem,
|
||||
@OperationParam(name = "systemVersion", min = 0, max = 1, typeName = "string") IPrimitiveType<String> theSystemVersion,
|
||||
@OperationParam(name = "display", min = 0, max = 1, typeName = "string") IPrimitiveType<String> theDisplay,
|
||||
@OperationParam(name = "coding", min = 0, max = 1, typeName = "Coding") ICompositeType theCoding,
|
||||
@OperationParam(name = "codeableConcept", min = 0, max = 1, typeName = "CodeableConcept") ICompositeType theCodeableConcept,
|
||||
RequestDetails theRequestDetails
|
||||
) {
|
||||
|
||||
startRequest(theServletRequest);
|
||||
try {
|
||||
IFhirResourceDaoValueSet<IBaseResource, ICompositeType, ICompositeType> dao = getDao();
|
||||
IPrimitiveType<String> valueSetIdentifier;
|
||||
if (theValueSetVersion != null) {
|
||||
valueSetIdentifier = (IPrimitiveType<String>) getContext().getElementDefinition("uri").newInstance();
|
||||
valueSetIdentifier.setValue(theValueSetUrl.getValue() + "|" + theValueSetVersion);
|
||||
} else {
|
||||
valueSetIdentifier = theValueSetUrl;
|
||||
}
|
||||
IPrimitiveType<String> codeSystemIdentifier;
|
||||
if (theSystemVersion != null) {
|
||||
codeSystemIdentifier = (IPrimitiveType<String>) getContext().getElementDefinition("uri").newInstance();
|
||||
codeSystemIdentifier.setValue(theSystem.getValue() + "|" + theSystemVersion);
|
||||
} else {
|
||||
codeSystemIdentifier = theSystem;
|
||||
}
|
||||
IValidationSupport.CodeValidationResult result = dao.validateCode(valueSetIdentifier, theId, theCode, codeSystemIdentifier, theDisplay, theCoding, theCodeableConcept, theRequestDetails);
|
||||
return BaseJpaResourceProviderValueSetDstu2.toValidateCodeResult(getContext(), result);
|
||||
} finally {
|
||||
endRequest(theServletRequest);
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(name = ProviderConstants.OPERATION_INVALIDATE_EXPANSION, idempotent = false, typeName = "ValueSet", returnParameters = {
|
||||
@OperationParam(name = "message", typeName = "string", min = 1, max = 1)
|
||||
})
|
||||
public IBaseParameters invalidateValueSetExpansion(
|
||||
@IdParam IIdType theValueSetId,
|
||||
RequestDetails theRequestDetails,
|
||||
HttpServletRequest theServletRequest) {
|
||||
startRequest(theServletRequest);
|
||||
try {
|
||||
|
||||
String outcome = myTermReadSvc.invalidatePreCalculatedExpansion(theValueSetId, theRequestDetails);
|
||||
|
||||
IBaseParameters retVal = ParametersUtil.newInstance(getContext());
|
||||
ParametersUtil.addParameterToParametersString(getContext(), retVal, "message", outcome);
|
||||
return retVal;
|
||||
|
||||
} finally {
|
||||
endRequest(theServletRequest);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static ValueSetExpansionOptions createValueSetExpansionOptions(DaoConfig theDaoConfig, IPrimitiveType<Integer> theOffset, IPrimitiveType<Integer> theCount, IPrimitiveType<Boolean> theIncludeHierarchy, IPrimitiveType<String> theFilter) {
|
||||
int offset = theDaoConfig.getPreExpandValueSetsDefaultOffset();
|
||||
if (theOffset != null && theOffset.hasValue()) {
|
||||
if (theOffset.getValue() >= 0) {
|
||||
offset = theOffset.getValue();
|
||||
} else {
|
||||
throw new InvalidRequestException("offset parameter for $expand operation must be >= 0 when specified. offset: " + theOffset.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
int count = theDaoConfig.getPreExpandValueSetsDefaultCount();
|
||||
if (theCount != null && theCount.hasValue()) {
|
||||
if (theCount.getValue() >= 0) {
|
||||
count = theCount.getValue();
|
||||
} else {
|
||||
throw new InvalidRequestException("count parameter for $expand operation must be >= 0 when specified. count: " + theCount.getValue());
|
||||
}
|
||||
}
|
||||
int countMax = theDaoConfig.getPreExpandValueSetsMaxCount();
|
||||
if (count > countMax) {
|
||||
ourLog.warn("count parameter for $expand operation of {} exceeds maximum value of {}; using maximum value.", count, countMax);
|
||||
count = countMax;
|
||||
}
|
||||
|
||||
ValueSetExpansionOptions options = ValueSetExpansionOptions.forOffsetAndCount(offset, count);
|
||||
|
||||
if (theIncludeHierarchy != null && Boolean.TRUE.equals(theIncludeHierarchy.getValue())) {
|
||||
options.setIncludeHierarchy(true);
|
||||
}
|
||||
|
||||
if (theFilter != null) {
|
||||
options.setFilter(theFilter.getValue());
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
private static boolean moreThanOneTrue(boolean... theBooleans) {
|
||||
boolean haveOne = false;
|
||||
for (boolean next : theBooleans) {
|
||||
if (next) {
|
||||
if (haveOne) {
|
||||
return true;
|
||||
} else {
|
||||
haveOne = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -20,152 +20,8 @@ package ca.uhn.fhir.jpa.provider.dstu3;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoValueSet;
|
||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||
import ca.uhn.fhir.jpa.provider.BaseJpaResourceProviderValueSetDstu2;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import org.hl7.fhir.dstu3.model.BooleanType;
|
||||
import org.hl7.fhir.dstu3.model.CodeType;
|
||||
import org.hl7.fhir.dstu3.model.CodeableConcept;
|
||||
import org.hl7.fhir.dstu3.model.Coding;
|
||||
import org.hl7.fhir.dstu3.model.IdType;
|
||||
import org.hl7.fhir.dstu3.model.IntegerType;
|
||||
import org.hl7.fhir.dstu3.model.Parameters;
|
||||
import org.hl7.fhir.dstu3.model.StringType;
|
||||
import org.hl7.fhir.dstu3.model.UriType;
|
||||
import org.hl7.fhir.dstu3.model.ValueSet;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import static ca.uhn.fhir.jpa.provider.r4.BaseJpaResourceProviderValueSetR4.createValueSetExpansionOptions;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public class BaseJpaResourceProviderValueSetDstu3 extends JpaResourceProviderDstu3<ValueSet> {
|
||||
|
||||
@Operation(name = JpaConstants.OPERATION_EXPAND, idempotent = true)
|
||||
public ValueSet expand(
|
||||
HttpServletRequest theServletRequest,
|
||||
@IdParam(optional = true) IdType theId,
|
||||
@OperationParam(name = "valueSet", min = 0, max = 1) ValueSet theValueSet,
|
||||
// Note: url is correct and identifier is not, but identifier was only added as
|
||||
// of 3.1.0 so we'll leave url for now. See: https://groups.google.com/d/msgid/hapi-fhir/CAN2Cfy8kW%2BAOkgC6VjPsU3gRCpExCNZBmJdi-k5R_TWeyWH4tA%40mail.gmail.com?utm_medium=email&utm_source=footer
|
||||
@OperationParam(name = "url", min = 0, max = 1) UriType theUrl,
|
||||
@OperationParam(name = "valueSetVersion", min = 0, max = 1) StringType theValueSetVersion,
|
||||
@OperationParam(name = "identifier", min = 0, max = 1) UriType theIdentifier,
|
||||
@OperationParam(name = "filter", min = 0, max = 1) StringType theFilter,
|
||||
@OperationParam(name = "offset", min = 0, max = 1) IntegerType theOffset,
|
||||
@OperationParam(name = "count", min = 0, max = 1) IntegerType theCount,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPAND_PARAM_INCLUDE_HIERARCHY, min = 0, max = 1, typeName = "boolean") IPrimitiveType<Boolean> theIncludeHierarchy,
|
||||
RequestDetails theRequestDetails) {
|
||||
|
||||
boolean haveId = theId != null && theId.hasIdPart();
|
||||
UriType url = theIdentifier;
|
||||
if (theUrl != null && isNotBlank(theUrl.getValue())) {
|
||||
url = theUrl;
|
||||
}
|
||||
|
||||
boolean haveIdentifier = url != null && isNotBlank(url.getValue());
|
||||
boolean haveValueSet = theValueSet != null && !theValueSet.isEmpty();
|
||||
boolean haveValueSetVersion = theValueSetVersion != null && !theValueSetVersion.isEmpty();
|
||||
|
||||
if (!haveId && !haveIdentifier && !haveValueSet) {
|
||||
throw new InvalidRequestException("$expand operation at the type level (no ID specified) requires an identifier or a valueSet as a part of the request.");
|
||||
}
|
||||
|
||||
if (moreThanOneTrue(haveId, haveIdentifier, haveValueSet)) {
|
||||
throw new InvalidRequestException("$expand must EITHER be invoked at the instance level, or have an identifier specified, or have a ValueSet specified. Can not combine these options.");
|
||||
}
|
||||
|
||||
ValueSetExpansionOptions options = createValueSetExpansionOptions(myDaoConfig, theOffset, theCount, theIncludeHierarchy, theFilter);
|
||||
|
||||
startRequest(theServletRequest);
|
||||
try {
|
||||
IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> dao = (IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept>) getDao();
|
||||
if (haveId) {
|
||||
return dao.expand(theId, options, theRequestDetails);
|
||||
} else if (haveIdentifier) {
|
||||
if (haveValueSetVersion) {
|
||||
return dao.expandByIdentifier(url.getValue() + "|" + theValueSetVersion.getValue(), options);
|
||||
} else {
|
||||
return dao.expandByIdentifier(url.getValue(), options);
|
||||
}
|
||||
} else {
|
||||
return dao.expand(theValueSet, options);
|
||||
}
|
||||
} finally {
|
||||
endRequest(theServletRequest);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Operation(name = JpaConstants.OPERATION_VALIDATE_CODE, idempotent = true, returnParameters = {
|
||||
@OperationParam(name = "result", type = BooleanType.class, min = 1),
|
||||
@OperationParam(name = "message", type = StringType.class),
|
||||
@OperationParam(name = "display", type = StringType.class)
|
||||
})
|
||||
public Parameters validateCode(
|
||||
HttpServletRequest theServletRequest,
|
||||
@IdParam(optional = true) IdType theId,
|
||||
@OperationParam(name = "identifier", min = 0, max = 1) UriType theValueSetIdentifier,
|
||||
@OperationParam(name = "url", min = 0, max = 1) UriType theValueSetUrl,
|
||||
@OperationParam(name = "valueSetVersion", min = 0, max = 1) StringType theValueSetVersion,
|
||||
@OperationParam(name = "code", min = 0, max = 1) CodeType theCode,
|
||||
@OperationParam(name = "system", min = 0, max = 1) UriType theSystem,
|
||||
@OperationParam(name = "systemVersion", min = 0, max = 1) StringType theSystemVersion,
|
||||
@OperationParam(name = "display", min = 0, max = 1) StringType theDisplay,
|
||||
@OperationParam(name = "coding", min = 0, max = 1) Coding theCoding,
|
||||
@OperationParam(name = "codeableConcept", min = 0, max = 1) CodeableConcept theCodeableConcept,
|
||||
RequestDetails theRequestDetails
|
||||
) {
|
||||
|
||||
UriType url = theValueSetIdentifier;
|
||||
if (theValueSetUrl != null && isNotBlank(theValueSetUrl.getValue())) {
|
||||
url = theValueSetUrl;
|
||||
}
|
||||
|
||||
startRequest(theServletRequest);
|
||||
try {
|
||||
IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> dao = (IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept>) getDao();
|
||||
UriType valueSetIdentifier;
|
||||
if (theValueSetVersion != null) {
|
||||
valueSetIdentifier = new UriType(url.getValue() + "|" + theValueSetVersion);
|
||||
} else {
|
||||
valueSetIdentifier = url;
|
||||
}
|
||||
UriType codeSystemIdentifier;
|
||||
if (theSystemVersion != null) {
|
||||
codeSystemIdentifier = new UriType(theSystem.getValue() + "|" + theSystemVersion);
|
||||
} else {
|
||||
codeSystemIdentifier = theSystem;
|
||||
}
|
||||
IValidationSupport.CodeValidationResult result = dao.validateCode(valueSetIdentifier, theId, theCode, codeSystemIdentifier, theDisplay, theCoding, theCodeableConcept, theRequestDetails);
|
||||
return (Parameters) BaseJpaResourceProviderValueSetDstu2.toValidateCodeResult(getContext(), result);
|
||||
} finally {
|
||||
endRequest(theServletRequest);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static boolean moreThanOneTrue(boolean... theBooleans) {
|
||||
boolean haveOne = false;
|
||||
for (boolean next : theBooleans) {
|
||||
if (next) {
|
||||
if (haveOne) {
|
||||
return true;
|
||||
} else {
|
||||
haveOne = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -20,175 +20,8 @@ package ca.uhn.fhir.jpa.provider.r4;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoValueSet;
|
||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||
import ca.uhn.fhir.jpa.provider.BaseJpaResourceProviderValueSetDstu2;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
import org.hl7.fhir.r4.model.BooleanType;
|
||||
import org.hl7.fhir.r4.model.CodeType;
|
||||
import org.hl7.fhir.r4.model.CodeableConcept;
|
||||
import org.hl7.fhir.r4.model.Coding;
|
||||
import org.hl7.fhir.r4.model.IdType;
|
||||
import org.hl7.fhir.r4.model.IntegerType;
|
||||
import org.hl7.fhir.r4.model.Parameters;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
import org.hl7.fhir.r4.model.UriType;
|
||||
import org.hl7.fhir.r4.model.ValueSet;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public class BaseJpaResourceProviderValueSetR4 extends JpaResourceProviderR4<ValueSet> {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseJpaResourceProviderValueSetR4.class);
|
||||
|
||||
@Operation(name = JpaConstants.OPERATION_EXPAND, idempotent = true)
|
||||
public ValueSet expand(
|
||||
HttpServletRequest theServletRequest,
|
||||
@IdParam(optional = true) IdType theId,
|
||||
@OperationParam(name = "valueSet", min = 0, max = 1) ValueSet theValueSet,
|
||||
@OperationParam(name = "url", min = 0, max = 1) UriType theUrl,
|
||||
@OperationParam(name = "valueSetVersion", min = 0, max = 1) StringType theValueSetVersion,
|
||||
@OperationParam(name = "filter", min = 0, max = 1) StringType theFilter,
|
||||
@OperationParam(name = "offset", min = 0, max = 1) IntegerType theOffset,
|
||||
@OperationParam(name = "count", min = 0, max = 1) IntegerType theCount,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPAND_PARAM_INCLUDE_HIERARCHY, min = 0, max = 1, typeName = "boolean") IPrimitiveType<Boolean> theIncludeHierarchy,
|
||||
RequestDetails theRequestDetails) {
|
||||
|
||||
boolean haveId = theId != null && theId.hasIdPart();
|
||||
boolean haveIdentifier = theUrl != null && isNotBlank(theUrl.getValue());
|
||||
boolean haveValueSet = theValueSet != null && !theValueSet.isEmpty();
|
||||
boolean haveValueSetVersion = theValueSetVersion != null && !theValueSetVersion.isEmpty();
|
||||
|
||||
if (!haveId && !haveIdentifier && !haveValueSet) {
|
||||
throw new InvalidRequestException("$expand operation at the type level (no ID specified) requires a url or a valueSet as a part of the request.");
|
||||
}
|
||||
|
||||
if (moreThanOneTrue(haveId, haveIdentifier, haveValueSet)) {
|
||||
throw new InvalidRequestException("$expand must EITHER be invoked at the instance level, or have a url specified, or have a ValueSet specified. Can not combine these options.");
|
||||
}
|
||||
|
||||
ValueSetExpansionOptions options = createValueSetExpansionOptions(myDaoConfig, theOffset, theCount, theIncludeHierarchy, theFilter);
|
||||
|
||||
startRequest(theServletRequest);
|
||||
try {
|
||||
|
||||
IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> dao = (IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept>) getDao();
|
||||
if (haveId) {
|
||||
return dao.expand(theId, options, theRequestDetails);
|
||||
} else if (haveIdentifier) {
|
||||
if (haveValueSetVersion) {
|
||||
return dao.expandByIdentifier(theUrl.getValue() + "|" + theValueSetVersion.getValue(), options);
|
||||
} else {
|
||||
return dao.expandByIdentifier(theUrl.getValue(), options);
|
||||
}
|
||||
} else {
|
||||
return dao.expand(theValueSet, options);
|
||||
}
|
||||
} finally {
|
||||
endRequest(theServletRequest);
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(name = JpaConstants.OPERATION_VALIDATE_CODE, idempotent = true, returnParameters = {
|
||||
@OperationParam(name = "result", type = BooleanType.class, min = 1),
|
||||
@OperationParam(name = "message", type = StringType.class),
|
||||
@OperationParam(name = "display", type = StringType.class)
|
||||
})
|
||||
public Parameters validateCode(
|
||||
HttpServletRequest theServletRequest,
|
||||
@IdParam(optional = true) IdType theId,
|
||||
@OperationParam(name = "url", min = 0, max = 1) UriType theValueSetUrl,
|
||||
@OperationParam(name = "valueSetVersion", min = 0, max = 1) StringType theValueSetVersion,
|
||||
@OperationParam(name = "code", min = 0, max = 1) CodeType theCode,
|
||||
@OperationParam(name = "system", min = 0, max = 1) UriType theSystem,
|
||||
@OperationParam(name = "systemVersion", min = 0, max = 1) StringType theSystemVersion,
|
||||
@OperationParam(name = "display", min = 0, max = 1) StringType theDisplay,
|
||||
@OperationParam(name = "coding", min = 0, max = 1) Coding theCoding,
|
||||
@OperationParam(name = "codeableConcept", min = 0, max = 1) CodeableConcept theCodeableConcept,
|
||||
RequestDetails theRequestDetails
|
||||
) {
|
||||
|
||||
startRequest(theServletRequest);
|
||||
try {
|
||||
IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> dao = (IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept>) getDao();
|
||||
UriType valueSetIdentifier;
|
||||
if (theValueSetVersion != null) {
|
||||
valueSetIdentifier = new UriType(theValueSetUrl.getValue() + "|" + theValueSetVersion);
|
||||
} else {
|
||||
valueSetIdentifier = theValueSetUrl;
|
||||
}
|
||||
UriType codeSystemIdentifier;
|
||||
if (theSystemVersion != null) {
|
||||
codeSystemIdentifier = new UriType(theSystem.getValue() + "|" + theSystemVersion);
|
||||
} else {
|
||||
codeSystemIdentifier = theSystem;
|
||||
}
|
||||
IValidationSupport.CodeValidationResult result = dao.validateCode(valueSetIdentifier, theId, theCode, codeSystemIdentifier, theDisplay, theCoding, theCodeableConcept, theRequestDetails);
|
||||
return (Parameters) BaseJpaResourceProviderValueSetDstu2.toValidateCodeResult(getContext(), result);
|
||||
} finally {
|
||||
endRequest(theServletRequest);
|
||||
}
|
||||
}
|
||||
|
||||
public static ValueSetExpansionOptions createValueSetExpansionOptions(DaoConfig theDaoConfig, IPrimitiveType<Integer> theOffset, IPrimitiveType<Integer> theCount, IPrimitiveType<Boolean> theIncludeHierarchy, IPrimitiveType<String> theFilter) {
|
||||
int offset = theDaoConfig.getPreExpandValueSetsDefaultOffset();
|
||||
if (theOffset != null && theOffset.hasValue()) {
|
||||
if (theOffset.getValue() >= 0) {
|
||||
offset = theOffset.getValue();
|
||||
} else {
|
||||
throw new InvalidRequestException("offset parameter for $expand operation must be >= 0 when specified. offset: " + theOffset.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
int count = theDaoConfig.getPreExpandValueSetsDefaultCount();
|
||||
if (theCount != null && theCount.hasValue()) {
|
||||
if (theCount.getValue() >= 0) {
|
||||
count = theCount.getValue();
|
||||
} else {
|
||||
throw new InvalidRequestException("count parameter for $expand operation must be >= 0 when specified. count: " + theCount.getValue());
|
||||
}
|
||||
}
|
||||
int countMax = theDaoConfig.getPreExpandValueSetsMaxCount();
|
||||
if (count > countMax) {
|
||||
ourLog.warn("count parameter for $expand operation of {} exceeds maximum value of {}; using maximum value.", count, countMax);
|
||||
count = countMax;
|
||||
}
|
||||
|
||||
ValueSetExpansionOptions options = ValueSetExpansionOptions.forOffsetAndCount(offset, count);
|
||||
|
||||
if (theIncludeHierarchy != null && Boolean.TRUE.equals(theIncludeHierarchy.getValue())) {
|
||||
options.setIncludeHierarchy(true);
|
||||
}
|
||||
|
||||
if (theFilter != null) {
|
||||
options.setFilter(theFilter.getValue());
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
private static boolean moreThanOneTrue(boolean... theBooleans) {
|
||||
boolean haveOne = false;
|
||||
for (boolean next : theBooleans) {
|
||||
if (next) {
|
||||
if (haveOne) {
|
||||
return true;
|
||||
} else {
|
||||
haveOne = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -20,138 +20,8 @@ package ca.uhn.fhir.jpa.provider.r5;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoValueSet;
|
||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||
import ca.uhn.fhir.jpa.provider.BaseJpaResourceProviderValueSetDstu2;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
import org.hl7.fhir.r5.model.BooleanType;
|
||||
import org.hl7.fhir.r5.model.CodeType;
|
||||
import org.hl7.fhir.r5.model.CodeableConcept;
|
||||
import org.hl7.fhir.r5.model.Coding;
|
||||
import org.hl7.fhir.r5.model.IdType;
|
||||
import org.hl7.fhir.r5.model.IntegerType;
|
||||
import org.hl7.fhir.r5.model.Parameters;
|
||||
import org.hl7.fhir.r5.model.StringType;
|
||||
import org.hl7.fhir.r5.model.UriType;
|
||||
import org.hl7.fhir.r5.model.ValueSet;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import static ca.uhn.fhir.jpa.provider.r4.BaseJpaResourceProviderValueSetR4.createValueSetExpansionOptions;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public class BaseJpaResourceProviderValueSetR5 extends JpaResourceProviderR5<ValueSet> {
|
||||
|
||||
@Operation(name = JpaConstants.OPERATION_EXPAND, idempotent = true)
|
||||
public ValueSet expand(
|
||||
HttpServletRequest theServletRequest,
|
||||
@IdParam(optional = true) IdType theId,
|
||||
@OperationParam(name = "valueSet", min = 0, max = 1) ValueSet theValueSet,
|
||||
@OperationParam(name = "url", min = 0, max = 1) UriType theUrl,
|
||||
@OperationParam(name = "valueSetVersion", min = 0, max = 1) StringType theValueSetVersion,
|
||||
@OperationParam(name = "filter", min = 0, max = 1) StringType theFilter,
|
||||
@OperationParam(name = "offset", min = 0, max = 1) IntegerType theOffset,
|
||||
@OperationParam(name = "count", min = 0, max = 1) IntegerType theCount,
|
||||
@OperationParam(name = JpaConstants.OPERATION_EXPAND_PARAM_INCLUDE_HIERARCHY, min = 0, max = 1, typeName = "boolean") IPrimitiveType<Boolean> theIncludeHierarchy,
|
||||
RequestDetails theRequestDetails) {
|
||||
|
||||
boolean haveId = theId != null && theId.hasIdPart();
|
||||
boolean haveIdentifier = theUrl != null && isNotBlank(theUrl.getValue());
|
||||
boolean haveValueSet = theValueSet != null && !theValueSet.isEmpty();
|
||||
boolean haveValueSetVersion = theValueSetVersion != null && !theValueSetVersion.isEmpty();
|
||||
|
||||
if (!haveId && !haveIdentifier && !haveValueSet) {
|
||||
throw new InvalidRequestException("$expand operation at the type level (no ID specified) requires a url or a valueSet as a part of the request.");
|
||||
}
|
||||
|
||||
if (moreThanOneTrue(haveId, haveIdentifier, haveValueSet)) {
|
||||
throw new InvalidRequestException("$expand must EITHER be invoked at the instance level, or have a url specified, or have a ValueSet specified. Can not combine these options.");
|
||||
}
|
||||
|
||||
ValueSetExpansionOptions options = createValueSetExpansionOptions(myDaoConfig, theOffset, theCount, theIncludeHierarchy, theFilter);
|
||||
|
||||
startRequest(theServletRequest);
|
||||
try {
|
||||
IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> dao = (IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept>) getDao();
|
||||
if (haveId) {
|
||||
return dao.expand(theId, options, theRequestDetails);
|
||||
} else if (haveIdentifier) {
|
||||
if (haveValueSetVersion) {
|
||||
return dao.expandByIdentifier(theUrl.getValue() + "|" + theValueSetVersion.getValue(), options);
|
||||
} else {
|
||||
return dao.expandByIdentifier(theUrl.getValue(), options);
|
||||
}
|
||||
} else {
|
||||
return dao.expand(theValueSet, options);
|
||||
}
|
||||
} finally {
|
||||
endRequest(theServletRequest);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Operation(name = JpaConstants.OPERATION_VALIDATE_CODE, idempotent = true, returnParameters = {
|
||||
@OperationParam(name = "result", type = BooleanType.class, min = 1),
|
||||
@OperationParam(name = "message", type = StringType.class),
|
||||
@OperationParam(name = "display", type = StringType.class)
|
||||
})
|
||||
public Parameters validateCode(
|
||||
HttpServletRequest theServletRequest,
|
||||
@IdParam(optional = true) IdType theId,
|
||||
@OperationParam(name = "url", min = 0, max = 1) UriType theValueSetUrl,
|
||||
@OperationParam(name = "valueSetVersion", min = 0, max = 1) StringType theValueSetVersion,
|
||||
@OperationParam(name = "code", min = 0, max = 1) CodeType theCode,
|
||||
@OperationParam(name = "system", min = 0, max = 1) UriType theSystem,
|
||||
@OperationParam(name = "systemVersion", min = 0, max = 1) StringType theSystemVersion,
|
||||
@OperationParam(name = "display", min = 0, max = 1) StringType theDisplay,
|
||||
@OperationParam(name = "coding", min = 0, max = 1) Coding theCoding,
|
||||
@OperationParam(name = "codeableConcept", min = 0, max = 1) CodeableConcept theCodeableConcept,
|
||||
RequestDetails theRequestDetails
|
||||
) {
|
||||
|
||||
startRequest(theServletRequest);
|
||||
try {
|
||||
IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> dao = (IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept>) getDao();
|
||||
UriType valueSetIdentifier;
|
||||
if (theValueSetVersion != null) {
|
||||
valueSetIdentifier = new UriType(theValueSetUrl.getValue() + "|" + theValueSetVersion);
|
||||
} else {
|
||||
valueSetIdentifier = theValueSetUrl;
|
||||
}
|
||||
UriType codeSystemIdentifier;
|
||||
if (theSystemVersion != null) {
|
||||
codeSystemIdentifier = new UriType(theSystem.getValue() + "|" + theSystemVersion);
|
||||
} else {
|
||||
codeSystemIdentifier = theSystem;
|
||||
}
|
||||
IValidationSupport.CodeValidationResult result = dao.validateCode(valueSetIdentifier, theId, theCode, codeSystemIdentifier, theDisplay, theCoding, theCodeableConcept, theRequestDetails);
|
||||
return (Parameters) BaseJpaResourceProviderValueSetDstu2.toValidateCodeResult(getContext(), result);
|
||||
} finally {
|
||||
endRequest(theServletRequest);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static boolean moreThanOneTrue(boolean... theBooleans) {
|
||||
boolean haveOne = false;
|
||||
for (boolean next : theBooleans) {
|
||||
if (next) {
|
||||
if (haveOne) {
|
||||
return true;
|
||||
} else {
|
||||
haveOne = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// nothing
|
||||
}
|
||||
|
|
|
@ -65,15 +65,14 @@ 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.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||
import ca.uhn.fhir.util.CoverageIgnore;
|
||||
|
@ -103,6 +102,10 @@ 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.convertors.advisors.impl.BaseAdvisor_40_50;
|
||||
import org.hl7.fhir.convertors.context.ConversionContext40_50;
|
||||
import org.hl7.fhir.convertors.conv40_50.VersionConvertor_40_50;
|
||||
import org.hl7.fhir.convertors.conv40_50.resources40_50.ValueSet40_50;
|
||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||
import org.hl7.fhir.instance.model.api.IBaseCoding;
|
||||
import org.hl7.fhir.instance.model.api.IBaseDatatype;
|
||||
|
@ -117,6 +120,7 @@ import org.hl7.fhir.r4.model.Coding;
|
|||
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.InstantType;
|
||||
import org.hl7.fhir.r4.model.IntegerType;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
import org.hl7.fhir.r4.model.ValueSet;
|
||||
|
@ -170,9 +174,11 @@ import java.util.StringTokenizer;
|
|||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static ca.uhn.fhir.jpa.term.api.ITermLoaderSvc.LOINC_URI;
|
||||
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
|
||||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
|
@ -256,6 +262,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
|
||||
private boolean addCodeIfNotAlreadyAdded(IValueSetConceptAccumulator theValueSetCodeAccumulator, Set<String> theAddedCodes, TermConcept theConcept, boolean theAdd, String theValueSetIncludeVersion) {
|
||||
String codeSystem = theConcept.getCodeSystemVersion().getCodeSystem().getCodeSystemUri();
|
||||
String codeSystemVersion = theConcept.getCodeSystemVersion().getCodeSystemVersionId();
|
||||
String code = theConcept.getCode();
|
||||
String display = theConcept.getDisplay();
|
||||
Long sourceConceptPid = theConcept.getId();
|
||||
|
@ -268,9 +275,9 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
|
||||
Collection<TermConceptDesignation> designations = theConcept.getDesignations();
|
||||
if (StringUtils.isNotEmpty(theValueSetIncludeVersion)) {
|
||||
return addCodeIfNotAlreadyAdded(theValueSetCodeAccumulator, theAddedCodes, designations, theAdd, codeSystem + "|" + theValueSetIncludeVersion, code, display, sourceConceptPid, directParentPids);
|
||||
return addCodeIfNotAlreadyAdded(theValueSetCodeAccumulator, theAddedCodes, designations, theAdd, codeSystem + "|" + theValueSetIncludeVersion, code, display, sourceConceptPid, directParentPids, codeSystemVersion);
|
||||
} else {
|
||||
return addCodeIfNotAlreadyAdded(theValueSetCodeAccumulator, theAddedCodes, designations, theAdd, codeSystem, code, display, sourceConceptPid, directParentPids);
|
||||
return addCodeIfNotAlreadyAdded(theValueSetCodeAccumulator, theAddedCodes, designations, theAdd, codeSystem, code, display, sourceConceptPid, directParentPids, codeSystemVersion);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -278,7 +285,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
if (StringUtils.isNotEmpty(theCodeSystemVersion)) {
|
||||
if (isNoneBlank(theCodeSystem, theCode)) {
|
||||
if (theAdd && theAddedCodes.add(theCodeSystem + "|" + theCode)) {
|
||||
theValueSetCodeAccumulator.includeConceptWithDesignations(theCodeSystem + "|" + theCodeSystemVersion, theCode, theDisplay, null, theSourceConceptPid, theSourceConceptDirectParentPids);
|
||||
theValueSetCodeAccumulator.includeConceptWithDesignations(theCodeSystem + "|" + theCodeSystemVersion, theCode, theDisplay, null, theSourceConceptPid, theSourceConceptDirectParentPids, theCodeSystemVersion);
|
||||
}
|
||||
|
||||
if (!theAdd && theAddedCodes.remove(theCodeSystem + "|" + theCode)) {
|
||||
|
@ -287,7 +294,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
}
|
||||
} else {
|
||||
if (theAdd && theAddedCodes.add(theCodeSystem + "|" + theCode)) {
|
||||
theValueSetCodeAccumulator.includeConceptWithDesignations(theCodeSystem, theCode, theDisplay, null, theSourceConceptPid, theSourceConceptDirectParentPids);
|
||||
theValueSetCodeAccumulator.includeConceptWithDesignations(theCodeSystem, theCode, theDisplay, null, theSourceConceptPid, theSourceConceptDirectParentPids, theCodeSystemVersion);
|
||||
}
|
||||
|
||||
if (!theAdd && theAddedCodes.remove(theCodeSystem + "|" + theCode)) {
|
||||
|
@ -296,10 +303,10 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
}
|
||||
}
|
||||
|
||||
private boolean addCodeIfNotAlreadyAdded(IValueSetConceptAccumulator theValueSetCodeAccumulator, Set<String> theAddedCodes, Collection<TermConceptDesignation> theDesignations, boolean theAdd, String theCodeSystem, String theCode, String theDisplay, Long theSourceConceptPid, String theSourceConceptDirectParentPids) {
|
||||
private boolean addCodeIfNotAlreadyAdded(IValueSetConceptAccumulator theValueSetCodeAccumulator, Set<String> theAddedCodes, Collection<TermConceptDesignation> theDesignations, boolean theAdd, String theCodeSystem, String theCode, String theDisplay, Long theSourceConceptPid, String theSourceConceptDirectParentPids, String theSystemVersion) {
|
||||
if (isNoneBlank(theCodeSystem, theCode)) {
|
||||
if (theAdd && theAddedCodes.add(theCodeSystem + "|" + theCode)) {
|
||||
theValueSetCodeAccumulator.includeConceptWithDesignations(theCodeSystem, theCode, theDisplay, theDesignations, theSourceConceptPid, theSourceConceptDirectParentPids);
|
||||
theValueSetCodeAccumulator.includeConceptWithDesignations(theCodeSystem, theCode, theDisplay, theDesignations, theSourceConceptPid, theSourceConceptDirectParentPids, theSystemVersion);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -312,17 +319,6 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
return false;
|
||||
}
|
||||
|
||||
private void addConceptsToList(IValueSetConceptAccumulator theValueSetCodeAccumulator, Set<String> theAddedCodes, String theSystem, List<CodeSystem.ConceptDefinitionComponent> theConcept, boolean theAdd, @Nonnull ExpansionFilter theExpansionFilter) {
|
||||
for (CodeSystem.ConceptDefinitionComponent next : theConcept) {
|
||||
if (isNoneBlank(theSystem, next.getCode())) {
|
||||
if (!theExpansionFilter.hasCode() || theExpansionFilter.getCode().equals(next.getCode())) {
|
||||
addOrRemoveCode(theValueSetCodeAccumulator, theAddedCodes, theAdd, theSystem, next.getCode(), next.getDisplay());
|
||||
}
|
||||
}
|
||||
addConceptsToList(theValueSetCodeAccumulator, theAddedCodes, theSystem, next.getConcept(), theAdd, theExpansionFilter);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean addToSet(Set<TermConcept> theSetToPopulate, TermConcept theConcept) {
|
||||
boolean retVal = theSetToPopulate.add(theConcept);
|
||||
if (retVal) {
|
||||
|
@ -351,13 +347,17 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
TermValueSet existingTermValueSet = optionalExistingTermValueSetById.get();
|
||||
|
||||
ourLog.info("Deleting existing TermValueSet[{}] and its children...", existingTermValueSet.getId());
|
||||
myValueSetConceptDesignationDao.deleteByTermValueSetId(existingTermValueSet.getId());
|
||||
myValueSetConceptDao.deleteByTermValueSetId(existingTermValueSet.getId());
|
||||
deletePreCalculatedValueSetContents(existingTermValueSet);
|
||||
myTermValueSetDao.deleteById(existingTermValueSet.getId());
|
||||
ourLog.info("Done deleting existing TermValueSet[{}] and its children.", existingTermValueSet.getId());
|
||||
}
|
||||
}
|
||||
|
||||
private void deletePreCalculatedValueSetContents(TermValueSet theValueSet) {
|
||||
myValueSetConceptDesignationDao.deleteByTermValueSetId(theValueSet.getId());
|
||||
myValueSetConceptDao.deleteByTermValueSetId(theValueSet.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void deleteValueSetAndChildren(ResourceTable theResourceTable) {
|
||||
|
@ -462,6 +462,8 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
*/
|
||||
if (!optionalTermValueSet.isPresent()) {
|
||||
ourLog.debug("ValueSet is not present in terminology tables. Will perform in-memory expansion without parameters. {}", getValueSetInfo(theValueSetToExpand));
|
||||
String msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "valueSetExpandedUsingInMemoryExpansion", getValueSetInfo(theValueSetToExpand));
|
||||
theAccumulator.addMessage(msg);
|
||||
expandValueSet(theExpansionOptions, theValueSetToExpand, theAccumulator, theFilter);
|
||||
return;
|
||||
}
|
||||
|
@ -480,11 +482,22 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
/*
|
||||
* ValueSet is pre-expanded in database so let's use that
|
||||
*/
|
||||
String msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "valueSetExpandedUsingPreExpansion");
|
||||
String expansionTimestamp = toHumanReadableExpansionTimestamp(termValueSet);
|
||||
String msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "valueSetExpandedUsingPreExpansion", expansionTimestamp);
|
||||
theAccumulator.addMessage(msg);
|
||||
expandConcepts(theAccumulator, termValueSet, theFilter, theAdd, isOracleDialect());
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private String toHumanReadableExpansionTimestamp(TermValueSet termValueSet) {
|
||||
String expansionTimestamp = "(unknown)";
|
||||
if (termValueSet.getExpansionTimestamp() != null) {
|
||||
String timeElapsed = StopWatch.formatMillis(System.currentTimeMillis() - termValueSet.getExpansionTimestamp().getTime());
|
||||
expansionTimestamp = new InstantType(termValueSet.getExpansionTimestamp()).getValueAsString() + " (" + timeElapsed + " ago)";
|
||||
}
|
||||
return expansionTimestamp;
|
||||
}
|
||||
|
||||
private boolean isOracleDialect() {
|
||||
return myHibernatePropertiesProvider.getDialect() instanceof org.hibernate.dialect.Oracle12cDialect;
|
||||
}
|
||||
|
@ -543,6 +556,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
String system = conceptView.getConceptSystemUrl();
|
||||
String code = conceptView.getConceptCode();
|
||||
String display = conceptView.getConceptDisplay();
|
||||
String systemVersion = conceptView.getConceptSystemVersion();
|
||||
|
||||
//-- this is quick solution, may need to revisit
|
||||
if (!applyFilter(display, filterDisplayValue))
|
||||
|
@ -550,7 +564,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
|
||||
Long conceptPid = conceptView.getConceptPid();
|
||||
if (!pidToConcept.containsKey(conceptPid)) {
|
||||
FhirVersionIndependentConcept concept = new FhirVersionIndependentConcept(system, code, display);
|
||||
FhirVersionIndependentConcept concept = new FhirVersionIndependentConcept(system, code, display, systemVersion);
|
||||
pidToConcept.put(conceptPid, concept);
|
||||
}
|
||||
|
||||
|
@ -585,6 +599,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
String system = concept.getSystem();
|
||||
String code = concept.getCode();
|
||||
String display = concept.getDisplay();
|
||||
String systemVersion = concept.getSystemVersion();
|
||||
|
||||
if (theAdd) {
|
||||
if (theAccumulator.getCapacityRemaining() != null) {
|
||||
|
@ -595,7 +610,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
|
||||
Long sourceConceptPid = pidToSourcePid.get(nextPid);
|
||||
String sourceConceptDirectParentPids = pidToSourceDirectParentPids.get(nextPid);
|
||||
theAccumulator.includeConceptWithDesignations(system, code, display, designations, sourceConceptPid, sourceConceptDirectParentPids);
|
||||
theAccumulator.includeConceptWithDesignations(system, code, display, designations, sourceConceptPid, sourceConceptDirectParentPids, systemVersion);
|
||||
} else {
|
||||
boolean removed = theAccumulator.excludeConcept(system, code);
|
||||
if (removed) {
|
||||
|
@ -789,77 +804,21 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
}
|
||||
}
|
||||
|
||||
// No CodeSystem matching the URL found in the database.
|
||||
CodeSystem codeSystemFromContext = fetchCanonicalCodeSystemFromCompleteContext(system);
|
||||
if (codeSystemFromContext == null) {
|
||||
Consumer<FhirVersionIndependentConcept> consumer = c -> {
|
||||
addOrRemoveCode(theValueSetCodeAccumulator, theAddedCodes, theAdd, system, c.getCode(), c.getDisplay(), c.getSystemVersion());
|
||||
};
|
||||
|
||||
// This is a last ditch effort.. We don't have a CodeSystem resource for the desired CS, and we don't have
|
||||
// anything at all in the database that matches it. So let's try asking the validation support context
|
||||
// just in case there is a registered service that knows how to handle this. This can happen, for example,
|
||||
// if someone creates a valueset that includes UCUM codes, since we don't have a CodeSystem resource for those
|
||||
// but CommonCodeSystemsTerminologyService can validate individual codes.
|
||||
List<FhirVersionIndependentConcept> includedConcepts = null;
|
||||
if (theExpansionFilter.hasCode()) {
|
||||
includedConcepts = new ArrayList<>();
|
||||
includedConcepts.add(theExpansionFilter.toFhirVersionIndependentConcept());
|
||||
} else if (!theIncludeOrExclude.getConcept().isEmpty()) {
|
||||
includedConcepts = theIncludeOrExclude
|
||||
.getConcept()
|
||||
.stream()
|
||||
.map(t -> new FhirVersionIndependentConcept(theIncludeOrExclude.getSystem(), t.getCode()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
if (includedConcepts != null) {
|
||||
int foundCount = 0;
|
||||
for (FhirVersionIndependentConcept next : includedConcepts) {
|
||||
String nextSystem = next.getSystem();
|
||||
if (nextSystem == null) {
|
||||
nextSystem = system;
|
||||
}
|
||||
|
||||
LookupCodeResult lookup = myValidationSupport.lookupCode(new ValidationSupportContext(provideValidationSupport()), nextSystem, next.getCode());
|
||||
if (lookup != null && lookup.isFound()) {
|
||||
addOrRemoveCode(theValueSetCodeAccumulator, theAddedCodes, theAdd, nextSystem, next.getCode(), lookup.getCodeDisplay());
|
||||
foundCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (foundCount == includedConcepts.size()) {
|
||||
return false;
|
||||
// ELSE, we'll continue below and throw an exception
|
||||
}
|
||||
}
|
||||
|
||||
String msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "expansionRefersToUnknownCs", system);
|
||||
if (provideExpansionOptions(theExpansionOptions).isFailOnMissingCodeSystem()) {
|
||||
throw new PreconditionFailedException(msg);
|
||||
} else {
|
||||
ourLog.warn(msg);
|
||||
theValueSetCodeAccumulator.addMessage(msg);
|
||||
try {
|
||||
ConversionContext40_50.INSTANCE.init(new VersionConvertor_40_50(new BaseAdvisor_40_50()), "ValueSet");
|
||||
org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent includeOrExclude = ValueSet40_50.convertConceptSetComponent(theIncludeOrExclude);
|
||||
new InMemoryTerminologyServerValidationSupport(myContext).expandValueSetIncludeOrExclude(new ValidationSupportContext(provideValidationSupport()), consumer, includeOrExclude);
|
||||
} catch (InMemoryTerminologyServerValidationSupport.ExpansionCouldNotBeCompletedInternallyException e) {
|
||||
if (!theExpansionOptions.isFailOnMissingCodeSystem() && e.getFailureType() == InMemoryTerminologyServerValidationSupport.FailureType.UNKNOWN_CODE_SYSTEM) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!theIncludeOrExclude.getConcept().isEmpty()) {
|
||||
for (ValueSet.ConceptReferenceComponent next : theIncludeOrExclude.getConcept()) {
|
||||
String nextCode = next.getCode();
|
||||
if (!theExpansionFilter.hasCode() || theExpansionFilter.getCode().equals(nextCode)) {
|
||||
if (isNoneBlank(system, nextCode) && !theAddedCodes.contains(system + "|" + nextCode)) {
|
||||
|
||||
CodeSystem.ConceptDefinitionComponent code = findCode(codeSystemFromContext.getConcept(), nextCode);
|
||||
if (code != null) {
|
||||
String display = code.getDisplay();
|
||||
addOrRemoveCode(theValueSetCodeAccumulator, theAddedCodes, theAdd, system, nextCode, display);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
List<CodeSystem.ConceptDefinitionComponent> concept = codeSystemFromContext.getConcept();
|
||||
addConceptsToList(theValueSetCodeAccumulator, theAddedCodes, system, concept, theAdd, theExpansionFilter);
|
||||
throw new InternalErrorException(e);
|
||||
} finally {
|
||||
ConversionContext40_50.INSTANCE.close("ValueSet");
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -1038,10 +997,11 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
}
|
||||
|
||||
private ValueSet.ConceptReferenceComponent getMatchedConceptIncludedInValueSet(ValueSet.ConceptSetComponent theIncludeOrExclude, TermConcept concept) {
|
||||
ValueSet.ConceptReferenceComponent theIncludeConcept = theIncludeOrExclude.getConcept().stream().filter(includedConcept ->
|
||||
includedConcept.getCode().equalsIgnoreCase(concept.getCode())
|
||||
).findFirst().orElse(null);
|
||||
return theIncludeConcept;
|
||||
return theIncludeOrExclude
|
||||
.getConcept()
|
||||
.stream().filter(includedConcept -> includedConcept.getCode().equalsIgnoreCase(concept.getCode()))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1089,9 +1049,9 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
}
|
||||
}
|
||||
|
||||
private void addOrRemoveCode(IValueSetConceptAccumulator theValueSetCodeAccumulator, Set<String> theAddedCodes, boolean theAdd, String theSystem, String theCode, String theDisplay) {
|
||||
private void addOrRemoveCode(IValueSetConceptAccumulator theValueSetCodeAccumulator, Set<String> theAddedCodes, boolean theAdd, String theSystem, String theCode, String theDisplay, String theSystemVersion) {
|
||||
if (theAdd && theAddedCodes.add(theSystem + "|" + theCode)) {
|
||||
theValueSetCodeAccumulator.includeConcept(theSystem, theCode, theDisplay, null, null);
|
||||
theValueSetCodeAccumulator.includeConcept(theSystem, theCode, theDisplay, null, null, theSystemVersion);
|
||||
}
|
||||
if (!theAdd && theAddedCodes.remove(theSystem + "|" + theCode)) {
|
||||
theValueSetCodeAccumulator.excludeConcept(theSystem, theCode);
|
||||
|
@ -1211,6 +1171,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
theBooleanClause.must(thePredicateFactory.exists().field(TermConceptPropertyBinder.CONCEPT_FIELD_PROPERTY_PREFIX + "EXTERNAL_COPYRIGHT_NOTICE"));
|
||||
}
|
||||
|
||||
@SuppressWarnings("EnumSwitchStatementWhichMissesCases")
|
||||
private void handleFilterLoincAncestor2(String theSystem, SearchPredicateFactory f, BooleanPredicateClausesStep<?> b, ValueSet.ConceptSetFilterComponent theFilter) {
|
||||
switch (theFilter.getOp()) {
|
||||
case EQUAL:
|
||||
|
@ -1244,6 +1205,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
|
||||
}
|
||||
|
||||
@SuppressWarnings("EnumSwitchStatementWhichMissesCases")
|
||||
private void handleFilterLoincParentChild(SearchPredicateFactory f, BooleanPredicateClausesStep<?> b, ValueSet.ConceptSetFilterComponent theFilter) {
|
||||
switch (theFilter.getOp()) {
|
||||
case EQUAL:
|
||||
|
@ -1305,7 +1267,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
}
|
||||
|
||||
private boolean isCodeSystemLoinc(String theSystem) {
|
||||
return ITermLoaderSvc.LOINC_URI.equals(theSystem);
|
||||
return LOINC_URI.equals(theSystem);
|
||||
}
|
||||
|
||||
private void handleFilterDisplay(SearchPredicateFactory f, BooleanPredicateClausesStep<?> b, ValueSet.ConceptSetFilterComponent theFilter) {
|
||||
|
@ -1436,7 +1398,6 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
Validate.isTrue(theInclude.getFilter().isEmpty(), "Can not expand ValueSet with filters - Hibernate Search is not enabled on this server.");
|
||||
Validate.isTrue(isNotBlank(theSystem), "Can not expand ValueSet without explicit system - Hibernate Search is not enabled on this server.");
|
||||
|
||||
|
||||
if (theInclude.getConcept().isEmpty()) {
|
||||
for (TermConcept next : theVersion.getConcepts()) {
|
||||
addCodeIfNotAlreadyAdded(theValueSetCodeAccumulator, theAddedCodes, theAdd, theSystem, theInclude.getVersion(), next.getCode(), next.getDisplay(), next.getId(), next.getParentPidsAsString());
|
||||
|
@ -1453,10 +1414,37 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public String invalidatePreCalculatedExpansion(IIdType theValueSetId, RequestDetails theRequestDetails) {
|
||||
IBaseResource valueSet = myDaoRegistry.getResourceDao("ValueSet").read(theValueSetId, theRequestDetails);
|
||||
ValueSet canonicalValueSet = toCanonicalValueSet(valueSet);
|
||||
Optional<TermValueSet> optionalTermValueSet = fetchValueSetEntity(canonicalValueSet);
|
||||
if (!optionalTermValueSet.isPresent()) {
|
||||
return myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "valueSetNotFoundInTerminologyDatabase", theValueSetId);
|
||||
}
|
||||
|
||||
ourLog.info("Invalidating pre-calculated expansion on ValueSet {} / {}", theValueSetId, canonicalValueSet.getUrl());
|
||||
|
||||
TermValueSet termValueSet = optionalTermValueSet.get();
|
||||
if (termValueSet.getExpansionStatus() == TermValueSetPreExpansionStatusEnum.NOT_EXPANDED) {
|
||||
return myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "valueSetCantInvalidateNotYetPrecalculated", termValueSet.getUrl(), termValueSet.getExpansionStatus());
|
||||
}
|
||||
|
||||
Long totalConcepts = termValueSet.getTotalConcepts();
|
||||
|
||||
deletePreCalculatedValueSetContents(termValueSet);
|
||||
|
||||
termValueSet.setExpansionStatus(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED);
|
||||
termValueSet.setExpansionTimestamp(null);
|
||||
myTermValueSetDao.save(termValueSet);
|
||||
return myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "valueSetPreExpansionInvalidated", termValueSet.getUrl(), totalConcepts);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isValueSetPreExpandedForCodeValidation(ValueSet theValueSet) {
|
||||
ResourcePersistentId valueSetResourcePid = myConceptStorageSvc.getValueSetResourcePid(theValueSet.getIdElement());
|
||||
Optional<TermValueSet> optionalTermValueSet = myTermValueSetDao.findByResourcePid(valueSetResourcePid.getIdAsLong());
|
||||
Optional<TermValueSet> optionalTermValueSet = fetchValueSetEntity(theValueSet);
|
||||
|
||||
if (!optionalTermValueSet.isPresent()) {
|
||||
ourLog.warn("ValueSet is not present in terminology tables. Will perform in-memory code validation. {}", getValueSetInfo(theValueSet));
|
||||
|
@ -1474,6 +1462,12 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
return true;
|
||||
}
|
||||
|
||||
private Optional<TermValueSet> fetchValueSetEntity(ValueSet theValueSet) {
|
||||
ResourcePersistentId valueSetResourcePid = myConceptStorageSvc.getValueSetResourcePid(theValueSet.getIdElement());
|
||||
Optional<TermValueSet> optionalTermValueSet = myTermValueSetDao.findByResourcePid(valueSetResourcePid.getIdAsLong());
|
||||
return optionalTermValueSet;
|
||||
}
|
||||
|
||||
protected IValidationSupport.CodeValidationResult validateCodeIsInPreExpandedValueSet(
|
||||
ConceptValidationOptions theValidationOptions,
|
||||
ValueSet theValueSet, String theSystem, String theCode, String theDisplay, Coding theCoding, CodeableConcept theCodeableConcept) {
|
||||
|
@ -1505,36 +1499,42 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
return null;
|
||||
}
|
||||
|
||||
TermValueSet valueSetEntity = myTermValueSetDao.findByResourcePid(valueSetResourcePid.getIdAsLong()).orElseThrow(() -> new IllegalStateException());
|
||||
Object timingDescription = toHumanReadableExpansionTimestamp(valueSetEntity);
|
||||
String msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "validationPerformedAgainstPreExpansion", timingDescription);
|
||||
|
||||
if (theValidationOptions.isValidateDisplay() && concepts.size() > 0) {
|
||||
String systemVersion = null;
|
||||
for (TermValueSetConcept concept : concepts) {
|
||||
systemVersion = concept.getSystemVersion();
|
||||
if (isBlank(theDisplay) || isBlank(concept.getDisplay()) || theDisplay.equals(concept.getDisplay())) {
|
||||
return new IValidationSupport.CodeValidationResult()
|
||||
.setCode(concept.getCode())
|
||||
.setDisplay(concept.getDisplay());
|
||||
.setDisplay(concept.getDisplay())
|
||||
.setCodeSystemVersion(concept.getSystemVersion())
|
||||
.setMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
return createFailureCodeValidationResult(theSystem, theCode, " - Concept Display \"" + theDisplay + "\" does not match expected \"" + concepts.get(0).getDisplay() + "\"").setDisplay(concepts.get(0).getDisplay());
|
||||
return createFailureCodeValidationResult(theSystem, theCode, systemVersion, " - Concept Display \"" + theDisplay + "\" does not match expected \"" + concepts.get(0).getDisplay() + "\". " + msg).setDisplay(concepts.get(0).getDisplay());
|
||||
}
|
||||
|
||||
if (!concepts.isEmpty()) {
|
||||
return new IValidationSupport.CodeValidationResult()
|
||||
.setCode(concepts.get(0).getCode())
|
||||
.setDisplay(concepts.get(0).getDisplay());
|
||||
.setDisplay(concepts.get(0).getDisplay())
|
||||
.setCodeSystemVersion(concepts.get(0).getSystemVersion())
|
||||
.setMessage(msg);
|
||||
}
|
||||
|
||||
return createFailureCodeValidationResult(theSystem, theCode);
|
||||
return createFailureCodeValidationResult(theSystem, theCode, null, " - Unknown code " + theSystem + "#" + theCode + ". " + msg);
|
||||
}
|
||||
|
||||
private CodeValidationResult createFailureCodeValidationResult(String theSystem, String theCode) {
|
||||
String append = "";
|
||||
return createFailureCodeValidationResult(theSystem, theCode, append);
|
||||
}
|
||||
|
||||
private CodeValidationResult createFailureCodeValidationResult(String theSystem, String theCode, String theAppend) {
|
||||
private CodeValidationResult createFailureCodeValidationResult(String theSystem, String theCode, String theCodeSystemVersion, String theAppend) {
|
||||
return new CodeValidationResult()
|
||||
.setSeverity(IssueSeverity.ERROR)
|
||||
.setMessage("Unknown code {" + theSystem + "}" + theCode + theAppend);
|
||||
.setCodeSystemVersion(theCodeSystemVersion)
|
||||
.setMessage("Unable to validate code " + theSystem + "#" + theCode + theAppend);
|
||||
}
|
||||
|
||||
private List<TermValueSetConcept> findByValueSetResourcePidSystemAndCode(ResourcePersistentId theResourcePid, String theSystem, String theCode) {
|
||||
|
@ -1781,6 +1781,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
// We are done with this ValueSet.
|
||||
txTemplate.execute(t -> {
|
||||
valueSetToExpand.setExpansionStatus(TermValueSetPreExpansionStatusEnum.EXPANDED);
|
||||
valueSetToExpand.setExpansionTimestamp(new Date());
|
||||
myTermValueSetDao.saveAndFlush(valueSetToExpand);
|
||||
return null;
|
||||
});
|
||||
|
@ -2096,11 +2097,11 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
.setCode(code.getCode())
|
||||
.setDisplay(code.getDisplay());
|
||||
} else {
|
||||
return createFailureCodeValidationResult(theCodeSystem, theCode, " - Concept Display \"" + code.getDisplay() + "\" does not match expected \"" + code.getDisplay() + "\"").setDisplay(code.getDisplay());
|
||||
return createFailureCodeValidationResult(theCodeSystem, theCode, code.getSystemVersion(), " - Concept Display \"" + code.getDisplay() + "\" does not match expected \"" + code.getDisplay() + "\"").setDisplay(code.getDisplay());
|
||||
}
|
||||
}
|
||||
|
||||
return createFailureCodeValidationResult(theCodeSystem, theCode);
|
||||
return createFailureCodeValidationResult(theCodeSystem, theCode, null, " - Code can not be found in CodeSystem");
|
||||
}
|
||||
|
||||
IValidationSupport.CodeValidationResult validateCodeInValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theValidationOptions, String theValueSetUrl, String theCodeSystem, String theCode, String theDisplay) {
|
||||
|
@ -2112,7 +2113,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
Long pid = IDao.RESOURCE_PID.get((IAnyResource) valueSet);
|
||||
if (pid != null) {
|
||||
if (isValueSetPreExpandedForCodeValidation(valueSet)) {
|
||||
return validateCodeIsInPreExpandedValueSet(theValidationOptions, valueSet, theCodeSystem, theCode, null, null, null);
|
||||
return validateCodeIsInPreExpandedValueSet(theValidationOptions, valueSet, theCodeSystem, theCode, theDisplay, null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2122,7 +2123,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
retVal = new InMemoryTerminologyServerValidationSupport(myContext).validateCodeInValueSet(theValidationSupportContext, theValidationOptions, theCodeSystem, theCode, theDisplay, valueSet);
|
||||
} else {
|
||||
String append = " - Unable to locate ValueSet[" + theValueSetUrl + "]";
|
||||
retVal = createFailureCodeValidationResult(theCodeSystem, theCode, append);
|
||||
retVal = createFailureCodeValidationResult(theCodeSystem, theCode, null, append);
|
||||
}
|
||||
|
||||
return retVal;
|
||||
|
@ -2383,10 +2384,6 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
predicates.add(criteriaBuilder.equal(root.get("myCode"), theCode));
|
||||
}
|
||||
|
||||
if (isNotBlank(theDisplay)) {
|
||||
predicates.add(criteriaBuilder.equal(root.get("myDisplay"), theDisplay));
|
||||
}
|
||||
|
||||
if (isNoneBlank(theCodeSystemUrl)) {
|
||||
predicates.add(criteriaBuilder.equal(systemJoin.get("myCodeSystemUri"), theCodeSystemUrl));
|
||||
}
|
||||
|
@ -2413,13 +2410,33 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
|
||||
if (!resultsList.isEmpty()) {
|
||||
TermConcept concept = resultsList.get(0);
|
||||
|
||||
if (isNotBlank(theDisplay) && !theDisplay.equals(concept.getDisplay())) {
|
||||
String message = "Concept Display \"" + theDisplay + "\" does not match expected \"" + concept.getDisplay() + "\" for CodeSystem: " + theCodeSystemUrl;
|
||||
return createFailureCodeValidationResult(theCodeSystemUrl, theCode, theCodeSystemVersion, message);
|
||||
}
|
||||
|
||||
return new CodeValidationResult().setCode(concept.getCode()).setDisplay(concept.getDisplay());
|
||||
}
|
||||
|
||||
if (isBlank(theDisplay))
|
||||
return createFailureCodeValidationResult(theCodeSystemUrl, theCode);
|
||||
else
|
||||
return createFailureCodeValidationResult(theCodeSystemUrl, theCode, " - Concept Display : " + theDisplay);
|
||||
return createFailureCodeValidationResult(theCodeSystemUrl, theCode, theCodeSystemVersion, " - Code is not found in CodeSystem: " + theCodeSystemUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<IBaseResource> readCodeSystemByForcedId(String theForcedId) {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<ResourceTable> resultList = (List<ResourceTable>) myEntityManager.createQuery(
|
||||
"select f.myResource from ForcedId f " +
|
||||
"where f.myResourceType = 'CodeSystem' and f.myForcedId = '" + theForcedId + "'").getResultList();
|
||||
if (resultList.isEmpty()) return Optional.empty();
|
||||
|
||||
if (resultList.size() > 1) throw new NonUniqueResultException(
|
||||
"More than one CodeSystem is pointed by forcedId: " + theForcedId + ". Was constraint "
|
||||
+ ForcedId.IDX_FORCEDID_TYPE_FID + " removed?");
|
||||
|
||||
IFhirResourceDao<CodeSystem> csDao = myDaoRegistry.getResourceDao("CodeSystem");
|
||||
IBaseResource cs = csDao.toResource(resultList.get(0), false);
|
||||
return Optional.of(cs);
|
||||
}
|
||||
|
||||
public static class Job implements HapiJob {
|
||||
|
@ -2526,22 +2543,4 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
return theReqLang.equalsIgnoreCase(theStoredLang);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Optional<IBaseResource> readCodeSystemByForcedId(String theForcedId) {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<ResourceTable> resultList = (List<ResourceTable>) myEntityManager.createQuery(
|
||||
"select f.myResource from ForcedId f " +
|
||||
"where f.myResourceType = 'CodeSystem' and f.myForcedId = '" + theForcedId + "'").getResultList();
|
||||
if (resultList.isEmpty()) return Optional.empty();
|
||||
|
||||
if (resultList.size() > 1) throw new NonUniqueResultException(
|
||||
"More than one CodeSystem is pointed by forcedId: " + theForcedId + ". Was constraint "
|
||||
+ ForcedId.IDX_FORCEDID_TYPE_FID + " removed?");
|
||||
|
||||
IFhirResourceDao<CodeSystem> csDao = myDaoRegistry.getResourceDao("CodeSystem");
|
||||
IBaseResource cs = csDao.toResource(resultList.get(0), false);
|
||||
return Optional.of(cs );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -29,9 +29,9 @@ public interface IValueSetConceptAccumulator {
|
|||
|
||||
void addMessage(String theMessage);
|
||||
|
||||
void includeConcept(String theSystem, String theCode, String theDisplay, Long theSourceConceptPid, String theSourceConceptDirectParentPids);
|
||||
void includeConcept(String theSystem, String theCode, String theDisplay, Long theSourceConceptPid, String theSourceConceptDirectParentPids, @Nullable String theSystemVersion);
|
||||
|
||||
void includeConceptWithDesignations(String theSystem, String theCode, String theDisplay, @Nullable Collection<TermConceptDesignation> theDesignations, Long theSourceConceptPid, String theSourceConceptDirectParentPids);
|
||||
void includeConceptWithDesignations(String theSystem, String theCode, String theDisplay, @Nullable Collection<TermConceptDesignation> theDesignations, Long theSourceConceptPid, String theSourceConceptDirectParentPids, @Nullable String theSystemVersion);
|
||||
|
||||
/**
|
||||
* @return Returns <code>true</code> if the code was actually present and was removed
|
||||
|
|
|
@ -65,7 +65,7 @@ public class TermReadSvcDstu3 extends BaseTermReadSvcImpl implements IValidation
|
|||
org.hl7.fhir.r4.model.ValueSet valueSetToExpandR4;
|
||||
valueSetToExpandR4 = toCanonicalValueSet(theValueSetToExpand);
|
||||
org.hl7.fhir.r4.model.ValueSet expandedR4 = super.expandValueSet(theExpansionOptions, valueSetToExpandR4);
|
||||
return new ValueSetExpansionOutcome(VersionConvertorFactory_30_40.convertResource(expandedR4, new BaseAdvisor_30_40(false)), null);
|
||||
return new ValueSetExpansionOutcome(VersionConvertorFactory_30_40.convertResource(expandedR4, new BaseAdvisor_30_40(false)));
|
||||
} catch (FHIRException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
|
|
|
@ -66,13 +66,13 @@ public class ValueSetConceptAccumulator implements IValueSetConceptAccumulator {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void includeConcept(String theSystem, String theCode, String theDisplay, Long theSourceConceptPid, String theSourceConceptDirectParentPids) {
|
||||
saveConcept(theSystem, theCode, theDisplay, theSourceConceptPid, theSourceConceptDirectParentPids);
|
||||
public void includeConcept(String theSystem, String theCode, String theDisplay, Long theSourceConceptPid, String theSourceConceptDirectParentPids, String theSystemVersion) {
|
||||
saveConcept(theSystem, theCode, theDisplay, theSourceConceptPid, theSourceConceptDirectParentPids, theSystemVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void includeConceptWithDesignations(String theSystem, String theCode, String theDisplay, Collection<TermConceptDesignation> theDesignations, Long theSourceConceptPid, String theSourceConceptDirectParentPids) {
|
||||
TermValueSetConcept concept = saveConcept(theSystem, theCode, theDisplay, theSourceConceptPid, theSourceConceptDirectParentPids);
|
||||
public void includeConceptWithDesignations(String theSystem, String theCode, String theDisplay, Collection<TermConceptDesignation> theDesignations, Long theSourceConceptPid, String theSourceConceptDirectParentPids, String theSystemVersion) {
|
||||
TermValueSetConcept concept = saveConcept(theSystem, theCode, theDisplay, theSourceConceptPid, theSourceConceptDirectParentPids, theSystemVersion);
|
||||
if (theDesignations != null) {
|
||||
for (TermConceptDesignation designation : theDesignations) {
|
||||
saveConceptDesignation(concept, designation);
|
||||
|
@ -117,7 +117,7 @@ public class ValueSetConceptAccumulator implements IValueSetConceptAccumulator {
|
|||
return false;
|
||||
}
|
||||
|
||||
private TermValueSetConcept saveConcept(String theSystem, String theCode, String theDisplay, Long theSourceConceptPid, String theSourceConceptDirectParentPids) {
|
||||
private TermValueSetConcept saveConcept(String theSystem, String theCode, String theDisplay, Long theSourceConceptPid, String theSourceConceptDirectParentPids, String theSystemVersion) {
|
||||
ValidateUtil.isNotBlankOrThrowInvalidRequest(theSystem, "ValueSet contains a concept with no system value");
|
||||
ValidateUtil.isNotBlankOrThrowInvalidRequest(theCode, "ValueSet contains a concept with no code value");
|
||||
|
||||
|
@ -135,6 +135,7 @@ public class ValueSetConceptAccumulator implements IValueSetConceptAccumulator {
|
|||
if (isNotBlank(theDisplay)) {
|
||||
concept.setDisplay(theDisplay);
|
||||
}
|
||||
concept.setSystemVersion(theSystemVersion);
|
||||
|
||||
concept.setSourceConceptPid(theSourceConceptPid);
|
||||
concept.setSourceConceptDirectParentPids(theSourceConceptDirectParentPids);
|
||||
|
|
|
@ -94,7 +94,7 @@ public class ValueSetExpansionComponentWithConceptAccumulator extends ValueSet.V
|
|||
}
|
||||
|
||||
@Override
|
||||
public void includeConcept(String theSystem, String theCode, String theDisplay, Long theSourceConceptPid, String theSourceConceptDirectParentPids) {
|
||||
public void includeConcept(String theSystem, String theCode, String theDisplay, Long theSourceConceptPid, String theSourceConceptDirectParentPids, String theCodeSystemVersion) {
|
||||
if (mySkipCountRemaining > 0) {
|
||||
mySkipCountRemaining--;
|
||||
return;
|
||||
|
@ -106,10 +106,11 @@ public class ValueSetExpansionComponentWithConceptAccumulator extends ValueSet.V
|
|||
setSystemAndVersion(theSystem, contains);
|
||||
contains.setCode(theCode);
|
||||
contains.setDisplay(theDisplay);
|
||||
contains.setVersion(theCodeSystemVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void includeConceptWithDesignations(String theSystem, String theCode, String theDisplay, Collection<TermConceptDesignation> theDesignations, Long theSourceConceptPid, String theSourceConceptDirectParentPids) {
|
||||
public void includeConceptWithDesignations(String theSystem, String theCode, String theDisplay, Collection<TermConceptDesignation> theDesignations, Long theSourceConceptPid, String theSourceConceptDirectParentPids, String theCodeSystemVersion) {
|
||||
if (mySkipCountRemaining > 0) {
|
||||
mySkipCountRemaining--;
|
||||
return;
|
||||
|
@ -129,6 +130,11 @@ public class ValueSetExpansionComponentWithConceptAccumulator extends ValueSet.V
|
|||
setSystemAndVersion(theSystem, contains);
|
||||
contains.setCode(theCode);
|
||||
contains.setDisplay(theDisplay);
|
||||
|
||||
if (isNotBlank(theCodeSystemVersion)) {
|
||||
contains.setVersion(theCodeSystemVersion);
|
||||
}
|
||||
|
||||
if (theDesignations != null) {
|
||||
for (TermConceptDesignation termConceptDesignation : theDesignations) {
|
||||
contains
|
||||
|
|
|
@ -8,6 +8,7 @@ import ca.uhn.fhir.jpa.entity.TermConcept;
|
|||
import ca.uhn.fhir.jpa.entity.TermValueSet;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.term.IValueSetConceptAccumulator;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.util.FhirVersionIndependentConcept;
|
||||
import org.hl7.fhir.instance.model.api.IBaseCoding;
|
||||
import org.hl7.fhir.instance.model.api.IBaseDatatype;
|
||||
|
@ -116,6 +117,8 @@ public interface ITermReadSvc extends IValidationSupport {
|
|||
*/
|
||||
CodeValidationResult codeSystemValidateCode(IIdType theCodeSystemId, String theValueSetUrl, String theVersion, String theCode, String theDisplay, IBaseDatatype theCoding, IBaseDatatype theCodeableConcept);
|
||||
|
||||
String invalidatePreCalculatedExpansion(IIdType theValueSetId, RequestDetails theRequestDetails);
|
||||
|
||||
/**
|
||||
* Version independent
|
||||
*/
|
||||
|
|
|
@ -72,6 +72,7 @@ public class JpaValidationSupportChain extends ValidationSupportChain {
|
|||
|
||||
@PostConstruct
|
||||
public void postConstruct() {
|
||||
addValidationSupport(myUnknownCodeSystemWarningValidationSupport);
|
||||
addValidationSupport(myDefaultProfileValidationSupport);
|
||||
addValidationSupport(myJpaValidationSupport);
|
||||
//TODO MAKE SURE THAT THIS IS BEING CAL
|
||||
|
@ -81,7 +82,6 @@ public class JpaValidationSupportChain extends ValidationSupportChain {
|
|||
addValidationSupport(myNpmJpaValidationSupport);
|
||||
addValidationSupport(new CommonCodeSystemsTerminologyService(myFhirContext));
|
||||
addValidationSupport(myConceptMappingSvc);
|
||||
addValidationSupport(myUnknownCodeSystemWarningValidationSupport);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -91,6 +91,7 @@ public class TransactionProcessorTest {
|
|||
when(myEntityManager.unwrap(eq(Session.class))).thenReturn(mySession);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testTransactionWithDisabledResourceType() {
|
||||
|
||||
|
|
|
@ -78,8 +78,7 @@ public class FhirResourceDaoValueSetDstu2Test extends BaseJpaDstu2Test {
|
|||
CodingDt coding = null;
|
||||
CodeableConceptDt codeableConcept = null;
|
||||
IValidationSupport.CodeValidationResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd);
|
||||
//TODO JA, from what I read, this _should_ pass, but this was flipped to false in a previous commit.
|
||||
assertTrue(result.isOk());
|
||||
assertFalse(result.isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -107,8 +106,8 @@ public class FhirResourceDaoValueSetDstu2Test extends BaseJpaDstu2Test {
|
|||
CodeableConceptDt codeableConcept = null;
|
||||
IValidationSupport.CodeValidationResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd);
|
||||
assertFalse(result.isOk());
|
||||
assertEquals("Concept Display \"Systolic blood pressure at First encounterXXXX\" does not match expected \"Systolic blood pressure at First encounter\" for in-memory expansion of ValueSet: http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", result.getMessage());
|
||||
assertEquals("Systolic blood pressure at First encounter", result.getDisplay());
|
||||
assertEquals("Concept Display \"Systolic blood pressure at First encounterXXXX\" does not match expected \"Systolic blood pressure at First encounter\"", result.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -550,23 +550,6 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
|
|||
assertEquals(0, expansion.getExpansion().getContains().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExpandWithNoResultsInLocalValueSet2() {
|
||||
createLocalCsAndVs();
|
||||
|
||||
ValueSet vs = new ValueSet();
|
||||
ConceptSetComponent include = vs.getCompose().addInclude();
|
||||
include.setSystem(URL_MY_CODE_SYSTEM + "AA");
|
||||
include.addConcept().setCode("A");
|
||||
|
||||
try {
|
||||
myValueSetDao.expand(vs, null);
|
||||
fail();
|
||||
} catch (PreconditionFailedException e) {
|
||||
assertEquals("Unknown CodeSystem URI \"http://example.com/my_code_systemAA\" referenced from ValueSet", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExpandWithSystemAndCodesInExternalValueSet() {
|
||||
createExternalCsAndLocalVs();
|
||||
|
|
|
@ -1510,20 +1510,18 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
id2 = myOrganizationDao.create(patient, mySrd).getId().toUnqualifiedVersionless().getValue();
|
||||
}
|
||||
|
||||
// FIXME: restore
|
||||
|
||||
int size;
|
||||
SearchParameterMap params = new SearchParameterMap();
|
||||
// params.setLoadSynchronous(true);
|
||||
// assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(params)), contains(id1));
|
||||
//
|
||||
// params = new SearchParameterMap();
|
||||
// params.add("_id", new StringParam(id1));
|
||||
// assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(params)), contains(id1));
|
||||
//
|
||||
// params = new SearchParameterMap();
|
||||
// params.add("_id", new StringParam("9999999999999999"));
|
||||
// assertEquals(0, toList(myPatientDao.search(params)).size());
|
||||
params.setLoadSynchronous(true);
|
||||
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(params)), contains(id1));
|
||||
|
||||
params = new SearchParameterMap();
|
||||
params.add("_id", new StringParam(id1));
|
||||
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(params)), contains(id1));
|
||||
|
||||
params = new SearchParameterMap();
|
||||
params.add("_id", new StringParam("9999999999999999"));
|
||||
assertEquals(0, toList(myPatientDao.search(params)).size());
|
||||
|
||||
myCaptureQueriesListener.clear();
|
||||
params = new SearchParameterMap();
|
||||
|
|
|
@ -14,7 +14,7 @@ import ca.uhn.fhir.jpa.dao.BaseJpaTest;
|
|||
import ca.uhn.fhir.jpa.dao.dstu2.FhirResourceDaoDstu2SearchNoFtTest;
|
||||
import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.jpa.term.ValueSetExpansionR4Test;
|
||||
import ca.uhn.fhir.jpa.util.ValueSetTestUtil;
|
||||
import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
|
||||
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
|
||||
|
@ -300,7 +300,7 @@ public class FhirResourceDaoR4SearchWithLuceneDisabledTest extends BaseJpaTest {
|
|||
// Non Pre-Expanded
|
||||
ValueSet outcome = myValueSetDao.expand(vs, new ValueSetExpansionOptions());
|
||||
assertEquals("ValueSet \"ValueSet.url[http://vs]\" has not yet been pre-expanded. Performing in-memory expansion without parameters. Current status: NOT_EXPANDED | The ValueSet is waiting to be picked up and pre-expanded by a scheduled task.", outcome.getMeta().getExtensionString(EXT_VALUESET_EXPANSION_MESSAGE));
|
||||
assertThat(ValueSetExpansionR4Test.toCodes(outcome).toString(), ValueSetExpansionR4Test.toCodes(outcome), contains(
|
||||
assertThat(ValueSetTestUtil.toCodes(outcome).toString(), ValueSetTestUtil.toCodes(outcome), contains(
|
||||
"code5", "code4", "code3", "code2", "code1"
|
||||
));
|
||||
|
||||
|
|
|
@ -655,23 +655,6 @@ public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExpandWithNoResultsInLocalValueSet2() {
|
||||
createLocalCsAndVs();
|
||||
|
||||
ValueSet vs = new ValueSet();
|
||||
ConceptSetComponent include = vs.getCompose().addInclude();
|
||||
include.setSystem(URL_MY_CODE_SYSTEM + "AA");
|
||||
include.addConcept().setCode("A");
|
||||
|
||||
try {
|
||||
myValueSetDao.expand(vs, null);
|
||||
fail();
|
||||
} catch (PreconditionFailedException e) {
|
||||
assertEquals("Unknown CodeSystem URI \"http://example.com/my_code_systemAA\" referenced from ValueSet", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: get this working
|
||||
@Disabled
|
||||
@Test
|
||||
|
|
|
@ -154,7 +154,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
|||
encoded = encode(oo);
|
||||
ourLog.info(encoded);
|
||||
assertEquals(1, oo.getIssue().size(), encoded);
|
||||
assertEquals("The code provided (http://cs#code99) is not in the value set http://vs, and a code from this value set is required: Unknown code system: http://cs", oo.getIssueFirstRep().getDiagnostics(), encoded);
|
||||
assertEquals("The code provided (http://cs#code99) is not in the value set http://vs, and a code from this value set is required: Unknown code 'http://cs#code99' for in-memory expansion of ValueSet 'http://vs'", oo.getIssueFirstRep().getDiagnostics(), encoded);
|
||||
assertEquals(OperationOutcome.IssueSeverity.ERROR, oo.getIssueFirstRep().getSeverity(), encoded);
|
||||
|
||||
}
|
||||
|
@ -194,9 +194,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
|||
oo = validateAndReturnOutcome(obs);
|
||||
encoded = encode(oo);
|
||||
ourLog.info(encoded);
|
||||
assertEquals(1, oo.getIssue().size(), encoded);
|
||||
assertEquals("Error Unknown code system: http://cs validating Coding", oo.getIssueFirstRep().getDiagnostics(), encoded);
|
||||
assertEquals(OperationOutcome.IssueSeverity.WARNING, oo.getIssueFirstRep().getSeverity(), encoded);
|
||||
assertEquals("No issues detected during validation", oo.getIssueFirstRep().getDiagnostics(), encoded);
|
||||
|
||||
}
|
||||
|
||||
|
@ -463,7 +461,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
|||
obs.getCode().getCodingFirstRep().setSystem("http://loinc.org").setCode("CODE3").setDisplay("Display 3");
|
||||
obs.getCategoryFirstRep().addCoding().setSystem("http://terminology.hl7.org/CodeSystem/observation-category").setCode("FOO");
|
||||
oo = validateAndReturnOutcome(obs);
|
||||
assertEquals("Unknown code 'http://terminology.hl7.org/CodeSystem/observation-category#FOO'", oo.getIssueFirstRep().getDiagnostics(), encode(oo));
|
||||
assertEquals("Unknown code 'http://terminology.hl7.org/CodeSystem/observation-category#FOO' for in-memory expansion of ValueSet 'http://hl7.org/fhir/ValueSet/observation-category'", oo.getIssueFirstRep().getDiagnostics(), encode(oo));
|
||||
|
||||
// Make sure we're caching the validations as opposed to hitting the DB every time
|
||||
myCaptureQueriesListener.clear();
|
||||
|
@ -792,7 +790,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
|||
obs.getCode().getCodingFirstRep().setDisplay("Some Code");
|
||||
outcome = (OperationOutcome) myObservationDao.validate(obs, null, null, null, ValidationModeEnum.CREATE, "http://example.com/structuredefinition", mySrd).getOperationOutcome();
|
||||
ourLog.info("Outcome: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome));
|
||||
assertEquals("Unknown code in fragment CodeSystem 'http://example.com/codesystem#foo-foo'", outcome.getIssueFirstRep().getDiagnostics());
|
||||
assertEquals("Unknown code in fragment CodeSystem 'http://example.com/codesystem#foo-foo' for in-memory expansion of ValueSet 'http://example.com/valueset'", outcome.getIssueFirstRep().getDiagnostics());
|
||||
assertEquals(OperationOutcome.IssueSeverity.WARNING, outcome.getIssueFirstRep().getSeverity());
|
||||
|
||||
// Correct codesystem, Code in codesystem
|
||||
|
@ -901,7 +899,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
|||
obs.getCode().getCodingFirstRep().setSystem("http://loinc.org").setCode("CODE3").setDisplay("Display 3");
|
||||
obs.getCategoryFirstRep().addCoding().setSystem("http://terminology.hl7.org/CodeSystem/observation-category").setCode("FOO");
|
||||
oo = validateAndReturnOutcome(obs);
|
||||
assertEquals("Unknown code 'http://terminology.hl7.org/CodeSystem/observation-category#FOO'", oo.getIssueFirstRep().getDiagnostics(), encode(oo));
|
||||
assertEquals("Unknown code 'http://terminology.hl7.org/CodeSystem/observation-category#FOO' for in-memory expansion of ValueSet 'http://hl7.org/fhir/ValueSet/observation-category'", oo.getIssueFirstRep().getDiagnostics(), encode(oo));
|
||||
|
||||
}
|
||||
|
||||
|
@ -1508,7 +1506,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
|||
OperationOutcome oo = (OperationOutcome) e.getOperationOutcome();
|
||||
String encoded = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo);
|
||||
ourLog.info("Outcome:\n{}", encoded);
|
||||
assertThat(encoded, containsString("Unknown code {urn:oid:2.16.840.1.113883.6.238}2106-3AAA"));
|
||||
assertThat(encoded, containsString("Unable to validate code urn:oid:2.16.840.1.113883.6.238#2106-3AAA"));
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -1720,7 +1718,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
|||
IValidationSupport.CodeValidationResult result = myValueSetDao.validateCode(new UriType("http://fooVs"), null, new StringType("10013-1"), new StringType(ITermLoaderSvc.LOINC_URI), null, null, null, mySrd);
|
||||
|
||||
assertFalse(result.isOk());
|
||||
assertEquals("Unknown code {http://loinc.org}10013-1 - Unable to locate ValueSet[http://fooVs]", result.getMessage());
|
||||
assertEquals("Unable to validate code http://loinc.org#10013-1 - Unable to locate ValueSet[http://fooVs]", result.getMessage());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -4,7 +4,9 @@ import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome;
|
|||
import ca.uhn.fhir.jpa.dao.data.ITermValueSetConceptDao;
|
||||
import ca.uhn.fhir.jpa.entity.TermValueSet;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.util.ValueSetTestUtil;
|
||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.hl7.fhir.r4.model.CodeSystem;
|
||||
import org.hl7.fhir.r4.model.ValueSet;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -17,6 +19,7 @@ import java.util.Map;
|
|||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
@ -233,12 +236,9 @@ public class FhirResourceDaoR4ValueSetMultiVersionTest extends BaseJpaR4Test {
|
|||
include.setVersion("1");
|
||||
include.addConcept().setCode("A");
|
||||
|
||||
try {
|
||||
myValueSetDao.expand(vs, null);
|
||||
fail();
|
||||
} catch (PreconditionFailedException e) {
|
||||
assertEquals("Unknown CodeSystem URI \"http://example.com/my_code_systemAA\" referenced from ValueSet", e.getMessage());
|
||||
}
|
||||
ValueSet expansion = myValueSetDao.expand(vs, null);
|
||||
assertThat(ValueSetTestUtil.toCodes(expansion), Matchers.contains("A"));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package ca.uhn.fhir.jpa.dao.r4;
|
||||
|
||||
import ca.uhn.fhir.context.support.ConceptValidationOptions;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
||||
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.term.custom.CustomTerminologySet;
|
||||
|
@ -12,6 +14,7 @@ import org.hl7.fhir.r4.model.CodeSystem;
|
|||
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.Enumerations;
|
||||
import org.hl7.fhir.r4.model.IdType;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
import org.hl7.fhir.r4.model.UriType;
|
||||
|
@ -98,7 +101,7 @@ public class FhirResourceDaoR4ValueSetTest extends BaseJpaR4Test {
|
|||
IValidationSupport.CodeValidationResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd);
|
||||
assertFalse(result.isOk());
|
||||
assertEquals("Systolic blood pressure at First encounter", result.getDisplay());
|
||||
assertEquals("Concept Display \"Systolic blood pressure at First encounterXXXX\" does not match expected \"Systolic blood pressure at First encounter\"", result.getMessage());
|
||||
assertEquals("Concept Display \"Systolic blood pressure at First encounterXXXX\" does not match expected \"Systolic blood pressure at First encounter\" for in-memory expansion of ValueSet: http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", result.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -273,6 +276,47 @@ public class FhirResourceDaoR4ValueSetTest extends BaseJpaR4Test {
|
|||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testExpandValueSet_VsUsesVersionedSystem_CsIsFragmentWithoutCode() {
|
||||
CodeSystem cs = new CodeSystem();
|
||||
cs.setId("snomed-ct-ca-imm");
|
||||
cs.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
cs.setContent(CodeSystem.CodeSystemContentMode.FRAGMENT);
|
||||
cs.setUrl("http://snomed.info/sct");
|
||||
cs.setVersion("0.1.17");
|
||||
myCodeSystemDao.update(cs);
|
||||
|
||||
ValueSet vs = new ValueSet();
|
||||
vs.setId("vaccinecode");
|
||||
vs.setUrl("http://ehealthontario.ca/fhir/ValueSet/vaccinecode");
|
||||
vs.setVersion("0.1.17");
|
||||
vs.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
ValueSet.ConceptSetComponent vsInclude = vs.getCompose().addInclude();
|
||||
vsInclude.setSystem("http://snomed.info/sct");
|
||||
vsInclude.setVersion("http://snomed.info/sct/20611000087101/version/20210331");
|
||||
vsInclude.addConcept().setCode("28571000087109").setDisplay("MODERNA COVID-19 mRNA-1273");
|
||||
myValueSetDao.update(vs);
|
||||
|
||||
|
||||
IValidationSupport.CodeValidationResult outcome = myValueSetDao.validateCode(null, new IdType("ValueSet/vaccinecode"), new CodeType("28571000087109"), new CodeType("http://snomed.info/sct"), null, null, null, mySrd);
|
||||
assertTrue(outcome.isOk());
|
||||
outcome = myTermSvc.validateCodeInValueSet(
|
||||
new ValidationSupportContext(myValidationSupport),
|
||||
new ConceptValidationOptions(),
|
||||
"http://snomed.info/sct",
|
||||
"28571000087109",
|
||||
"MODERNA COVID-19 mRNA-1273",
|
||||
vs
|
||||
);
|
||||
assertTrue(outcome.isOk());
|
||||
|
||||
ValueSet expansion = myValueSetDao.expand(new IdType("ValueSet/vaccinecode"), new ValueSetExpansionOptions(), mySrd);
|
||||
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expansion));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -103,7 +103,7 @@ public class FhirResourceDaoR5ValueSetTest extends BaseJpaR5Test {
|
|||
IValidationSupport.CodeValidationResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd);
|
||||
assertFalse(result.isOk());
|
||||
assertEquals("Systolic blood pressure at First encounter", result.getDisplay());
|
||||
assertEquals("Concept Display \"Systolic blood pressure at First encounterXXXX\" does not match expected \"Systolic blood pressure at First encounter\"", result.getMessage());
|
||||
assertEquals("Concept Display \"Systolic blood pressure at First encounterXXXX\" does not match expected \"Systolic blood pressure at First encounter\" for in-memory expansion of ValueSet: http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", result.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -6,6 +6,7 @@ import ca.uhn.fhir.jpa.dao.dstu3.BaseJpaDstu3Test;
|
|||
import ca.uhn.fhir.jpa.provider.GraphQLProvider;
|
||||
import ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider;
|
||||
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
|
||||
import ca.uhn.fhir.jpa.provider.ValueSetOperationProvider;
|
||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||
import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryImpl;
|
||||
import ca.uhn.fhir.jpa.subscription.match.config.WebsocketDispatcherConfig;
|
||||
|
@ -96,6 +97,7 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test {
|
|||
ourRestServer.registerProvider(subscriptionTriggeringProvider);
|
||||
|
||||
ourRestServer.registerProvider(myAppCtx.getBean(GraphQLProvider.class));
|
||||
ourRestServer.registerProvider(myAppCtx.getBean(ValueSetOperationProvider.class));
|
||||
|
||||
JpaConformanceProviderDstu3 confProvider = new JpaConformanceProviderDstu3(ourRestServer, mySystemDao, myDaoConfig, ourSearchParamRegistry);
|
||||
confProvider.setImplementationDescription("THIS IS THE DESC");
|
||||
|
|
|
@ -373,7 +373,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
|
|||
.operation()
|
||||
.onType(ValueSet.class)
|
||||
.named("expand")
|
||||
.withParameter(Parameters.class, "identifier", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2"))
|
||||
.withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2"))
|
||||
.execute();
|
||||
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
|
||||
|
||||
|
@ -564,7 +564,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
|
|||
.execute();
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("HTTP 400 Bad Request: $expand operation at the type level (no ID specified) requires an identifier or a valueSet as a part of the request.", e.getMessage());
|
||||
assertEquals("HTTP 400 Bad Request: $expand operation at the type level (no ID specified) requires a url or a valueSet as a part of the request.", e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -574,11 +574,11 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
|
|||
.onType(ValueSet.class)
|
||||
.named("expand")
|
||||
.withParameter(Parameters.class, "valueSet", toExpand)
|
||||
.andParameter("identifier", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2"))
|
||||
.andParameter("url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2"))
|
||||
.execute();
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("HTTP 400 Bad Request: $expand must EITHER be invoked at the instance level, or have an identifier specified, or have a ValueSet specified. Can not combine these options.", e.getMessage());
|
||||
assertEquals("HTTP 400 Bad Request: $expand must EITHER be invoked at the instance level, or have a url specified, or have a ValueSet specified. Can not combine these options.", e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -588,11 +588,11 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
|
|||
.onInstance(myExtensionalVsId)
|
||||
.named("expand")
|
||||
.withParameter(Parameters.class, "valueSet", toExpand)
|
||||
.andParameter("identifier", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2"))
|
||||
.andParameter("url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2"))
|
||||
.execute();
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("HTTP 400 Bad Request: $expand must EITHER be invoked at the instance level, or have an identifier specified, or have a ValueSet specified. Can not combine these options.", e.getMessage());
|
||||
assertEquals("HTTP 400 Bad Request: $expand must EITHER be invoked at the instance level, or have a url specified, or have a ValueSet specified. Can not combine these options.", e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -670,7 +670,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
|
|||
.operation()
|
||||
.onType(ValueSet.class)
|
||||
.named("expand")
|
||||
.withParameter(Parameters.class, "identifier", new UriType(URL_MY_VALUE_SET))
|
||||
.withParameter(Parameters.class, "url", new UriType(URL_MY_VALUE_SET))
|
||||
.execute();
|
||||
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
|
||||
|
||||
|
@ -796,7 +796,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
|
|||
.onType(ValueSet.class)
|
||||
.named("validate-code")
|
||||
.withParameter(Parameters.class, "code", new StringType("male"))
|
||||
.andParameter("identifier", new StringType("http://hl7.org/fhir/ValueSet/administrative-gender"))
|
||||
.andParameter("url", new StringType("http://hl7.org/fhir/ValueSet/administrative-gender"))
|
||||
.andParameter("system", new StringType("http://hl7.org/fhir/administrative-gender"))
|
||||
.useHttpGet()
|
||||
.execute();
|
||||
|
@ -807,8 +807,11 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
|
|||
assertEquals("result", respParam.getParameter().get(0).getName());
|
||||
assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
|
||||
assertEquals("display", respParam.getParameter().get(1).getName());
|
||||
assertEquals("Male", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
|
||||
assertEquals("message", respParam.getParameter().get(1).getName());
|
||||
assertEquals("Code was validated against in-memory expansion of ValueSet: http://hl7.org/fhir/ValueSet/administrative-gender", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
|
||||
|
||||
assertEquals("display", respParam.getParameter().get(2).getName());
|
||||
assertEquals("Male", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -834,8 +837,11 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
|
|||
assertEquals("result", respParam.getParameter().get(0).getName());
|
||||
assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
|
||||
assertEquals("display", respParam.getParameter().get(1).getName());
|
||||
assertEquals("Male", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
|
||||
assertEquals("message", respParam.getParameter().get(1).getName());
|
||||
assertEquals("Code was validated against in-memory expansion of ValueSet: http://hl7.org/fhir/ValueSet/administrative-gender", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
|
||||
|
||||
assertEquals("display", respParam.getParameter().get(2).getName());
|
||||
assertEquals("Male", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -9,6 +9,7 @@ import ca.uhn.fhir.jpa.provider.DiffProvider;
|
|||
import ca.uhn.fhir.jpa.provider.GraphQLProvider;
|
||||
import ca.uhn.fhir.jpa.provider.JpaCapabilityStatementProvider;
|
||||
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
|
||||
import ca.uhn.fhir.jpa.provider.ValueSetOperationProvider;
|
||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||
import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryImpl;
|
||||
import ca.uhn.fhir.jpa.subscription.match.config.WebsocketDispatcherConfig;
|
||||
|
@ -115,6 +116,7 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
|
|||
ourRestServer.registerProviders(mySystemProvider, myTerminologyUploaderProvider, myDeleteExpungeProvider, myReindexProvider);
|
||||
ourRestServer.registerProvider(myAppCtx.getBean(GraphQLProvider.class));
|
||||
ourRestServer.registerProvider(myAppCtx.getBean(DiffProvider.class));
|
||||
ourRestServer.registerProvider(myAppCtx.getBean(ValueSetOperationProvider.class));
|
||||
|
||||
ourPagingProvider = myAppCtx.getBean(DatabaseBackedPagingProvider.class);
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.provider.r4;
|
|||
import ca.uhn.fhir.interceptor.api.Hook;
|
||||
import ca.uhn.fhir.interceptor.api.Interceptor;
|
||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||
import ca.uhn.fhir.jpa.config.TestR4Config;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
@ -73,6 +74,11 @@ public class ResourceProviderConcurrencyR4Test extends BaseResourceProviderR4Tes
|
|||
*/
|
||||
@Test
|
||||
public void testSearchesExecuteConcurrently() {
|
||||
if (TestR4Config.getMaxThreads() == 1) {
|
||||
ourLog.info("Skipping this test because the test thread pool only has one max connection");
|
||||
return;
|
||||
}
|
||||
|
||||
createPatient(withFamily("FAMILY1"));
|
||||
createPatient(withFamily("FAMILY2"));
|
||||
createPatient(withFamily("FAMILY3"));
|
||||
|
|
|
@ -482,7 +482,7 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test
|
|||
ourLog.info(resp);
|
||||
|
||||
assertFalse(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue());
|
||||
assertEquals("Unknown code {http://acme.org}8452-5-a", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString());
|
||||
assertEquals("Unable to validate code http://acme.org#8452-5-a - Code is not found in CodeSystem: http://acme.org", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString());
|
||||
|
||||
}
|
||||
|
||||
|
@ -518,7 +518,7 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test
|
|||
ourLog.info(resp);
|
||||
|
||||
assertFalse(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue());
|
||||
assertEquals("Unknown code {http://acme.org}8452-5 - Concept Display : Old Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString());
|
||||
assertEquals("Unable to validate code http://acme.org#8452-5Concept Display \"Old Systolic blood pressure.inspiration - expiration\" does not match expected \"Systolic blood pressure.inspiration - expiration\" for CodeSystem: http://acme.org", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -675,7 +675,7 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test
|
|||
ourLog.info(resp);
|
||||
|
||||
assertFalse(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue());
|
||||
assertEquals("Unknown code {http://acme.org}8452-5-a", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString());
|
||||
assertEquals("Unable to validate code http://acme.org#8452-5-a - Code is not found in CodeSystem: http://acme.org", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString());
|
||||
|
||||
}
|
||||
|
||||
|
@ -755,7 +755,7 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test
|
|||
ourLog.info(resp);
|
||||
|
||||
assertFalse(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue());
|
||||
assertEquals("Unknown code {http://acme.org}8452-5-a", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString());
|
||||
assertEquals("Unable to validate code http://acme.org#8452-5-a - Code is not found in CodeSystem: http://acme.org", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -6,6 +6,7 @@ import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoValueSet;
|
|||
import ca.uhn.fhir.jpa.config.TestR4WithLuceneDisabledConfig;
|
||||
import ca.uhn.fhir.jpa.dao.BaseJpaTest;
|
||||
import ca.uhn.fhir.jpa.dao.dstu2.FhirResourceDaoDstu2SearchNoFtTest;
|
||||
import ca.uhn.fhir.jpa.provider.ValueSetOperationProvider;
|
||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||
import ca.uhn.fhir.jpa.subscription.match.config.WebsocketDispatcherConfig;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
|
@ -190,6 +191,8 @@ public class ResourceProviderR4ValueSetLuceneDisabledTest extends BaseJpaTest {
|
|||
|
||||
ourPagingProvider = myAppCtx.getBean(DatabaseBackedPagingProvider.class);
|
||||
|
||||
ourRestServer.registerProvider(myAppCtx.getBean(ValueSetOperationProvider.class));
|
||||
|
||||
Server server = new Server(0);
|
||||
|
||||
ServletContextHandler proxyHandler = new ServletContextHandler();
|
||||
|
|
|
@ -15,6 +15,7 @@ import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
|
|||
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
import com.google.common.base.Charsets;
|
||||
|
@ -119,7 +120,7 @@ public class ResourceProviderR4ValueSetNoVerCSNoVerTest extends BaseResourceProv
|
|||
private void loadAndPersistValueSet() throws IOException {
|
||||
ValueSet valueSet = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml");
|
||||
valueSet.setId("ValueSet/vs");
|
||||
persistValueSet(valueSet, HttpVerb.POST);
|
||||
persistValueSet(valueSet, HttpVerb.PUT);
|
||||
}
|
||||
|
||||
@SuppressWarnings("EnumSwitchStatementWhichMissesCases")
|
||||
|
@ -1104,7 +1105,7 @@ public class ResourceProviderR4ValueSetNoVerCSNoVerTest extends BaseResourceProv
|
|||
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expansion));
|
||||
assertThat(toDirectCodes(expansion.getExpansion().getContains()), containsInAnyOrder("A", "AA", "AB", "AAA"));
|
||||
assertEquals(15, myCaptureQueriesListener.getSelectQueries().size());
|
||||
assertEquals(null, expansion.getMeta().getExtensionString(EXT_VALUESET_EXPANSION_MESSAGE));
|
||||
assertEquals("ValueSet with URL \"Unidentified ValueSet\" was expanded using an in-memory expansion", expansion.getMeta().getExtensionString(EXT_VALUESET_EXPANSION_MESSAGE));
|
||||
|
||||
// Hierarchical
|
||||
myCaptureQueriesListener.clear();
|
||||
|
@ -1156,7 +1157,7 @@ public class ResourceProviderR4ValueSetNoVerCSNoVerTest extends BaseResourceProv
|
|||
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expansion));
|
||||
assertThat(toDirectCodes(expansion.getExpansion().getContains()), containsInAnyOrder("A", "AA", "AB", "AAA"));
|
||||
assertEquals(3, myCaptureQueriesListener.getSelectQueries().size());
|
||||
assertEquals("ValueSet was expanded using a pre-calculated expansion", expansion.getMeta().getExtensionString(EXT_VALUESET_EXPANSION_MESSAGE));
|
||||
assertThat(expansion.getMeta().getExtensionString(EXT_VALUESET_EXPANSION_MESSAGE), containsString("ValueSet was expanded using an expansion that was pre-calculated"));
|
||||
|
||||
// Hierarchical
|
||||
myCaptureQueriesListener.clear();
|
||||
|
@ -1284,8 +1285,11 @@ public class ResourceProviderR4ValueSetNoVerCSNoVerTest extends BaseResourceProv
|
|||
assertEquals("result", respParam.getParameter().get(0).getName());
|
||||
assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
|
||||
assertEquals("display", respParam.getParameter().get(1).getName());
|
||||
assertEquals("Male", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
|
||||
assertEquals("message", respParam.getParameter().get(1).getName());
|
||||
assertEquals("Code was validated against in-memory expansion of ValueSet: http://hl7.org/fhir/ValueSet/administrative-gender", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
|
||||
|
||||
assertEquals("display", respParam.getParameter().get(2).getName());
|
||||
assertEquals("Male", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1370,6 +1374,48 @@ public class ResourceProviderR4ValueSetNoVerCSNoVerTest extends BaseResourceProv
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidatePrecalculatedExpansion() throws IOException {
|
||||
loadAndPersistCodeSystemAndValueSet();
|
||||
myTerminologyDeferredStorageSvc.saveAllDeferred();
|
||||
myTermSvc.preExpandDeferredValueSetsToTerminologyTables();
|
||||
|
||||
assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, runInTransaction(()->myTermValueSetDao.findTermValueSetByUrlAndNullVersion("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2").orElseThrow(()->new IllegalStateException()).getExpansionStatus()));
|
||||
|
||||
Parameters outcome = myClient
|
||||
.operation()
|
||||
.onInstance("ValueSet/vs")
|
||||
.named(ProviderConstants.OPERATION_INVALIDATE_EXPANSION)
|
||||
.withNoParameters(Parameters.class)
|
||||
.execute();
|
||||
assertEquals("ValueSet with URL \"http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2\" precaluclated expansion with 24 concept(s) has been invalidated", outcome.getParameter("message").primitiveValue());
|
||||
|
||||
assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, runInTransaction(()->myTermValueSetDao.findTermValueSetByUrlAndNullVersion("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2").orElseThrow(()->new IllegalStateException()).getExpansionStatus()));
|
||||
|
||||
outcome = myClient
|
||||
.operation()
|
||||
.onInstance("ValueSet/vs")
|
||||
.named(ProviderConstants.OPERATION_INVALIDATE_EXPANSION)
|
||||
.withNoParameters(Parameters.class)
|
||||
.execute();
|
||||
assertEquals("ValueSet with URL \"http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2\" already has status: NOT_EXPANDED", outcome.getParameter("message").primitiveValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidatePrecalculatedExpansion_NonExistent() {
|
||||
try {
|
||||
myClient
|
||||
.operation()
|
||||
.onInstance("ValueSet/FOO")
|
||||
.named(ProviderConstants.OPERATION_INVALIDATE_EXPANSION)
|
||||
.withNoParameters(Parameters.class)
|
||||
.execute();
|
||||
fail();
|
||||
} catch (ResourceNotFoundException e) {
|
||||
assertEquals("HTTP 404 Not Found: Resource ValueSet/FOO is not known", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExpandByValueSetWithFilterContainsPrefixValue() throws IOException {
|
||||
loadAndPersistCodeSystem();
|
||||
|
|
|
@ -7,6 +7,7 @@ import ca.uhn.fhir.jpa.dao.r5.BaseJpaR5Test;
|
|||
import ca.uhn.fhir.jpa.provider.GraphQLProvider;
|
||||
import ca.uhn.fhir.jpa.provider.JpaCapabilityStatementProvider;
|
||||
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
|
||||
import ca.uhn.fhir.jpa.provider.ValueSetOperationProvider;
|
||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||
import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryImpl;
|
||||
import ca.uhn.fhir.jpa.subscription.match.config.WebsocketDispatcherConfig;
|
||||
|
@ -108,6 +109,8 @@ public abstract class BaseResourceProviderR5Test extends BaseJpaR5Test {
|
|||
myDaoRegistry = myAppCtx.getBean(DaoRegistry.class);
|
||||
ourRestServer.registerProviders(mySystemProvider, myTerminologyUploaderProvider);
|
||||
|
||||
ourRestServer.registerProvider(myAppCtx.getBean(ValueSetOperationProvider.class));
|
||||
|
||||
ourRestServer.registerProvider(myAppCtx.getBean(GraphQLProvider.class));
|
||||
IValidationSupport validationSupport = myAppCtx.getBean(IValidationSupport.class);
|
||||
|
||||
|
|
|
@ -1248,8 +1248,11 @@ public class ResourceProviderR5ValueSetTest extends BaseResourceProviderR5Test {
|
|||
assertEquals("result", respParam.getParameter().get(0).getName());
|
||||
assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
|
||||
assertEquals("display", respParam.getParameter().get(1).getName());
|
||||
assertEquals("Male", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
|
||||
assertEquals("message", respParam.getParameter().get(1).getName());
|
||||
assertEquals("Code was validated against in-memory expansion of ValueSet: http://hl7.org/fhir/ValueSet/administrative-gender", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
|
||||
|
||||
assertEquals("display", respParam.getParameter().get(2).getName());
|
||||
assertEquals("Male", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
|
||||
}
|
||||
|
||||
// Good code and system, but not in specified valueset
|
||||
|
@ -1271,7 +1274,7 @@ public class ResourceProviderR5ValueSetTest extends BaseResourceProviderR5Test {
|
|||
assertEquals(false, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
|
||||
assertEquals("message", respParam.getParameter().get(1).getName());
|
||||
assertEquals("Unknown code 'http://hl7.org/fhir/administrative-gender#male'", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
|
||||
assertEquals("Unknown code 'http://hl7.org/fhir/administrative-gender#male' for in-memory expansion of ValueSet 'http://hl7.org/fhir/ValueSet/marital-status'", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -245,8 +245,8 @@ public class TerminologyLoaderSvcIntegrationDstu3Test extends BaseJpaDstu3Test {
|
|||
myTerminologyDeferredStorageSvc.saveDeferred();
|
||||
|
||||
IValidationSupport.CodeValidationResult result = myValueSetDao.validateCode(new UriType("http://loinc.org/vs"), null, new StringType("10013-1-9999999999"), new StringType(ITermLoaderSvc.LOINC_URI), null, null, null, mySrd);
|
||||
|
||||
assertNull(result);
|
||||
assertFalse(result.isOk());
|
||||
assertEquals("Failed to expand ValueSet 'http://loinc.org/vs' (in-memory). Could not validate code http://loinc.org#10013-1-9999999999. Error was: Unable to expand ValueSet because CodeSystem could not be found: http://loinc.org|1.0.0", result.getMessage());
|
||||
}
|
||||
|
||||
private Set<String> toExpandedCodes(ValueSet theExpanded) {
|
||||
|
|
|
@ -42,7 +42,7 @@ public class ValueSetConceptAccumulatorTest {
|
|||
@Test
|
||||
public void testIncludeConcept() {
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
myAccumulator.includeConcept("sys", "code", "display", null, null);
|
||||
myAccumulator.includeConcept("sys", "code", "display", null, null, null);
|
||||
}
|
||||
verify(myValueSetConceptDao, times(1000)).save(any());
|
||||
}
|
||||
|
|
|
@ -206,7 +206,7 @@ public class ValueSetExpansionR4ElasticsearchIT extends BaseJpaTest {
|
|||
include.setSystem(CS_URL);
|
||||
|
||||
myTermSvc.expandValueSet(null, vs, myValueSetCodeAccumulator);
|
||||
verify(myValueSetCodeAccumulator, times(9)).includeConceptWithDesignations(anyString(), anyString(), nullable(String.class), anyCollection(), nullable(Long.class), nullable(String.class));
|
||||
verify(myValueSetCodeAccumulator, times(9)).includeConceptWithDesignations(anyString(), anyString(), nullable(String.class), anyCollection(), nullable(Long.class), nullable(String.class), nullable(String.class));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package ca.uhn.fhir.jpa.term;
|
||||
|
||||
import ca.uhn.fhir.context.support.ConceptValidationOptions;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
|
||||
import ca.uhn.fhir.jpa.entity.TermCodeSystem;
|
||||
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
|
||||
|
@ -13,14 +15,17 @@ import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
|||
import ca.uhn.fhir.jpa.search.builder.SearchBuilder;
|
||||
import ca.uhn.fhir.jpa.term.custom.CustomTerminologySet;
|
||||
import ca.uhn.fhir.jpa.util.SqlQuery;
|
||||
import ca.uhn.fhir.jpa.util.ValueSetTestUtil;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.util.HapiExtensions;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.r4.model.CodeSystem;
|
||||
import org.hl7.fhir.r4.model.CodeType;
|
||||
import org.hl7.fhir.r4.model.Extension;
|
||||
import org.hl7.fhir.r4.model.Enumerations;
|
||||
import org.hl7.fhir.r4.model.IdType;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
import org.hl7.fhir.r4.model.UriType;
|
||||
import org.hl7.fhir.r4.model.ValueSet;
|
||||
import org.hl7.fhir.r4.model.codesystems.HttpVerb;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
|
@ -47,8 +52,10 @@ import static org.hamcrest.MatcherAssert.assertThat;
|
|||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.hamcrest.Matchers.empty;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
@ -90,15 +97,11 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test {
|
|||
ValueSet expandedValueSet = myTermSvc.expandValueSet(null, valueSet);
|
||||
assertEquals(24, expandedValueSet.getExpansion().getContains().size());
|
||||
|
||||
runInTransaction(() -> {
|
||||
assertEquals(24, myTermValueSetConceptDao.count());
|
||||
});
|
||||
runInTransaction(() -> assertEquals(24, myTermValueSetConceptDao.count()));
|
||||
|
||||
myValueSetDao.delete(valueSet.getIdElement());
|
||||
|
||||
runInTransaction(() -> {
|
||||
assertEquals(0, myTermValueSetConceptDao.count());
|
||||
});
|
||||
runInTransaction(() -> assertEquals(0, myTermValueSetConceptDao.count()));
|
||||
|
||||
expandedValueSet = myTermSvc.expandValueSet(null, valueSet);
|
||||
assertEquals(24, expandedValueSet.getExpansion().getContains().size());
|
||||
|
@ -183,10 +186,10 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test {
|
|||
ValueSet expandedValueSet = myTermSvc.expandValueSet(new ValueSetExpansionOptions(), input);
|
||||
ourLog.debug("Expanded ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expandedValueSet));
|
||||
|
||||
assertThat(toCodes(expandedValueSet).toString(), toCodes(expandedValueSet), containsInAnyOrder(
|
||||
assertThat(ValueSetTestUtil.toCodes(expandedValueSet).toString(), ValueSetTestUtil.toCodes(expandedValueSet), containsInAnyOrder(
|
||||
"code9", "code90", "code91", "code92", "code93", "code94", "code95", "code96", "code97", "code98", "code99"
|
||||
));
|
||||
assertEquals(11, expandedValueSet.getExpansion().getContains().size(), toCodes(expandedValueSet).toString());
|
||||
assertEquals(11, expandedValueSet.getExpansion().getContains().size(), ValueSetTestUtil.toCodes(expandedValueSet).toString());
|
||||
assertEquals(11, expandedValueSet.getExpansion().getTotal());
|
||||
|
||||
// Make sure we used the pre-expanded version
|
||||
|
@ -215,7 +218,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test {
|
|||
// Expansion should contain all codes
|
||||
myCaptureQueriesListener.clear();
|
||||
ValueSet expandedValueSet = myTermSvc.expandValueSet(new ValueSetExpansionOptions(), input);
|
||||
List<String> codes = expandedValueSet.getExpansion().getContains().stream().map(t -> t.getCode()).collect(Collectors.toList());
|
||||
List<String> codes = ValueSetTestUtil.toCodes(expandedValueSet);
|
||||
assertThat(codes.toString(), codes, containsInAnyOrder("code100", "code1000", "code1001", "code1002", "code1003", "code1004"));
|
||||
|
||||
// Make sure we used the pre-expanded version
|
||||
|
@ -230,7 +233,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test {
|
|||
myCaptureQueriesListener.clear();
|
||||
ValueSetExpansionOptions options = ValueSetExpansionOptions.forOffsetAndCount(0, 1000).setFilter("display value 100");
|
||||
ValueSet expandedValueSet = myValueSetDao.expand(vsId, options, mySrd);
|
||||
List<String> codes = expandedValueSet.getExpansion().getContains().stream().map(t -> t.getCode()).collect(Collectors.toList());
|
||||
List<String> codes = ValueSetTestUtil.toCodes(expandedValueSet);
|
||||
assertThat(codes.toString(), codes, containsInAnyOrder("code100", "code1000", "code1001", "code1002", "code1003", "code1004"));
|
||||
|
||||
// Make sure we used the pre-expanded version
|
||||
|
@ -273,8 +276,8 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test {
|
|||
expandedConceptCodes.removeIf(concept -> !concept.startsWith("code9"));
|
||||
|
||||
//Ensure that the subsequent expansion with offset returns the same slice we are anticipating.
|
||||
assertThat(toCodes(expandedValueSet).toString(), toCodes(expandedValueSet), is(equalTo(expandedConceptCodes.subList(offset, offset + count))));
|
||||
assertEquals(4, expandedValueSet.getExpansion().getContains().size(), toCodes(expandedValueSet).toString());
|
||||
assertThat(ValueSetTestUtil.toCodes(expandedValueSet).toString(), ValueSetTestUtil.toCodes(expandedValueSet), is(equalTo(expandedConceptCodes.subList(offset, offset + count))));
|
||||
assertEquals(4, expandedValueSet.getExpansion().getContains().size(), ValueSetTestUtil.toCodes(expandedValueSet).toString());
|
||||
assertEquals(11, expandedValueSet.getExpansion().getTotal());
|
||||
|
||||
// Make sure we used the pre-expanded version
|
||||
|
@ -301,7 +304,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test {
|
|||
ValueSet expandedValueSet = myTermSvc.expandValueSet(null, input);
|
||||
ourLog.debug("Expanded ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expandedValueSet));
|
||||
|
||||
assertThat(toCodes(expandedValueSet).toString(), toCodes(expandedValueSet), contains("code99"));
|
||||
assertThat(ValueSetTestUtil.toCodes(expandedValueSet).toString(), ValueSetTestUtil.toCodes(expandedValueSet), contains("code99"));
|
||||
|
||||
// Make sure we used the pre-expanded version
|
||||
List<SqlQuery> selectQueries = myCaptureQueriesListener.getSelectQueries();
|
||||
|
@ -335,10 +338,10 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test {
|
|||
ValueSet expandedValueSet = myTermSvc.expandValueSet(new ValueSetExpansionOptions(), input);
|
||||
ourLog.debug("Expanded ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expandedValueSet));
|
||||
|
||||
assertThat(toCodes(expandedValueSet).toString(), toCodes(expandedValueSet), containsInAnyOrder(
|
||||
assertThat(ValueSetTestUtil.toCodes(expandedValueSet).toString(), ValueSetTestUtil.toCodes(expandedValueSet), containsInAnyOrder(
|
||||
"code9", "code91", "code92", "code93", "code94", "code95", "code96", "code97", "code98", "code99"
|
||||
));
|
||||
assertEquals(10, expandedValueSet.getExpansion().getContains().size(), toCodes(expandedValueSet).toString());
|
||||
assertEquals(10, expandedValueSet.getExpansion().getContains().size(), ValueSetTestUtil.toCodes(expandedValueSet).toString());
|
||||
assertEquals(10, expandedValueSet.getExpansion().getTotal());
|
||||
|
||||
// Make sure we used the pre-expanded version
|
||||
|
@ -434,11 +437,11 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test {
|
|||
ValueSet expandedValueSet = myTermSvc.expandValueSet(new ValueSetExpansionOptions(), input);
|
||||
ourLog.debug("Expanded ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expandedValueSet));
|
||||
|
||||
assertThat(toCodes(expandedValueSet).toString(), toCodes(expandedValueSet), containsInAnyOrder(
|
||||
assertThat(ValueSetTestUtil.toCodes(expandedValueSet).toString(), ValueSetTestUtil.toCodes(expandedValueSet), containsInAnyOrder(
|
||||
"code9", "code90", "code91", "code92", "code93", "code94", "code95", "code96", "code97", "code98", "code99"
|
||||
));
|
||||
assertEquals(11, expandedValueSet.getExpansion().getContains().size(), toCodes(expandedValueSet).toString());
|
||||
assertEquals(11, expandedValueSet.getExpansion().getTotal(), toCodes(expandedValueSet).toString());
|
||||
assertEquals(11, expandedValueSet.getExpansion().getContains().size(), ValueSetTestUtil.toCodes(expandedValueSet).toString());
|
||||
assertEquals(11, expandedValueSet.getExpansion().getTotal(), ValueSetTestUtil.toCodes(expandedValueSet).toString());
|
||||
|
||||
// Make sure we used the pre-expanded version
|
||||
List<SqlQuery> selectQueries = myCaptureQueriesListener.getSelectQueries();
|
||||
|
@ -446,6 +449,32 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test {
|
|||
assertThat(lastSelectQuery, containsString(" like '%display value 9%'"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExpandNonPersistedValueSet() {
|
||||
|
||||
// Expand
|
||||
ValueSet expansion = myTermSvc.expandValueSet(new ValueSetExpansionOptions(), "http://hl7.org/fhir/ValueSet/administrative-gender");
|
||||
assertThat(ValueSetTestUtil.toCodes(expansion), containsInAnyOrder("male", "female", "other", "unknown"));
|
||||
assertEquals("ValueSet with URL \"ValueSet.url[http://hl7.org/fhir/ValueSet/administrative-gender]\" was expanded using an in-memory expansion", ValueSetTestUtil.extractExpansionMessage(expansion));
|
||||
|
||||
// Validate Code - Good
|
||||
String codeSystemUrl = "http://hl7.org/fhir/administrative-gender";
|
||||
String valueSetUrl = "http://hl7.org/fhir/ValueSet/administrative-gender";
|
||||
String code = "male";
|
||||
IValidationSupport.CodeValidationResult outcome = myValueSetDao.validateCode(new CodeType(valueSetUrl), null, new CodeType(code), new CodeType(codeSystemUrl), null, null, null, mySrd);
|
||||
assertTrue(outcome.isOk());
|
||||
assertEquals("Code was validated against in-memory expansion of ValueSet: http://hl7.org/fhir/ValueSet/administrative-gender", outcome.getMessage());
|
||||
|
||||
// Validate Code - Bad
|
||||
code = "AAA";
|
||||
outcome = myValueSetDao.validateCode(new CodeType(valueSetUrl), null, new CodeType(code), new CodeType(codeSystemUrl), null, null, null, mySrd);
|
||||
assertFalse(outcome.isOk());
|
||||
assertEquals("Unknown code 'http://hl7.org/fhir/administrative-gender#AAA' for in-memory expansion of ValueSet 'http://hl7.org/fhir/ValueSet/administrative-gender'", outcome.getMessage());
|
||||
assertEquals("error", outcome.getSeverityCode());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("SpellCheckingInspection")
|
||||
@Test
|
||||
public void testExpandTermValueSetAndChildren() throws Exception {
|
||||
|
@ -557,8 +586,8 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test {
|
|||
assertConceptContainsDesignation(otherConcept, "nl", "http://snomed.info/sct", "900000000000013009", "Synonym", "Systolische bloeddruk minimaal 1 uur");
|
||||
|
||||
//Ensure they are streamed back in the same order.
|
||||
List<String> firstExpansionCodes = reexpandedValueSet.getExpansion().getContains().stream().map(cn -> cn.getCode()).collect(Collectors.toList());
|
||||
List<String> secondExpansionCodes = expandedValueSet.getExpansion().getContains().stream().map(cn -> cn.getCode()).collect(Collectors.toList());
|
||||
List<String> firstExpansionCodes = ValueSetTestUtil.toCodes(reexpandedValueSet);
|
||||
List<String> secondExpansionCodes = ValueSetTestUtil.toCodes(expandedValueSet);
|
||||
assertThat(firstExpansionCodes, is(equalTo(secondExpansionCodes)));
|
||||
|
||||
//Ensure that internally the designations are expanded back in the same order.
|
||||
|
@ -638,7 +667,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test {
|
|||
|
||||
assertEquals(23, expandedValueSet.getExpansion().getContains().size());
|
||||
//It is enough to test that the sublist returned is the correct one.
|
||||
assertThat(toCodes(expandedValueSet), is(equalTo(expandedConceptCodes.subList(0, 23))));
|
||||
assertThat(ValueSetTestUtil.toCodes(expandedValueSet), is(equalTo(expandedConceptCodes.subList(0, 23))));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -666,7 +695,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test {
|
|||
ValueSet expandedValueSet = myTermSvc.expandValueSet(options, valueSet);
|
||||
String expandedValueSetString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expandedValueSet);
|
||||
ourLog.info("Expanded ValueSet:\n" + expandedValueSetString);
|
||||
assertThat(expandedValueSetString, containsString("ValueSet was expanded using a pre-calculated expansion"));
|
||||
assertThat(expandedValueSetString, containsString("ValueSet was expanded using an expansion that was pre-calculated"));
|
||||
|
||||
assertEquals(codeSystem.getConcept().size(), expandedValueSet.getExpansion().getTotal());
|
||||
assertEquals(myDaoConfig.getPreExpandValueSetsDefaultOffset(), expandedValueSet.getExpansion().getOffset());
|
||||
|
@ -781,7 +810,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test {
|
|||
assertEquals(1000, expandedValueSet.getExpansion().getParameter().get(1).getValueIntegerType().getValue().intValue());
|
||||
|
||||
assertEquals(codeSystem.getConcept().size() - expandedValueSet.getExpansion().getOffset(), expandedValueSet.getExpansion().getContains().size());
|
||||
assertThat(toCodes(expandedValueSet), is(equalTo(expandedConcepts.subList(1, expandedConcepts.size()))));
|
||||
assertThat(ValueSetTestUtil.toCodes(expandedValueSet), is(equalTo(expandedConcepts.subList(1, expandedConcepts.size()))));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -813,7 +842,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test {
|
|||
assertEquals(1000, expandedValueSet.getExpansion().getParameter().get(1).getValueIntegerType().getValue().intValue());
|
||||
|
||||
assertEquals(codeSystem.getConcept().size() - expandedValueSet.getExpansion().getOffset(), expandedValueSet.getExpansion().getContains().size());
|
||||
assertThat(toCodes(expandedValueSet), is(equalTo(expandedConcepts.subList(1, expandedConcepts.size()))));
|
||||
assertThat(ValueSetTestUtil.toCodes(expandedValueSet), is(equalTo(expandedConcepts.subList(1, expandedConcepts.size()))));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -849,21 +878,67 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test {
|
|||
assertEquals(22, expandedValueSet.getExpansion().getContains().size());
|
||||
|
||||
//It is enough to test that the sublist returned is the correct one.
|
||||
assertThat(toCodes(expandedValueSet), is(equalTo(expandedConceptCodes.subList(1, 23))));
|
||||
assertThat(ValueSetTestUtil.toCodes(expandedValueSet), is(equalTo(expandedConceptCodes.subList(1, 23))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExpandValueSetWithUnknownCodeSystem() {
|
||||
// Direct expansion
|
||||
ValueSet vs = new ValueSet();
|
||||
ValueSet.ConceptSetComponent include = vs.getCompose().addInclude();
|
||||
include.setSystem("http://unknown-system");
|
||||
ValueSet outcome = myTermSvc.expandValueSet(new ValueSetExpansionOptions().setFailOnMissingCodeSystem(false), vs);
|
||||
assertEquals(0, outcome.getExpansion().getContains().size());
|
||||
String encoded = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome);
|
||||
ourLog.info(encoded);
|
||||
vs.getCompose().addInclude().setSystem("http://unknown-system");
|
||||
vs = myTermSvc.expandValueSet(new ValueSetExpansionOptions().setFailOnMissingCodeSystem(false), vs);
|
||||
assertNotNull(vs);
|
||||
assertEquals(0, vs.getExpansion().getContains().size());
|
||||
|
||||
// Store it
|
||||
vs = new ValueSet();
|
||||
vs.setId("ValueSet/vs-with-invalid-cs");
|
||||
vs.setUrl("http://vs-with-invalid-cs");
|
||||
vs.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
vs.getCompose().addInclude().setSystem("http://unknown-system");
|
||||
myValueSetDao.update(vs);
|
||||
|
||||
// In memory expansion
|
||||
try {
|
||||
myValueSetDao.expand(vs, new ValueSetExpansionOptions());
|
||||
fail();
|
||||
} catch (InternalErrorException e) {
|
||||
assertEquals("Unable to expand ValueSet because CodeSystem could not be found: http://unknown-system", e.getMessage());
|
||||
}
|
||||
|
||||
// Try validating a code against this VS - This code isn't in a system that's included by the VS, so we know
|
||||
// conclusively that the code isn't valid for the VS even though we don't have the CS that actually is included
|
||||
String codeSystemUrl = "http://invalid-cs";
|
||||
String valueSetUrl = "http://vs-with-invalid-cs";
|
||||
String code = "28571000087109";
|
||||
IValidationSupport.CodeValidationResult outcome = myValueSetDao.validateCode(new CodeType(valueSetUrl), null, new CodeType(code), new CodeType(codeSystemUrl), null, null, null, mySrd);
|
||||
assertFalse(outcome.isOk());
|
||||
assertEquals("Unknown code 'http://invalid-cs#28571000087109' for in-memory expansion of ValueSet 'http://vs-with-invalid-cs'", outcome.getMessage());
|
||||
assertEquals("error", outcome.getSeverityCode());
|
||||
|
||||
// Try validating a code that is in the missing CS that is imported by the VS
|
||||
codeSystemUrl = "http://unknown-system";
|
||||
outcome = myValueSetDao.validateCode(new CodeType(valueSetUrl), null, new CodeType(code), new CodeType(codeSystemUrl), null, null, null, mySrd);
|
||||
assertFalse(outcome.isOk());
|
||||
assertEquals("Failed to expand ValueSet 'http://vs-with-invalid-cs' (in-memory). Could not validate code http://unknown-system#28571000087109. Error was: Unable to expand ValueSet because CodeSystem could not be found: http://unknown-system", outcome.getMessage());
|
||||
assertEquals("error", outcome.getSeverityCode());
|
||||
|
||||
// Perform Pre-Expansion
|
||||
myTerminologyDeferredStorageSvc.saveAllDeferred();
|
||||
myTermSvc.preExpandDeferredValueSetsToTerminologyTables();
|
||||
|
||||
// Make sure it's done
|
||||
runInTransaction(() -> assertNull(myTermCodeSystemDao.findByCodeSystemUri("http://snomed.info/sct")));
|
||||
runInTransaction(() -> assertEquals(TermValueSetPreExpansionStatusEnum.FAILED_TO_EXPAND, myTermValueSetDao.findByUrl("http://vs-with-invalid-cs").orElseThrow(()->new IllegalStateException()).getExpansionStatus()));
|
||||
|
||||
// Try expansion again
|
||||
try {
|
||||
myValueSetDao.expand(vs, new ValueSetExpansionOptions());
|
||||
fail();
|
||||
} catch (InternalErrorException e) {
|
||||
assertEquals("Unable to expand ValueSet because CodeSystem could not be found: http://unknown-system", e.getMessage());
|
||||
}
|
||||
|
||||
Extension extensionByUrl = outcome.getMeta().getExtensionByUrl(HapiExtensions.EXT_VALUESET_EXPANSION_MESSAGE);
|
||||
assertEquals("Unknown CodeSystem URI \"http://unknown-system\" referenced from ValueSet", extensionByUrl.getValueAsPrimitive().getValueAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -899,7 +974,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test {
|
|||
assertEquals(22, expandedValueSet.getExpansion().getContains().size());
|
||||
|
||||
//It is enough to test that the sublist returned is the correct one.
|
||||
assertThat(toCodes(expandedValueSet), is(equalTo(expandedConceptCodes.subList(1, 23))));
|
||||
assertThat(ValueSetTestUtil.toCodes(expandedValueSet), is(equalTo(expandedConceptCodes.subList(1, 23))));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -947,7 +1022,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test {
|
|||
include.setSystem(CS_URL);
|
||||
|
||||
myTermSvc.expandValueSet(null, vs, myValueSetCodeAccumulator);
|
||||
verify(myValueSetCodeAccumulator, times(9)).includeConceptWithDesignations(anyString(), anyString(), nullable(String.class), anyCollection(), nullable(Long.class), nullable(String.class));
|
||||
verify(myValueSetCodeAccumulator, times(9)).includeConceptWithDesignations(anyString(), anyString(), nullable(String.class), anyCollection(), nullable(Long.class), nullable(String.class), nullable(String.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -981,7 +1056,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test {
|
|||
// Non Pre-Expanded
|
||||
ValueSet outcome = myValueSetDao.expand(vs, new ValueSetExpansionOptions());
|
||||
assertEquals("ValueSet \"ValueSet.url[http://vs]\" has not yet been pre-expanded. Performing in-memory expansion without parameters. Current status: NOT_EXPANDED | The ValueSet is waiting to be picked up and pre-expanded by a scheduled task.", outcome.getMeta().getExtensionString(EXT_VALUESET_EXPANSION_MESSAGE));
|
||||
assertThat(toCodes(outcome).toString(), toCodes(outcome), contains(
|
||||
assertThat(ValueSetTestUtil.toCodes(outcome).toString(), ValueSetTestUtil.toCodes(outcome), contains(
|
||||
"code5", "code4", "code3", "code2", "code1"
|
||||
));
|
||||
|
||||
|
@ -991,8 +1066,8 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test {
|
|||
myCaptureQueriesListener.clear();
|
||||
outcome = myValueSetDao.expand(vs, new ValueSetExpansionOptions());
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||
assertEquals("ValueSet was expanded using a pre-calculated expansion", outcome.getMeta().getExtensionString(EXT_VALUESET_EXPANSION_MESSAGE));
|
||||
assertThat(toCodes(outcome).toString(), toCodes(outcome), contains(
|
||||
assertThat(outcome.getMeta().getExtensionString(EXT_VALUESET_EXPANSION_MESSAGE), containsString("ValueSet was expanded using an expansion that was pre-calculated"));
|
||||
assertThat(ValueSetTestUtil.toCodes(outcome).toString(), ValueSetTestUtil.toCodes(outcome), contains(
|
||||
"code5", "code4", "code3", "code2", "code1"
|
||||
));
|
||||
|
||||
|
@ -1474,9 +1549,469 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test {
|
|||
});
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static List<String> toCodes(ValueSet theExpandedValueSet) {
|
||||
return theExpandedValueSet.getExpansion().getContains().stream().map(t -> t.getCode()).collect(Collectors.toList());
|
||||
@Test
|
||||
public void testExpandValueSet_VsIsEnumeratedWithVersionedSystem_CsOnlyDifferentVersionPresent() {
|
||||
CodeSystem cs = new CodeSystem();
|
||||
cs.setId("snomed-ct-ca-imm");
|
||||
cs.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
cs.setContent(CodeSystem.CodeSystemContentMode.FRAGMENT);
|
||||
cs.setUrl("http://snomed.info/sct");
|
||||
cs.setVersion("http://snomed.info/sct/20611000087101/version/20210331");
|
||||
cs.addConcept().setCode("28571000087109").setDisplay("MODERNA COVID-19 mRNA-1273");
|
||||
myCodeSystemDao.update(cs);
|
||||
|
||||
ValueSet vs = new ValueSet();
|
||||
vs.setId("vaccinecode");
|
||||
vs.setUrl("http://ehealthontario.ca/fhir/ValueSet/vaccinecode");
|
||||
vs.setVersion("0.1.17");
|
||||
vs.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
ValueSet.ConceptSetComponent vsInclude = vs.getCompose().addInclude();
|
||||
vsInclude.setSystem("http://snomed.info/sct");
|
||||
vsInclude.setVersion("0.17"); // different version
|
||||
vsInclude.addConcept().setCode("28571000087109").setDisplay("MODERNA COVID-19 mRNA-1273");
|
||||
myValueSetDao.update(vs);
|
||||
|
||||
ConceptValidationOptions options = new ConceptValidationOptions();
|
||||
options.setValidateDisplay(true);
|
||||
|
||||
String codeSystemUrl;
|
||||
String code;
|
||||
ValueSet expansion;
|
||||
IdType vsId = new IdType("ValueSet/vaccinecode");
|
||||
|
||||
// Expand VS
|
||||
expansion = myValueSetDao.expand(vsId, new ValueSetExpansionOptions(), mySrd);
|
||||
assertThat(ValueSetTestUtil.extractExpansionMessage(expansion), containsString("Current status: NOT_EXPANDED"));
|
||||
assertThat(ValueSetTestUtil.toCodes(expansion), contains("28571000087109"));
|
||||
|
||||
// Validate code - good
|
||||
codeSystemUrl = "http://snomed.info/sct";
|
||||
code = "28571000087109";
|
||||
String display = null;
|
||||
IValidationSupport.CodeValidationResult outcome = myValueSetDao.validateCode(null, vsId, new CodeType(code), new UriType(codeSystemUrl), new StringType(display), null, null, mySrd);
|
||||
assertTrue(outcome.isOk());
|
||||
assertEquals("28571000087109", outcome.getCode());
|
||||
assertEquals("MODERNA COVID-19 mRNA-1273", outcome.getDisplay());
|
||||
assertEquals("0.17", outcome.getCodeSystemVersion());
|
||||
|
||||
// Validate code - good code, bad display
|
||||
codeSystemUrl = "http://snomed.info/sct";
|
||||
code = "28571000087109";
|
||||
display = "BLAH";
|
||||
outcome = myValueSetDao.validateCode(null, vsId, new CodeType(code), new UriType(codeSystemUrl), new StringType(display), null, null, mySrd);
|
||||
assertFalse(outcome.isOk());
|
||||
assertEquals(null, outcome.getCode());
|
||||
assertEquals("MODERNA COVID-19 mRNA-1273", outcome.getDisplay());
|
||||
assertEquals("Concept Display \"BLAH\" does not match expected \"MODERNA COVID-19 mRNA-1273\" for in-memory expansion of ValueSet: http://ehealthontario.ca/fhir/ValueSet/vaccinecode", outcome.getMessage());
|
||||
assertEquals("0.17", outcome.getCodeSystemVersion());
|
||||
|
||||
// Validate code - good code, good display
|
||||
codeSystemUrl = "http://snomed.info/sct";
|
||||
code = "28571000087109";
|
||||
display = "MODERNA COVID-19 mRNA-1273";
|
||||
outcome = myValueSetDao.validateCode(null, vsId, new CodeType(code), new UriType(codeSystemUrl), new StringType(display), null, null, mySrd);
|
||||
assertTrue(outcome.isOk());
|
||||
assertEquals("28571000087109", outcome.getCode());
|
||||
assertEquals("MODERNA COVID-19 mRNA-1273", outcome.getDisplay());
|
||||
assertEquals("0.17", outcome.getCodeSystemVersion());
|
||||
|
||||
// Validate code - bad code
|
||||
codeSystemUrl = "http://snomed.info/sct";
|
||||
code = "BLAH";
|
||||
outcome = myValueSetDao.validateCode(null, vsId, new CodeType(code), new UriType(codeSystemUrl), new StringType(display), null, null, mySrd);
|
||||
assertFalse(outcome.isOk());
|
||||
assertEquals(null, outcome.getCode());
|
||||
assertEquals(null, outcome.getDisplay());
|
||||
assertEquals(null, outcome.getCodeSystemVersion());
|
||||
|
||||
// Calculate pre-expansions
|
||||
myTerminologyDeferredStorageSvc.saveAllDeferred();
|
||||
myTermSvc.preExpandDeferredValueSetsToTerminologyTables();
|
||||
|
||||
// Validate code - good
|
||||
codeSystemUrl = "http://snomed.info/sct";
|
||||
code = "28571000087109";
|
||||
display = null;
|
||||
outcome = myValueSetDao.validateCode(null, vsId, new CodeType(code), new UriType(codeSystemUrl), new StringType(display), null, null, mySrd);
|
||||
assertTrue(outcome.isOk());
|
||||
assertEquals("28571000087109", outcome.getCode());
|
||||
assertEquals("MODERNA COVID-19 mRNA-1273", outcome.getDisplay());
|
||||
assertEquals("0.17", outcome.getCodeSystemVersion());
|
||||
assertThat(outcome.getMessage(), startsWith("Code validation occurred using a ValueSet expansion that was pre-calculated at "));
|
||||
|
||||
// Validate code - good code, bad display
|
||||
codeSystemUrl = "http://snomed.info/sct";
|
||||
code = "28571000087109";
|
||||
display = "BLAH";
|
||||
outcome = myValueSetDao.validateCode(null, vsId, new CodeType(code), new UriType(codeSystemUrl), new StringType(display), null, null, mySrd);
|
||||
assertFalse(outcome.isOk());
|
||||
assertEquals(null, outcome.getCode());
|
||||
assertEquals("MODERNA COVID-19 mRNA-1273", outcome.getDisplay());
|
||||
assertEquals("0.17", outcome.getCodeSystemVersion());
|
||||
assertThat(outcome.getMessage(), containsString("Unable to validate code http://snomed.info/sct#28571000087109 - Concept Display \"BLAH\" does not match expected \"MODERNA COVID-19 mRNA-1273\". Code validation occurred using a ValueSet expansion that was pre-calculated at "));
|
||||
|
||||
// Validate code - good code, good display
|
||||
codeSystemUrl = "http://snomed.info/sct";
|
||||
code = "28571000087109";
|
||||
display = "MODERNA COVID-19 mRNA-1273";
|
||||
outcome = myValueSetDao.validateCode(null, vsId, new CodeType(code), new UriType(codeSystemUrl), new StringType(display), null, null, mySrd);
|
||||
assertTrue(outcome.isOk());
|
||||
assertEquals("28571000087109", outcome.getCode());
|
||||
assertEquals("MODERNA COVID-19 mRNA-1273", outcome.getDisplay());
|
||||
assertEquals("0.17", outcome.getCodeSystemVersion());
|
||||
|
||||
// Validate code - bad code
|
||||
codeSystemUrl = "http://snomed.info/sct";
|
||||
code = "BLAH";
|
||||
outcome = myValueSetDao.validateCode(null, vsId, new CodeType(code), new UriType(codeSystemUrl), new StringType(display), null, null, mySrd);
|
||||
assertFalse(outcome.isOk());
|
||||
assertEquals(null, outcome.getCode());
|
||||
assertEquals(null, outcome.getDisplay());
|
||||
assertEquals(null, outcome.getCodeSystemVersion());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testExpandValueSet_VsIsEnumeratedWithVersionedSystem_CsIsFragmentWithWrongVersion() {
|
||||
CodeSystem cs = new CodeSystem();
|
||||
cs.setId("snomed-ct-ca-imm");
|
||||
cs.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
cs.setContent(CodeSystem.CodeSystemContentMode.FRAGMENT);
|
||||
cs.setUrl("http://foo-cs");
|
||||
cs.setVersion("http://snomed.info/sct/20611000087101/version/20210331");
|
||||
cs.addConcept().setCode("28571000087109").setDisplay("MODERNA COVID-19 mRNA-1273");
|
||||
myCodeSystemDao.update(cs, mySrd);
|
||||
|
||||
ValueSet vs = new ValueSet();
|
||||
vs.setId("vaccinecode");
|
||||
vs.setUrl("http://ehealthontario.ca/fhir/ValueSet/vaccinecode");
|
||||
vs.setVersion("0.1.17");
|
||||
vs.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
ValueSet.ConceptSetComponent vsInclude = vs.getCompose().addInclude();
|
||||
vsInclude.setSystem("http://foo-cs");
|
||||
vsInclude.setVersion("0.17"); // different version
|
||||
vsInclude.addConcept().setCode("28571000087109").setDisplay("MODERNA COVID-19 mRNA-1273");
|
||||
myValueSetDao.update(vs, mySrd);
|
||||
|
||||
String codeSystemUrl;
|
||||
String valueSetUrl;
|
||||
String code;
|
||||
|
||||
// Make sure nothing is stored in the TRM DB yet
|
||||
runInTransaction(() -> assertNull(myTermCodeSystemDao.findByCodeSystemUri("http://snomed.info/sct")));
|
||||
runInTransaction(() -> assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, myTermValueSetDao.findByUrl("http://ehealthontario.ca/fhir/ValueSet/vaccinecode").get().getExpansionStatus()));
|
||||
|
||||
// In memory expansion
|
||||
ValueSet expansion = myValueSetDao.expand(vs, new ValueSetExpansionOptions());
|
||||
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expansion));
|
||||
assertThat(ValueSetTestUtil.extractExpansionMessage(expansion), containsString("has not yet been pre-expanded"));
|
||||
assertThat(ValueSetTestUtil.extractExpansionMessage(expansion), containsString("Current status: NOT_EXPANDED"));
|
||||
assertThat(ValueSetTestUtil.toCodes(expansion), contains("28571000087109"));
|
||||
|
||||
codeSystemUrl = "http://snomed.info/sct";
|
||||
valueSetUrl = "http://ehealthontario.ca/fhir/ValueSet/vaccinecode";
|
||||
code = "28571000087109";
|
||||
IValidationSupport.CodeValidationResult outcome = myValueSetDao.validateCode(new CodeType(valueSetUrl), null, new CodeType(code), new CodeType(codeSystemUrl), null, null, null, mySrd);
|
||||
assertFalse(outcome.isOk());
|
||||
assertEquals("Unknown code 'http://snomed.info/sct#28571000087109' for in-memory expansion of ValueSet 'http://ehealthontario.ca/fhir/ValueSet/vaccinecode'", outcome.getMessage());
|
||||
assertEquals("error", outcome.getSeverityCode());
|
||||
|
||||
// Perform Pre-Expansion
|
||||
myTerminologyDeferredStorageSvc.saveAllDeferred();
|
||||
myTermSvc.preExpandDeferredValueSetsToTerminologyTables();
|
||||
|
||||
// Make sure it's done
|
||||
runInTransaction(() -> assertNull(myTermCodeSystemDao.findByCodeSystemUri("http://snomed.info/sct")));
|
||||
runInTransaction(() -> assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, myTermValueSetDao.findByUrl("http://ehealthontario.ca/fhir/ValueSet/vaccinecode").get().getExpansionStatus()));
|
||||
|
||||
// Try expansion again
|
||||
expansion = myValueSetDao.expand(vs, new ValueSetExpansionOptions());
|
||||
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expansion));
|
||||
assertThat(ValueSetTestUtil.extractExpansionMessage(expansion), containsString("ValueSet was expanded using an expansion that was pre-calculated"));
|
||||
assertThat(ValueSetTestUtil.toCodes(expansion), contains("28571000087109"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExpandValueSet_VsIsNonEnumeratedWithVersionedSystem_CsIsFragmentWithWrongVersion() {
|
||||
CodeSystem cs = new CodeSystem();
|
||||
cs.setId("snomed-ct-ca-imm");
|
||||
cs.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
cs.setContent(CodeSystem.CodeSystemContentMode.FRAGMENT);
|
||||
cs.setUrl("http://foo-cs");
|
||||
cs.setVersion("http://snomed.info/sct/20611000087101/version/20210331");
|
||||
cs.addConcept().setCode("28571000087109").setDisplay("MODERNA COVID-19 mRNA-1273");
|
||||
myCodeSystemDao.update(cs, mySrd);
|
||||
|
||||
ValueSet vs = new ValueSet();
|
||||
vs.setId("vaccinecode");
|
||||
vs.setUrl("http://ehealthontario.ca/fhir/ValueSet/vaccinecode");
|
||||
vs.setVersion("0.1.17");
|
||||
vs.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
ValueSet.ConceptSetComponent vsInclude = vs.getCompose().addInclude();
|
||||
vsInclude.setSystem("http://foo-cs");
|
||||
vsInclude.setVersion("0.17"); // different version
|
||||
myValueSetDao.update(vs, mySrd);
|
||||
|
||||
String codeSystemUrl;
|
||||
String valueSetUrl;
|
||||
String code;
|
||||
|
||||
// Make sure nothing is stored in the TRM DB yet
|
||||
runInTransaction(() -> assertNull(myTermCodeSystemDao.findByCodeSystemUri("http://snomed.info/sct")));
|
||||
runInTransaction(() -> assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, myTermValueSetDao.findByUrl("http://ehealthontario.ca/fhir/ValueSet/vaccinecode").get().getExpansionStatus()));
|
||||
|
||||
// In memory expansion
|
||||
try {
|
||||
myValueSetDao.expand(vs, new ValueSetExpansionOptions());
|
||||
} catch (InternalErrorException e) {
|
||||
assertEquals("Unable to expand ValueSet because CodeSystem could not be found: http://foo-cs|0.17", e.getMessage());
|
||||
}
|
||||
|
||||
codeSystemUrl = "http://snomed.info/sct";
|
||||
valueSetUrl = "http://ehealthontario.ca/fhir/ValueSet/vaccinecode";
|
||||
code = "28571000087109";
|
||||
IValidationSupport.CodeValidationResult outcome = myValueSetDao.validateCode(new CodeType(valueSetUrl), null, new CodeType(code), new CodeType(codeSystemUrl), null, null, null, mySrd);
|
||||
assertFalse(outcome.isOk());
|
||||
assertEquals("Unknown code 'http://snomed.info/sct#28571000087109' for in-memory expansion of ValueSet 'http://ehealthontario.ca/fhir/ValueSet/vaccinecode'", outcome.getMessage());
|
||||
assertEquals("error", outcome.getSeverityCode());
|
||||
|
||||
// Perform Pre-Expansion
|
||||
myTerminologyDeferredStorageSvc.saveAllDeferred();
|
||||
myTermSvc.preExpandDeferredValueSetsToTerminologyTables();
|
||||
|
||||
// Make sure it's done
|
||||
runInTransaction(() -> assertNull(myTermCodeSystemDao.findByCodeSystemUri("http://snomed.info/sct")));
|
||||
runInTransaction(() -> assertEquals(TermValueSetPreExpansionStatusEnum.FAILED_TO_EXPAND, myTermValueSetDao.findByUrl("http://ehealthontario.ca/fhir/ValueSet/vaccinecode").get().getExpansionStatus()));
|
||||
|
||||
// Try expansion again
|
||||
try {
|
||||
myValueSetDao.expand(vs, new ValueSetExpansionOptions());
|
||||
} catch (InternalErrorException e) {
|
||||
assertEquals("Unable to expand ValueSet because CodeSystem could not be found: http://foo-cs|0.17", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testExpandValueSet_VsUsesVersionedSystem_CsIsFragmentWithoutCode() {
|
||||
CodeSystem cs = new CodeSystem();
|
||||
cs.setId("snomed-ct-ca-imm");
|
||||
cs.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
cs.setContent(CodeSystem.CodeSystemContentMode.FRAGMENT);
|
||||
cs.setUrl("http://snomed.info/sct");
|
||||
cs.setVersion("http://snomed.info/sct/20611000087101/version/20210331");
|
||||
cs.addConcept().setCode("28571000087109").setDisplay("MODERNA COVID-19 mRNA-1273");
|
||||
myCodeSystemDao.update(cs, mySrd);
|
||||
|
||||
ValueSet vs = new ValueSet();
|
||||
vs.setId("vaccinecode");
|
||||
vs.setUrl("http://ehealthontario.ca/fhir/ValueSet/vaccinecode");
|
||||
vs.setVersion("0.1.17");
|
||||
vs.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
ValueSet.ConceptSetComponent vsInclude = vs.getCompose().addInclude();
|
||||
vsInclude.setSystem("http://snomed.info/sct");
|
||||
vsInclude.setVersion("http://snomed.info/sct/20611000087101/version/20210331");
|
||||
vsInclude.addConcept().setCode("28571000087109").setDisplay("MODERNA COVID-19 mRNA-1273");
|
||||
myValueSetDao.update(vs, mySrd);
|
||||
|
||||
String codeSystemUrl;
|
||||
String valueSetUrl;
|
||||
String code;
|
||||
|
||||
ValueSet valueSet = myValueSetDao.expand(vs, new ValueSetExpansionOptions());
|
||||
assertNotNull(valueSet);
|
||||
assertEquals(1, valueSet.getExpansion().getContains().size());
|
||||
assertEquals("28571000087109", valueSet.getExpansion().getContains().get(0).getCode());
|
||||
assertEquals("MODERNA COVID-19 mRNA-1273", valueSet.getExpansion().getContains().get(0).getDisplay());
|
||||
|
||||
codeSystemUrl = "http://snomed.info/sct";
|
||||
valueSetUrl = "http://ehealthontario.ca/fhir/ValueSet/vaccinecode";
|
||||
code = "28571000087109";
|
||||
IValidationSupport.CodeValidationResult outcome = myValueSetDao.validateCode(new CodeType(valueSetUrl), null, new CodeType(code), new CodeType(codeSystemUrl), null, null, null, mySrd);
|
||||
assertTrue(outcome.isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExpandValueSet_VsUsesVersionedSystem_CsIsFragmentWithCode() {
|
||||
CodeSystem cs = new CodeSystem();
|
||||
cs.setId("snomed-ct-ca-imm");
|
||||
cs.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
cs.setContent(CodeSystem.CodeSystemContentMode.FRAGMENT);
|
||||
cs.setUrl("http://snomed.info/sct");
|
||||
cs.setVersion("http://snomed.info/sct/20611000087101/version/20210331");
|
||||
cs.addConcept().setCode("28571000087109").setDisplay("MODERNA COVID-19 mRNA-1273");
|
||||
myCodeSystemDao.update(cs, mySrd);
|
||||
|
||||
ValueSet vs = new ValueSet();
|
||||
vs.setId("vaccinecode");
|
||||
vs.setUrl("http://ehealthontario.ca/fhir/ValueSet/vaccinecode");
|
||||
vs.setVersion("http://snomed.info/sct/20611000087101/version/20210331");
|
||||
vs.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
ValueSet.ConceptSetComponent vsInclude = vs.getCompose().addInclude();
|
||||
vsInclude.setSystem("http://snomed.info/sct");
|
||||
vsInclude.setVersion("http://snomed.info/sct/20611000087101/version/20210331");
|
||||
vsInclude.addConcept().setCode("28571000087109").setDisplay("MODERNA COVID-19 mRNA-1273");
|
||||
myValueSetDao.update(vs, mySrd);
|
||||
|
||||
String codeSystemUrl;
|
||||
String valueSetUrl;
|
||||
String code;
|
||||
IValidationSupport.CodeValidationResult outcome;
|
||||
|
||||
// Good code
|
||||
codeSystemUrl = "http://snomed.info/sct";
|
||||
valueSetUrl = "http://ehealthontario.ca/fhir/ValueSet/vaccinecode";
|
||||
code = "28571000087109";
|
||||
outcome = myValueSetDao.validateCode(new CodeType(valueSetUrl), null, new CodeType(code), new CodeType(codeSystemUrl), null, null, null, mySrd);
|
||||
assertTrue(outcome.isOk());
|
||||
assertEquals("MODERNA COVID-19 mRNA-1273", outcome.getDisplay());
|
||||
|
||||
// Bad code
|
||||
codeSystemUrl = "http://snomed.info/sct";
|
||||
valueSetUrl = "http://ehealthontario.ca/fhir/ValueSet/vaccinecode";
|
||||
code = "123";
|
||||
outcome = myValueSetDao.validateCode(new CodeType(valueSetUrl), null, new CodeType(code), new CodeType(codeSystemUrl), null, null, null, mySrd);
|
||||
assertFalse(outcome.isOk());
|
||||
|
||||
ValueSet valueSet = myValueSetDao.expand(vs, new ValueSetExpansionOptions());
|
||||
assertNotNull(valueSet);
|
||||
assertEquals(1, valueSet.getExpansion().getContains().size());
|
||||
ValueSet.ValueSetExpansionContainsComponent expansionCode = valueSet.getExpansion().getContains().get(0);
|
||||
assertEquals("28571000087109", expansionCode.getCode());
|
||||
assertEquals("MODERNA COVID-19 mRNA-1273", expansionCode.getDisplay());
|
||||
assertEquals("http://snomed.info/sct/20611000087101/version/20210331", expansionCode.getVersion());
|
||||
|
||||
myTerminologyDeferredStorageSvc.saveAllDeferred();
|
||||
myTermSvc.preExpandDeferredValueSetsToTerminologyTables();
|
||||
|
||||
valueSet = myValueSetDao.expand(vs, new ValueSetExpansionOptions());
|
||||
assertNotNull(valueSet);
|
||||
assertEquals(1, valueSet.getExpansion().getContains().size());
|
||||
expansionCode = valueSet.getExpansion().getContains().get(0);
|
||||
assertEquals("28571000087109", expansionCode.getCode());
|
||||
assertEquals("MODERNA COVID-19 mRNA-1273", expansionCode.getDisplay());
|
||||
assertEquals("http://snomed.info/sct/20611000087101/version/20210331", expansionCode.getVersion());
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExpandValueSet_VsUsesVersionedSystem_CsIsCompleteWithCode() {
|
||||
CodeSystem cs = new CodeSystem();
|
||||
cs.setId("snomed-ct-ca-imm");
|
||||
cs.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
cs.setContent(CodeSystem.CodeSystemContentMode.COMPLETE);
|
||||
cs.setUrl("http://snomed.info/sct");
|
||||
cs.setVersion("http://snomed.info/sct/20611000087101/version/20210331");
|
||||
cs.addConcept().setCode("28571000087109").setDisplay("MODERNA COVID-19 mRNA-1273");
|
||||
myCodeSystemDao.update(cs, mySrd);
|
||||
|
||||
ValueSet vs = new ValueSet();
|
||||
vs.setId("vaccinecode");
|
||||
vs.setUrl("http://ehealthontario.ca/fhir/ValueSet/vaccinecode");
|
||||
vs.setVersion("http://snomed.info/sct/20611000087101/version/20210331");
|
||||
vs.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
ValueSet.ConceptSetComponent vsInclude = vs.getCompose().addInclude();
|
||||
vsInclude.setSystem("http://snomed.info/sct");
|
||||
vsInclude.setVersion("http://snomed.info/sct/20611000087101/version/20210331");
|
||||
vsInclude.addConcept().setCode("28571000087109").setDisplay("MODERNA COVID-19 mRNA-1273");
|
||||
myValueSetDao.update(vs, mySrd);
|
||||
|
||||
String codeSystemUrl;
|
||||
String valueSetUrl;
|
||||
String code;
|
||||
IValidationSupport.CodeValidationResult outcome;
|
||||
|
||||
// Good code
|
||||
codeSystemUrl = "http://snomed.info/sct";
|
||||
valueSetUrl = "http://ehealthontario.ca/fhir/ValueSet/vaccinecode";
|
||||
code = "28571000087109";
|
||||
outcome = myValueSetDao.validateCode(new CodeType(valueSetUrl), null, new CodeType(code), new CodeType(codeSystemUrl), null, null, null, mySrd);
|
||||
assertTrue(outcome.isOk());
|
||||
assertEquals("MODERNA COVID-19 mRNA-1273", outcome.getDisplay());
|
||||
|
||||
// Bad code
|
||||
codeSystemUrl = "http://snomed.info/sct";
|
||||
valueSetUrl = "http://ehealthontario.ca/fhir/ValueSet/vaccinecode";
|
||||
code = "123";
|
||||
outcome = myValueSetDao.validateCode(new CodeType(valueSetUrl), null, new CodeType(code), new CodeType(codeSystemUrl), null, null, null, mySrd);
|
||||
assertFalse(outcome.isOk());
|
||||
|
||||
ValueSet valueSet = myValueSetDao.expand(vs, new ValueSetExpansionOptions());
|
||||
assertNotNull(valueSet);
|
||||
assertEquals(1, valueSet.getExpansion().getContains().size());
|
||||
assertEquals("28571000087109", valueSet.getExpansion().getContains().get(0).getCode());
|
||||
assertEquals("MODERNA COVID-19 mRNA-1273", valueSet.getExpansion().getContains().get(0).getDisplay());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequestValueSetReExpansion() {
|
||||
CodeSystem cs = new CodeSystem();
|
||||
cs.setId("cs");
|
||||
cs.setUrl("http://cs");
|
||||
cs.setContent(CodeSystem.CodeSystemContentMode.COMPLETE);
|
||||
cs.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
cs.addConcept().setCode("A").setDisplay("Code A");
|
||||
myCodeSystemDao.update(cs, mySrd);
|
||||
|
||||
ValueSet vs = new ValueSet();
|
||||
vs.setId("vs");
|
||||
vs.setUrl("http://vs");
|
||||
vs.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
vs.getCompose().addInclude().setSystem("http://cs");
|
||||
myValueSetDao.update(vs, mySrd);
|
||||
|
||||
// Perform pre-expansion
|
||||
myTerminologyDeferredStorageSvc.saveAllDeferred();
|
||||
myTermSvc.preExpandDeferredValueSetsToTerminologyTables();
|
||||
|
||||
// Expand
|
||||
ValueSet expansion = myValueSetDao.expand(new IdType("ValueSet/vs"), new ValueSetExpansionOptions(), mySrd);
|
||||
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expansion));
|
||||
assertThat(ValueSetTestUtil.extractExpansionMessage(expansion), containsString("ValueSet was expanded using an expansion that was pre-calculated"));
|
||||
assertThat(ValueSetTestUtil.toCodes(expansion), contains("A"));
|
||||
|
||||
// Change the CodeSystem
|
||||
cs.getConcept().clear();
|
||||
cs.addConcept().setCode("B").setDisplay("Code B");
|
||||
myCodeSystemDao.update(cs, mySrd);
|
||||
|
||||
// Previous precalculated expansion should still hold
|
||||
expansion = myValueSetDao.expand(new IdType("ValueSet/vs"), new ValueSetExpansionOptions(), mySrd);
|
||||
assertThat(ValueSetTestUtil.toCodes(expansion), contains("A"));
|
||||
|
||||
// Invalidate the precalculated expansion
|
||||
myTermSvc.invalidatePreCalculatedExpansion(new IdType("ValueSet/vs"), mySrd);
|
||||
|
||||
// Expand (should not use a precalculated expansion)
|
||||
expansion = myValueSetDao.expand(new IdType("ValueSet/vs"), new ValueSetExpansionOptions(), mySrd);
|
||||
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expansion));
|
||||
assertThat(ValueSetTestUtil.extractExpansionMessage(expansion), containsString("Performing in-memory expansion without parameters"));
|
||||
assertThat(ValueSetTestUtil.toCodes(expansion), contains("B"));
|
||||
|
||||
// Perform pre-expansion
|
||||
myTerminologyDeferredStorageSvc.saveAllDeferred();
|
||||
myTermSvc.preExpandDeferredValueSetsToTerminologyTables();
|
||||
|
||||
// Expand (should use the new precalculated expansion)
|
||||
expansion = myValueSetDao.expand(new IdType("ValueSet/vs"), new ValueSetExpansionOptions(), mySrd);
|
||||
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expansion));
|
||||
assertThat(ValueSetTestUtil.extractExpansionMessage(expansion), containsString("ValueSet was expanded using an expansion that was pre-calculated"));
|
||||
assertThat(ValueSetTestUtil.toCodes(expansion), contains("B"));
|
||||
|
||||
// Validate code that is good
|
||||
IValidationSupport.CodeValidationResult outcome = myValueSetDao.validateCode(vs.getUrlElement(), null, new StringType("B"), cs.getUrlElement(), null, null, null, mySrd);
|
||||
assertEquals(true, outcome.isOk());
|
||||
assertThat(outcome.getMessage(), containsString("Code validation occurred using a ValueSet expansion that was pre-calculated"));
|
||||
|
||||
// Validate code that is bad
|
||||
outcome = myValueSetDao.validateCode(vs.getUrlElement(), null, new StringType("A"), cs.getUrlElement(), null, null, null, mySrd);
|
||||
assertEquals(false, outcome.isOk());
|
||||
assertThat(outcome.getMessage(), containsString("Code validation occurred using a ValueSet expansion that was pre-calculated"));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package ca.uhn.fhir.jpa.util;
|
||||
|
||||
import org.hl7.fhir.r4.model.Extension;
|
||||
import org.hl7.fhir.r4.model.ValueSet;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static ca.uhn.fhir.util.HapiExtensions.EXT_VALUESET_EXPANSION_MESSAGE;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class ValueSetTestUtil {
|
||||
public static String extractExpansionMessage(ValueSet outcome) {
|
||||
List<Extension> extensions = outcome.getMeta().getExtensionsByUrl(EXT_VALUESET_EXPANSION_MESSAGE);
|
||||
assertEquals(1, extensions.size());
|
||||
String expansionMessage = extensions.get(0).getValueAsPrimitive().getValueAsString();
|
||||
return expansionMessage;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static List<String> toCodes(ValueSet theExpandedValueSet) {
|
||||
return theExpandedValueSet.getExpansion().getContains().stream().map(t -> t.getCode()).collect(Collectors.toList());
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ import ca.uhn.fhir.cql.common.helper.PartitionHelper;
|
|||
import ca.uhn.fhir.cql.common.provider.CqlProviderTestBase;
|
||||
import ca.uhn.fhir.cql.config.CqlR4Config;
|
||||
import ca.uhn.fhir.cql.config.TestCqlConfig;
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
|
||||
import ca.uhn.fhir.jpa.subscription.match.config.SubscriptionProcessorConfig;
|
||||
|
@ -13,6 +14,9 @@ import ca.uhn.fhir.rest.api.server.RequestDetails;
|
|||
import ca.uhn.fhir.test.utilities.RequestDetailsHelper;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.model.Meta;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -45,7 +49,19 @@ public class BaseCqlR4Test extends BaseJpaR4Test implements CqlProviderTestBase
|
|||
protected
|
||||
FhirContext myFhirContext;
|
||||
@Autowired
|
||||
IFhirSystemDao mySystemDao;
|
||||
IFhirSystemDao<Bundle, Meta> mySystemDao;
|
||||
@Autowired
|
||||
DaoConfig myDaoConfig;
|
||||
|
||||
@BeforeEach
|
||||
public void beforeEach() {
|
||||
myDaoConfig.setMaximumExpansionSize(5000);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void afterEach() {
|
||||
myDaoConfig.setMaximumExpansionSize(new DaoConfig().getMaximumExpansionSize());
|
||||
}
|
||||
|
||||
protected int loadDataFromDirectory(String theDirectoryName) throws IOException {
|
||||
int count = 0;
|
||||
|
|
|
@ -40,10 +40,16 @@ public class RuntimeSearchParamCache extends ReadOnlySearchParamCache {
|
|||
getSearchParamMap(theResourceName).put(theName, theSearchParam);
|
||||
String uri = theSearchParam.getUri();
|
||||
if (isNotBlank(uri)) {
|
||||
if (myUrlToParam.containsKey(uri)) {
|
||||
RuntimeSearchParam existingForUrl = myUrlToParam.get(uri);
|
||||
if (existingForUrl == theSearchParam) {
|
||||
// This is expected, since the same SP can span multiple resource types
|
||||
// so it may get added more than once by this method
|
||||
ourLog.trace("Search param was previously registered for url: {}", uri);
|
||||
} else if (existingForUrl != null) {
|
||||
ourLog.warn("Multiple search parameters have URL: {}", uri);
|
||||
} else {
|
||||
myUrlToParam.put(uri, theSearchParam);
|
||||
}
|
||||
myUrlToParam.put(uri, theSearchParam);
|
||||
}
|
||||
if (theSearchParam.getId() != null && theSearchParam.getId().hasIdPart()) {
|
||||
String value = theSearchParam.getId().toUnqualifiedVersionless().getValue();
|
||||
|
|
|
@ -156,6 +156,11 @@ public class ProviderConstants {
|
|||
*/
|
||||
public static final String OPERATION_REINDEX = "$reindex";
|
||||
|
||||
/**
|
||||
* Operation name for the $invalidate-expansion operation
|
||||
*/
|
||||
public static final String OPERATION_INVALIDATE_EXPANSION = "$invalidate-expansion";
|
||||
|
||||
/**
|
||||
* url of resources to delete for the $delete-expunge operation
|
||||
*/
|
||||
|
|
|
@ -21,6 +21,7 @@ package ca.uhn.fhir.rest.server.provider;
|
|||
*/
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
|
@ -28,9 +29,11 @@ import ca.uhn.fhir.rest.api.server.storage.IReindexJobSubmitter;
|
|||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.util.ParametersUtil;
|
||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.JobParametersInvalidException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.math.BigDecimal;
|
||||
|
@ -55,7 +58,7 @@ public class ReindexProvider {
|
|||
@OperationParam(name = ProviderConstants.OPERATION_REINDEX_PARAM_EVERYTHING, typeName = "boolean", min = 0, max = 1) IPrimitiveType<Boolean> theEverything,
|
||||
RequestDetails theRequestDetails
|
||||
) {
|
||||
Boolean everything = theEverything != null && theEverything.getValue();
|
||||
boolean everything = theEverything != null && theEverything.getValue();
|
||||
@Nullable Integer batchSize = myMultiUrlProcessor.getBatchSize(theBatchSize);
|
||||
if (everything) {
|
||||
return processEverything(batchSize, theRequestDetails);
|
||||
|
@ -70,9 +73,9 @@ public class ReindexProvider {
|
|||
private IBaseParameters processEverything(Integer theBatchSize, RequestDetails theRequestDetails) {
|
||||
try {
|
||||
JobExecution jobExecution = myReindexJobSubmitter.submitEverythingJob(theBatchSize, theRequestDetails);
|
||||
IBaseParameters retval = ParametersUtil.newInstance(myFhirContext);
|
||||
ParametersUtil.addParameterToParametersLong(myFhirContext, retval, ProviderConstants.OPERATION_BATCH_RESPONSE_JOB_ID, jobExecution.getJobId());
|
||||
return retval;
|
||||
IBaseParameters retVal = ParametersUtil.newInstance(myFhirContext);
|
||||
ParametersUtil.addParameterToParametersLong(myFhirContext, retVal, ProviderConstants.OPERATION_BATCH_RESPONSE_JOB_ID, jobExecution.getJobId());
|
||||
return retVal;
|
||||
} catch (JobParametersInvalidException e) {
|
||||
throw new InvalidRequestException("Invalid job parameters: " + e.getMessage(), e);
|
||||
}
|
||||
|
|
|
@ -66,6 +66,11 @@ public class BaseValidationSupportWrapper extends BaseValidationSupport {
|
|||
return myWrap.validateCode(theValidationSupportContext, theOptions, theCodeSystem, theCode, theDisplay, theValueSetUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IValidationSupport.CodeValidationResult validateCodeInValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theValidationOptions, String theCodeSystem, String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) {
|
||||
return myWrap.validateCodeInValueSet(theValidationSupportContext, theValidationOptions, theCodeSystem, theCode, theDisplay, theValueSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theDisplayLanguage) {
|
||||
return myWrap.lookupCode(theValidationSupportContext, theSystem, theCode, theDisplayLanguage);
|
||||
|
@ -102,11 +107,6 @@ public class BaseValidationSupportWrapper extends BaseValidationSupport {
|
|||
return myWrap.generateSnapshot(theValidationSupportContext, theInput, theUrl, theWebUrl, theProfileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IValidationSupport.CodeValidationResult validateCodeInValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theValidationOptions, String theCodeSystem, String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) {
|
||||
return myWrap.validateCodeInValueSet(theValidationSupportContext, theValidationOptions, theCodeSystem, theCode, theDisplay, theValueSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TranslateConceptResults translateConcept(TranslateCodeRequest theRequest) {
|
||||
return myWrap.translateConcept(theRequest);
|
||||
|
|
|
@ -90,6 +90,21 @@ public class CachingValidationSupport extends BaseValidationSupportWrapper imple
|
|||
return loadFromCache(myCache, key, t -> super.fetchAllNonBaseStructureDefinitions());
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBaseResource fetchCodeSystem(String theSystem) {
|
||||
return loadFromCache(myCache, "fetchCodeSystem " + theSystem, t -> super.fetchCodeSystem(theSystem));
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBaseResource fetchValueSet(String theUri) {
|
||||
return loadFromCache(myCache, "fetchValueSet " + theUri, t -> super.fetchValueSet(theUri));
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBaseResource fetchStructureDefinition(String theUrl) {
|
||||
return loadFromCache(myCache, "fetchStructureDefinition " + theUrl, t -> super.fetchStructureDefinition(theUrl));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends IBaseResource> T fetchResource(@Nullable Class<T> theClass, String theUri) {
|
||||
return loadFromCache(myCache, "fetchResource " + theClass + " " + theUri,
|
||||
|
|
|
@ -31,6 +31,7 @@ import java.util.List;
|
|||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -59,8 +60,16 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
|||
|
||||
@Override
|
||||
public ValueSetExpansionOutcome expandValueSet(ValidationSupportContext theValidationSupportContext, ValueSetExpansionOptions theExpansionOptions, @Nonnull IBaseResource theValueSetToExpand) {
|
||||
return expandValueSet(theValidationSupportContext, theExpansionOptions, theValueSetToExpand, null, null);
|
||||
}
|
||||
|
||||
org.hl7.fhir.r5.model.ValueSet expansionR5 = expandValueSetToCanonical(theValidationSupportContext, theValueSetToExpand, null, null);
|
||||
private ValueSetExpansionOutcome expandValueSet(ValidationSupportContext theValidationSupportContext, ValueSetExpansionOptions theExpansionOptions, IBaseResource theValueSetToExpand, String theWantSystemAndVersion, String theWantCode) {
|
||||
org.hl7.fhir.r5.model.ValueSet expansionR5;
|
||||
try {
|
||||
expansionR5 = expandValueSetToCanonical(theValidationSupportContext, theValueSetToExpand, theWantSystemAndVersion, theWantCode);
|
||||
} catch (ExpansionCouldNotBeCompletedInternallyException e) {
|
||||
return new ValueSetExpansionOutcome(e.getMessage());
|
||||
}
|
||||
if (expansionR5 == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -89,10 +98,10 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
|||
throw new IllegalArgumentException("Can not handle version: " + myCtx.getVersion().getVersion());
|
||||
}
|
||||
|
||||
return new ValueSetExpansionOutcome(expansion, null);
|
||||
return new ValueSetExpansionOutcome(expansion);
|
||||
}
|
||||
|
||||
private org.hl7.fhir.r5.model.ValueSet expandValueSetToCanonical(ValidationSupportContext theValidationSupportContext, IBaseResource theValueSetToExpand, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) {
|
||||
private org.hl7.fhir.r5.model.ValueSet expandValueSetToCanonical(ValidationSupportContext theValidationSupportContext, IBaseResource theValueSetToExpand, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) throws ExpansionCouldNotBeCompletedInternallyException {
|
||||
org.hl7.fhir.r5.model.ValueSet expansionR5;
|
||||
switch (theValueSetToExpand.getStructureFhirVersionEnum()) {
|
||||
case DSTU2: {
|
||||
|
@ -112,7 +121,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
|||
break;
|
||||
}
|
||||
case R5: {
|
||||
expansionR5 = expandValueSetR5(theValidationSupportContext, (org.hl7.fhir.r5.model.ValueSet) theValueSetToExpand);
|
||||
expansionR5 = expandValueSetR5(theValidationSupportContext, (org.hl7.fhir.r5.model.ValueSet) theValueSetToExpand, theWantSystemUrlAndVersion, theWantCode);
|
||||
break;
|
||||
}
|
||||
case DSTU2_1:
|
||||
|
@ -124,17 +133,34 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
|||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult
|
||||
validateCodeInValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystemUrlAndVersion, String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) {
|
||||
org.hl7.fhir.r5.model.ValueSet expansion = expandValueSetToCanonical(theValidationSupportContext, theValueSet, theCodeSystemUrlAndVersion, theCode);
|
||||
public CodeValidationResult validateCodeInValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystemUrlAndVersion, String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) {
|
||||
org.hl7.fhir.r5.model.ValueSet expansion;
|
||||
String vsUrl = CommonCodeSystemsTerminologyService.getValueSetUrl(theValueSet);
|
||||
try {
|
||||
expansion = expandValueSetToCanonical(theValidationSupportContext, theValueSet, theCodeSystemUrlAndVersion, theCode);
|
||||
} catch (ExpansionCouldNotBeCompletedInternallyException e) {
|
||||
CodeValidationResult codeValidationResult = new CodeValidationResult();
|
||||
codeValidationResult.setSeverityCode("error");
|
||||
|
||||
String msg = "Failed to expand ValueSet '" + vsUrl + "' (in-memory). Could not validate code " + theCodeSystemUrlAndVersion + "#" + theCode;
|
||||
if (e.getMessage() != null) {
|
||||
msg += ". Error was: " + e.getMessage();
|
||||
}
|
||||
|
||||
codeValidationResult.setMessage(msg);
|
||||
return codeValidationResult;
|
||||
}
|
||||
|
||||
if (expansion == null) {
|
||||
return null;
|
||||
}
|
||||
return validateCodeInExpandedValueSet(theValidationSupportContext, theOptions, theCodeSystemUrlAndVersion, theCode, theDisplay, expansion);
|
||||
|
||||
return validateCodeInExpandedValueSet(theValidationSupportContext, theOptions, theCodeSystemUrlAndVersion, theCode, theDisplay, expansion, vsUrl);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public CodeValidationResult validateCode(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
|
||||
IBaseResource vs;
|
||||
if (isNotBlank(theValueSetUrl)) {
|
||||
|
@ -198,18 +224,22 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
|||
}
|
||||
}
|
||||
|
||||
ValueSetExpansionOutcome valueSetExpansionOutcome = expandValueSet(theValidationSupportContext, null, vs);
|
||||
ValueSetExpansionOutcome valueSetExpansionOutcome = expandValueSet(theValidationSupportContext, null, vs, theCodeSystem, theCode);
|
||||
if (valueSetExpansionOutcome == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (valueSetExpansionOutcome.getError() != null) {
|
||||
return new CodeValidationResult()
|
||||
.setSeverity(IssueSeverity.ERROR)
|
||||
.setMessage(valueSetExpansionOutcome.getError());
|
||||
}
|
||||
|
||||
IBaseResource expansion = valueSetExpansionOutcome.getValueSet();
|
||||
|
||||
return validateCodeInExpandedValueSet(theValidationSupportContext, theOptions, theCodeSystem, theCode, theDisplay, expansion);
|
||||
|
||||
return validateCodeInExpandedValueSet(theValidationSupportContext, theOptions, theCodeSystem, theCode, theDisplay, expansion, theValueSetUrl);
|
||||
}
|
||||
|
||||
private CodeValidationResult validateCodeInExpandedValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystemUrlAndVersionToValidate, String theCodeToValidate, String theDisplayToValidate, IBaseResource theExpansion) {
|
||||
private CodeValidationResult validateCodeInExpandedValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystemUrlAndVersionToValidate, String theCodeToValidate, String theDisplayToValidate, IBaseResource theExpansion, String theValueSetUrl) {
|
||||
assert theExpansion != null;
|
||||
|
||||
boolean caseSensitive = true;
|
||||
|
@ -290,13 +320,13 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
|||
}
|
||||
}
|
||||
|
||||
String codeSystemUrlToValidate=null;
|
||||
String codeSystemVersionToValidate=null;
|
||||
String codeSystemUrlToValidate = null;
|
||||
String codeSystemVersionToValidate = null;
|
||||
if (theCodeSystemUrlAndVersionToValidate != null) {
|
||||
int versionIndex = theCodeSystemUrlAndVersionToValidate.indexOf("|");
|
||||
if (versionIndex > -1) {
|
||||
codeSystemUrlToValidate = theCodeSystemUrlAndVersionToValidate.substring(0, versionIndex);
|
||||
codeSystemVersionToValidate = theCodeSystemUrlAndVersionToValidate.substring(versionIndex+1);
|
||||
codeSystemVersionToValidate = theCodeSystemUrlAndVersionToValidate.substring(versionIndex + 1);
|
||||
} else {
|
||||
codeSystemUrlToValidate = theCodeSystemUrlAndVersionToValidate;
|
||||
}
|
||||
|
@ -311,19 +341,31 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
|||
}
|
||||
if (codeMatches) {
|
||||
if (theOptions.isInferSystem() || (nextExpansionCode.getSystem().equals(codeSystemUrlToValidate) && (codeSystemVersionToValidate == null || codeSystemVersionToValidate.equals(nextExpansionCode.getSystemVersion())))) {
|
||||
String csVersion = codeSystemResourceVersion;
|
||||
if (isNotBlank(nextExpansionCode.getSystemVersion())) {
|
||||
csVersion = nextExpansionCode.getSystemVersion();
|
||||
}
|
||||
if (!theOptions.isValidateDisplay() || (isBlank(nextExpansionCode.getDisplay()) || isBlank(theDisplayToValidate) || nextExpansionCode.getDisplay().equals(theDisplayToValidate))) {
|
||||
return new CodeValidationResult()
|
||||
CodeValidationResult codeValidationResult = new CodeValidationResult()
|
||||
.setCode(theCodeToValidate)
|
||||
.setDisplay(nextExpansionCode.getDisplay())
|
||||
.setCodeSystemName(codeSystemResourceName)
|
||||
.setCodeSystemVersion(codeSystemResourceVersion);
|
||||
.setCodeSystemVersion(csVersion);
|
||||
if (isNotBlank(theValueSetUrl)) {
|
||||
codeValidationResult.setMessage("Code was validated against in-memory expansion of ValueSet: " + theValueSetUrl);
|
||||
}
|
||||
return codeValidationResult;
|
||||
} else {
|
||||
String message = "Concept Display \"" + theDisplayToValidate + "\" does not match expected \"" + nextExpansionCode.getDisplay() + "\"";
|
||||
if (isNotBlank(theValueSetUrl)) {
|
||||
message += " for in-memory expansion of ValueSet: " + theValueSetUrl;
|
||||
}
|
||||
return new CodeValidationResult()
|
||||
.setSeverity(IssueSeverity.ERROR)
|
||||
.setDisplay(nextExpansionCode.getDisplay())
|
||||
.setMessage("Concept Display \"" + theDisplayToValidate + "\" does not match expected \"" + nextExpansionCode.getDisplay() + "\"")
|
||||
.setMessage(message)
|
||||
.setCodeSystemName(codeSystemResourceName)
|
||||
.setCodeSystemVersion(codeSystemResourceVersion);
|
||||
.setCodeSystemVersion(csVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -338,6 +380,9 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
|||
severity = ValidationMessage.IssueSeverity.ERROR;
|
||||
message = "Unknown code '" + (isNotBlank(theCodeSystemUrlAndVersionToValidate) ? theCodeSystemUrlAndVersionToValidate + "#" : "") + theCodeToValidate + "'";
|
||||
}
|
||||
if (isNotBlank(theValueSetUrl)) {
|
||||
message += " for in-memory expansion of ValueSet '" + theValueSetUrl + "'";
|
||||
}
|
||||
|
||||
return new CodeValidationResult()
|
||||
.setSeverityCode(severity.toCode())
|
||||
|
@ -346,52 +391,27 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
|||
|
||||
@Override
|
||||
public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theDisplayLanguage) {
|
||||
return validateCode(theValidationSupportContext, new ConceptValidationOptions(), theSystem, theCode, null, null).asLookupCodeResult(theSystem, theCode);
|
||||
CodeValidationResult codeValidationResult = validateCode(theValidationSupportContext, new ConceptValidationOptions(), theSystem, theCode, null, null);
|
||||
if (codeValidationResult == null) {
|
||||
return null;
|
||||
}
|
||||
return codeValidationResult.asLookupCodeResult(theSystem, theCode);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private org.hl7.fhir.r5.model.ValueSet expandValueSetDstu2Hl7Org(ValidationSupportContext theValidationSupportContext, ValueSet theInput, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) {
|
||||
Function<String, CodeSystem> codeSystemLoader = t -> {
|
||||
org.hl7.fhir.dstu2.model.ValueSet codeSystem = (org.hl7.fhir.dstu2.model.ValueSet) theValidationSupportContext.getRootValidationSupport().fetchCodeSystem(t);
|
||||
CodeSystem retVal = new CodeSystem();
|
||||
addCodesDstu2Hl7Org(codeSystem.getCodeSystem().getConcept(), retVal.getConcept());
|
||||
return retVal;
|
||||
};
|
||||
Function<String, org.hl7.fhir.r5.model.ValueSet> valueSetLoader = t -> {
|
||||
org.hl7.fhir.dstu2.model.ValueSet valueSet = (org.hl7.fhir.dstu2.model.ValueSet) theValidationSupportContext.getRootValidationSupport().fetchValueSet(t);
|
||||
return (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_10_50.convertResource(valueSet, new BaseAdvisor_10_50(false));
|
||||
};
|
||||
|
||||
private org.hl7.fhir.r5.model.ValueSet expandValueSetDstu2Hl7Org(ValidationSupportContext theValidationSupportContext, ValueSet theInput, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) throws ExpansionCouldNotBeCompletedInternallyException {
|
||||
org.hl7.fhir.r5.model.ValueSet input = (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_10_50.convertResource(theInput, new BaseAdvisor_10_50(false));
|
||||
org.hl7.fhir.r5.model.ValueSet output = expandValueSetR5(theValidationSupportContext, input, codeSystemLoader, valueSetLoader, theWantSystemUrlAndVersion, theWantCode);
|
||||
return (output);
|
||||
return (expandValueSetR5(theValidationSupportContext, input, theWantSystemUrlAndVersion, theWantCode));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private org.hl7.fhir.r5.model.ValueSet expandValueSetDstu2(ValidationSupportContext theValidationSupportContext, ca.uhn.fhir.model.dstu2.resource.ValueSet theInput, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) {
|
||||
private org.hl7.fhir.r5.model.ValueSet expandValueSetDstu2(ValidationSupportContext theValidationSupportContext, ca.uhn.fhir.model.dstu2.resource.ValueSet theInput, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) throws ExpansionCouldNotBeCompletedInternallyException {
|
||||
IParser parserRi = FhirContext.forCached(FhirVersionEnum.DSTU2_HL7ORG).newJsonParser();
|
||||
IParser parserHapi = FhirContext.forDstu2Cached().newJsonParser();
|
||||
|
||||
Function<String, CodeSystem> codeSystemLoader = t -> {
|
||||
ca.uhn.fhir.model.dstu2.resource.ValueSet codeSystem = theInput;
|
||||
CodeSystem retVal = null;
|
||||
if (codeSystem != null) {
|
||||
retVal = new CodeSystem();
|
||||
retVal.setUrl(codeSystem.getUrl());
|
||||
addCodesDstu2(codeSystem.getCodeSystem().getConcept(), retVal.getConcept());
|
||||
}
|
||||
return retVal;
|
||||
};
|
||||
Function<String, org.hl7.fhir.r5.model.ValueSet> valueSetLoader = t -> {
|
||||
ca.uhn.fhir.model.dstu2.resource.ValueSet valueSet = (ca.uhn.fhir.model.dstu2.resource.ValueSet) theValidationSupportContext.getRootValidationSupport().fetchValueSet(t);
|
||||
org.hl7.fhir.dstu2.model.ValueSet valueSetRi = parserRi.parseResource(org.hl7.fhir.dstu2.model.ValueSet.class, parserHapi.encodeResourceToString(valueSet));
|
||||
return (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_10_50.convertResource(valueSetRi, new BaseAdvisor_10_50(false));
|
||||
};
|
||||
|
||||
org.hl7.fhir.dstu2.model.ValueSet valueSetRi = parserRi.parseResource(org.hl7.fhir.dstu2.model.ValueSet.class, parserHapi.encodeResourceToString(theInput));
|
||||
org.hl7.fhir.r5.model.ValueSet input = (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_10_50.convertResource(valueSetRi, new BaseAdvisor_10_50(false));
|
||||
org.hl7.fhir.r5.model.ValueSet output = expandValueSetR5(theValidationSupportContext, input, codeSystemLoader, valueSetLoader, theWantSystemUrlAndVersion, theWantCode);
|
||||
return (output);
|
||||
return (expandValueSetR5(theValidationSupportContext, input, theWantSystemUrlAndVersion, theWantCode));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -437,55 +457,28 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
|||
}
|
||||
|
||||
@Nullable
|
||||
private org.hl7.fhir.r5.model.ValueSet expandValueSetDstu3(ValidationSupportContext theValidationSupportContext, org.hl7.fhir.dstu3.model.ValueSet theInput, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) {
|
||||
Function<String, org.hl7.fhir.r5.model.CodeSystem> codeSystemLoader = t -> {
|
||||
org.hl7.fhir.dstu3.model.CodeSystem codeSystem = (org.hl7.fhir.dstu3.model.CodeSystem) theValidationSupportContext.getRootValidationSupport().fetchCodeSystem(t);
|
||||
return (CodeSystem) VersionConvertorFactory_30_50.convertResource(codeSystem, new BaseAdvisor_30_50(false));
|
||||
};
|
||||
Function<String, org.hl7.fhir.r5.model.ValueSet> valueSetLoader = t -> {
|
||||
org.hl7.fhir.dstu3.model.ValueSet valueSet = (org.hl7.fhir.dstu3.model.ValueSet) theValidationSupportContext.getRootValidationSupport().fetchValueSet(t);
|
||||
return (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_30_50.convertResource(valueSet, new BaseAdvisor_30_50(false));
|
||||
};
|
||||
|
||||
private org.hl7.fhir.r5.model.ValueSet expandValueSetDstu3(ValidationSupportContext theValidationSupportContext, org.hl7.fhir.dstu3.model.ValueSet theInput, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) throws ExpansionCouldNotBeCompletedInternallyException {
|
||||
org.hl7.fhir.r5.model.ValueSet input = (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_30_50.convertResource(theInput, new BaseAdvisor_30_50(false));
|
||||
org.hl7.fhir.r5.model.ValueSet output = expandValueSetR5(theValidationSupportContext, input, codeSystemLoader, valueSetLoader, theWantSystemUrlAndVersion, theWantCode);
|
||||
return (output);
|
||||
return (expandValueSetR5(theValidationSupportContext, input, theWantSystemUrlAndVersion, theWantCode));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private org.hl7.fhir.r5.model.ValueSet expandValueSetR4(ValidationSupportContext theValidationSupportContext, org.hl7.fhir.r4.model.ValueSet theInput, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) {
|
||||
Function<String, org.hl7.fhir.r5.model.CodeSystem> codeSystemLoader = t -> {
|
||||
org.hl7.fhir.r4.model.CodeSystem codeSystem = (org.hl7.fhir.r4.model.CodeSystem) theValidationSupportContext.getRootValidationSupport().fetchCodeSystem(t);
|
||||
return (CodeSystem) VersionConvertorFactory_40_50.convertResource(codeSystem, new BaseAdvisor_40_50(false));
|
||||
};
|
||||
Function<String, org.hl7.fhir.r5.model.ValueSet> valueSetLoader = t -> {
|
||||
org.hl7.fhir.r4.model.ValueSet valueSet = (org.hl7.fhir.r4.model.ValueSet) theValidationSupportContext.getRootValidationSupport().fetchValueSet(t);
|
||||
return (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_40_50.convertResource(valueSet, new BaseAdvisor_40_50(false));
|
||||
};
|
||||
|
||||
private org.hl7.fhir.r5.model.ValueSet expandValueSetR4(ValidationSupportContext theValidationSupportContext, org.hl7.fhir.r4.model.ValueSet theInput, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) throws ExpansionCouldNotBeCompletedInternallyException {
|
||||
org.hl7.fhir.r5.model.ValueSet input = (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_40_50.convertResource(theInput, new BaseAdvisor_40_50(false));
|
||||
org.hl7.fhir.r5.model.ValueSet output = expandValueSetR5(theValidationSupportContext, input, codeSystemLoader, valueSetLoader, theWantSystemUrlAndVersion, theWantCode);
|
||||
return (output);
|
||||
return expandValueSetR5(theValidationSupportContext, input, theWantSystemUrlAndVersion, theWantCode);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private org.hl7.fhir.r5.model.ValueSet expandValueSetR5(ValidationSupportContext theValidationSupportContext, org.hl7.fhir.r5.model.ValueSet theInput) {
|
||||
Function<String, org.hl7.fhir.r5.model.CodeSystem> codeSystemLoader = t -> (org.hl7.fhir.r5.model.CodeSystem) theValidationSupportContext.getRootValidationSupport().fetchCodeSystem(t);
|
||||
Function<String, org.hl7.fhir.r5.model.ValueSet> valueSetLoader = t -> (org.hl7.fhir.r5.model.ValueSet) theValidationSupportContext.getRootValidationSupport().fetchValueSet(t);
|
||||
|
||||
return expandValueSetR5(theValidationSupportContext, theInput, codeSystemLoader, valueSetLoader, null, null);
|
||||
private org.hl7.fhir.r5.model.ValueSet expandValueSetR5(ValidationSupportContext theValidationSupportContext, org.hl7.fhir.r5.model.ValueSet theInput) throws ExpansionCouldNotBeCompletedInternallyException {
|
||||
return expandValueSetR5(theValidationSupportContext, theInput, null, null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private org.hl7.fhir.r5.model.ValueSet expandValueSetR5(ValidationSupportContext theValidationSupportContext, org.hl7.fhir.r5.model.ValueSet theInput, Function<String, CodeSystem> theCodeSystemLoader, Function<String, org.hl7.fhir.r5.model.ValueSet> theValueSetLoader, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) {
|
||||
private org.hl7.fhir.r5.model.ValueSet expandValueSetR5(ValidationSupportContext theValidationSupportContext, org.hl7.fhir.r5.model.ValueSet theInput, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) throws ExpansionCouldNotBeCompletedInternallyException {
|
||||
Set<FhirVersionIndependentConcept> concepts = new HashSet<>();
|
||||
|
||||
try {
|
||||
expandValueSetR5IncludeOrExclude(theValidationSupportContext, concepts, theCodeSystemLoader, theValueSetLoader, theInput.getCompose().getInclude(), true, theWantSystemUrlAndVersion, theWantCode);
|
||||
expandValueSetR5IncludeOrExclude(theValidationSupportContext, concepts, theCodeSystemLoader, theValueSetLoader, theInput.getCompose().getExclude(), false, theWantSystemUrlAndVersion, theWantCode);
|
||||
} catch (ExpansionCouldNotBeCompletedInternallyException e) {
|
||||
return null;
|
||||
}
|
||||
expandValueSetR5IncludeOrExcludes(theValidationSupportContext, concepts, theInput.getCompose().getInclude(), true, theWantSystemUrlAndVersion, theWantCode);
|
||||
expandValueSetR5IncludeOrExcludes(theValidationSupportContext, concepts, theInput.getCompose().getExclude(), false, theWantSystemUrlAndVersion, theWantCode);
|
||||
|
||||
org.hl7.fhir.r5.model.ValueSet retVal = new org.hl7.fhir.r5.model.ValueSet();
|
||||
for (FhirVersionIndependentConcept next : concepts) {
|
||||
|
@ -499,130 +492,308 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
|||
return retVal;
|
||||
}
|
||||
|
||||
private void expandValueSetR5IncludeOrExclude(ValidationSupportContext theValidationSupportContext, Set<FhirVersionIndependentConcept> theConcepts, Function<String, CodeSystem> theCodeSystemLoader, Function<String, org.hl7.fhir.r5.model.ValueSet> theValueSetLoader, List<org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent> theComposeList, boolean theComposeListIsInclude, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) throws ExpansionCouldNotBeCompletedInternallyException {
|
||||
/**
|
||||
* Use with caution - this is not a stable API
|
||||
*
|
||||
* @since 5.6.0
|
||||
*/
|
||||
public void expandValueSetIncludeOrExclude(ValidationSupportContext theValidationSupportContext, Consumer<FhirVersionIndependentConcept> theConsumer, org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent theIncludeOrExclude) throws ExpansionCouldNotBeCompletedInternallyException {
|
||||
expandValueSetR5IncludeOrExclude(theValidationSupportContext, theConsumer, null, null, theIncludeOrExclude);
|
||||
}
|
||||
|
||||
|
||||
private void expandValueSetR5IncludeOrExcludes(ValidationSupportContext theValidationSupportContext, Set<FhirVersionIndependentConcept> theConcepts, List<org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent> theComposeList, boolean theComposeListIsInclude, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) throws ExpansionCouldNotBeCompletedInternallyException {
|
||||
Consumer<FhirVersionIndependentConcept> consumer = c -> {
|
||||
if (theComposeListIsInclude) {
|
||||
theConcepts.add(c);
|
||||
} else {
|
||||
theConcepts.remove(c);
|
||||
}
|
||||
};
|
||||
expandValueSetR5IncludeOrExcludes(theValidationSupportContext, consumer, theComposeList, theWantSystemUrlAndVersion, theWantCode);
|
||||
}
|
||||
|
||||
|
||||
private void expandValueSetR5IncludeOrExcludes(ValidationSupportContext theValidationSupportContext, Consumer<FhirVersionIndependentConcept> theConsumer, List<org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent> theComposeList, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) throws ExpansionCouldNotBeCompletedInternallyException {
|
||||
ExpansionCouldNotBeCompletedInternallyException caughtException = null;
|
||||
for (org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent nextInclude : theComposeList) {
|
||||
try {
|
||||
boolean outcome = expandValueSetR5IncludeOrExclude(theValidationSupportContext, theConsumer, theWantSystemUrlAndVersion, theWantCode, nextInclude);
|
||||
if (isNotBlank(theWantCode)) {
|
||||
if (outcome) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (ExpansionCouldNotBeCompletedInternallyException e) {
|
||||
if (isBlank(theWantCode)) {
|
||||
throw e;
|
||||
} else {
|
||||
caughtException = e;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (caughtException != null) {
|
||||
throw caughtException;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if at least one code was addded
|
||||
*/
|
||||
private boolean expandValueSetR5IncludeOrExclude(ValidationSupportContext theValidationSupportContext, Consumer<FhirVersionIndependentConcept> theConsumer, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode, org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent theInclude) throws ExpansionCouldNotBeCompletedInternallyException {
|
||||
String wantSystemUrl = null;
|
||||
String wantSystemVersion = null;
|
||||
if (theWantSystemUrlAndVersion != null) {
|
||||
int versionIndex = theWantSystemUrlAndVersion.indexOf("|");
|
||||
if (versionIndex > -1) {
|
||||
wantSystemUrl = theWantSystemUrlAndVersion.substring(0,versionIndex);
|
||||
wantSystemVersion = theWantSystemUrlAndVersion.substring(versionIndex+1);
|
||||
wantSystemUrl = theWantSystemUrlAndVersion.substring(0, versionIndex);
|
||||
wantSystemVersion = theWantSystemUrlAndVersion.substring(versionIndex + 1);
|
||||
} else {
|
||||
wantSystemUrl = theWantSystemUrlAndVersion;
|
||||
}
|
||||
}
|
||||
|
||||
for (org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent nextInclude : theComposeList) {
|
||||
Function<String, CodeSystem> codeSystemLoader = newCodeSystemLoader(theValidationSupportContext);
|
||||
Function<String, org.hl7.fhir.r5.model.ValueSet> valueSetLoader = newValueSetLoader(theValidationSupportContext);
|
||||
|
||||
List<FhirVersionIndependentConcept> nextCodeList = new ArrayList<>();
|
||||
String includeOrExcludeConceptSystemUrl = nextInclude.getSystem();
|
||||
String includeOrExcludeConceptSystemVersion = nextInclude.getVersion();
|
||||
if (isNotBlank(includeOrExcludeConceptSystemUrl)) {
|
||||
List<FhirVersionIndependentConcept> nextCodeList = new ArrayList<>();
|
||||
String includeOrExcludeConceptSystemUrl = theInclude.getSystem();
|
||||
String includeOrExcludeConceptSystemVersion = theInclude.getVersion();
|
||||
CodeSystem includeOrExcludeSystemResource = null;
|
||||
if (isNotBlank(includeOrExcludeConceptSystemUrl)) {
|
||||
|
||||
if (wantSystemUrl != null && !wantSystemUrl.equals(includeOrExcludeConceptSystemUrl)) {
|
||||
continue;
|
||||
}
|
||||
if (wantSystemUrl != null && !wantSystemUrl.equals(includeOrExcludeConceptSystemUrl)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (wantSystemVersion != null && !wantSystemVersion.equals(includeOrExcludeConceptSystemVersion)) {
|
||||
continue;
|
||||
}
|
||||
if (wantSystemVersion != null && !wantSystemVersion.equals(includeOrExcludeConceptSystemVersion)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CodeSystem includeOrExcludeSystemResource;
|
||||
if (includeOrExcludeConceptSystemVersion != null) {
|
||||
includeOrExcludeSystemResource = theCodeSystemLoader.apply(includeOrExcludeConceptSystemUrl + "|" + includeOrExcludeConceptSystemVersion);
|
||||
} else {
|
||||
includeOrExcludeSystemResource = theCodeSystemLoader.apply(includeOrExcludeConceptSystemUrl);
|
||||
}
|
||||
String loadedCodeSystemUrl;
|
||||
if (includeOrExcludeConceptSystemVersion != null) {
|
||||
loadedCodeSystemUrl = includeOrExcludeConceptSystemUrl + "|" + includeOrExcludeConceptSystemVersion;
|
||||
} else {
|
||||
loadedCodeSystemUrl = includeOrExcludeConceptSystemUrl;
|
||||
}
|
||||
|
||||
Set<String> wantCodes;
|
||||
if (nextInclude.getConcept().isEmpty()) {
|
||||
wantCodes = null;
|
||||
} else {
|
||||
wantCodes = nextInclude
|
||||
.getConcept()
|
||||
.stream().map(t -> t.getCode()).collect(Collectors.toSet());
|
||||
}
|
||||
includeOrExcludeSystemResource = codeSystemLoader.apply(loadedCodeSystemUrl);
|
||||
|
||||
boolean ableToHandleCode = false;
|
||||
if (includeOrExcludeSystemResource == null || includeOrExcludeSystemResource.getContent() == CodeSystem.CodeSystemContentMode.NOTPRESENT) {
|
||||
Set<String> wantCodes;
|
||||
if (theInclude.getConcept().isEmpty()) {
|
||||
wantCodes = null;
|
||||
} else {
|
||||
wantCodes = theInclude
|
||||
.getConcept()
|
||||
.stream().map(t -> t.getCode()).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
if (theWantCode != null) {
|
||||
if (theValidationSupportContext.getRootValidationSupport().isCodeSystemSupported(theValidationSupportContext, includeOrExcludeConceptSystemUrl)) {
|
||||
LookupCodeResult lookup = theValidationSupportContext.getRootValidationSupport().lookupCode(theValidationSupportContext, includeOrExcludeConceptSystemUrl, theWantCode, null);
|
||||
if (lookup != null && lookup.isFound()) {
|
||||
boolean ableToHandleCode = false;
|
||||
String failureMessage = null;
|
||||
FailureType failureType = FailureType.OTHER;
|
||||
|
||||
if (includeOrExcludeSystemResource == null || includeOrExcludeSystemResource.getContent() == CodeSystem.CodeSystemContentMode.NOTPRESENT) {
|
||||
|
||||
if (theWantCode != null) {
|
||||
if (theValidationSupportContext.getRootValidationSupport().isCodeSystemSupported(theValidationSupportContext, includeOrExcludeConceptSystemUrl)) {
|
||||
LookupCodeResult lookup = theValidationSupportContext.getRootValidationSupport().lookupCode(theValidationSupportContext, includeOrExcludeConceptSystemUrl, theWantCode, null);
|
||||
if (lookup != null && lookup.isFound()) {
|
||||
CodeSystem.ConceptDefinitionComponent conceptDefinition = new CodeSystem.ConceptDefinitionComponent()
|
||||
.addConcept()
|
||||
.setCode(theWantCode)
|
||||
.setDisplay(lookup.getCodeDisplay());
|
||||
List<CodeSystem.ConceptDefinitionComponent> codesList = Collections.singletonList(conceptDefinition);
|
||||
addCodes(includeOrExcludeConceptSystemUrl, includeOrExcludeConceptSystemVersion, codesList, nextCodeList, wantCodes);
|
||||
ableToHandleCode = true;
|
||||
}
|
||||
} else {
|
||||
|
||||
/*
|
||||
* If we're doing an expansion specifically looking for a single code, that means we're validating that code.
|
||||
* In the case where we have a ValueSet that explicitly enumerates a collection of codes
|
||||
* (via ValueSet.compose.include.code) in a code system that is unknown we'll assume the code is valid
|
||||
* even if we can't find the CodeSystem. This is a compromise obviously, since it would be ideal for
|
||||
* CodeSystems to always be known, but realistically there are always going to be CodeSystems that
|
||||
* can't be supplied because of copyright issues, or because they are grammar based. Allowing a VS to
|
||||
* enumerate a set of good codes for them is a nice compromise there.
|
||||
*/
|
||||
if (Objects.equals(theInclude.getSystem(), theWantSystemUrlAndVersion)) {
|
||||
Optional<org.hl7.fhir.r5.model.ValueSet.ConceptReferenceComponent> matchingEnumeratedConcept = theInclude.getConcept().stream().filter(t -> Objects.equals(t.getCode(), theWantCode)).findFirst();
|
||||
|
||||
// If the ValueSet.compose.include has no individual concepts in it, and
|
||||
// we can't find the actual referenced CodeSystem, we have no choice
|
||||
// but to fail
|
||||
if (!theInclude.getConcept().isEmpty()) {
|
||||
ableToHandleCode = true;
|
||||
} else {
|
||||
failureMessage = getFailureMessageForMissingOrUnusableCodeSystem(includeOrExcludeSystemResource, loadedCodeSystemUrl);
|
||||
}
|
||||
|
||||
if (matchingEnumeratedConcept.isPresent()) {
|
||||
CodeSystem.ConceptDefinitionComponent conceptDefinition = new CodeSystem.ConceptDefinitionComponent()
|
||||
.addConcept()
|
||||
.setCode(theWantCode)
|
||||
.setDisplay(lookup.getCodeDisplay());
|
||||
.setDisplay(matchingEnumeratedConcept.get().getDisplay());
|
||||
List<CodeSystem.ConceptDefinitionComponent> codesList = Collections.singletonList(conceptDefinition);
|
||||
addCodes(includeOrExcludeConceptSystemUrl, includeOrExcludeConceptSystemVersion, codesList, nextCodeList, wantCodes);
|
||||
ableToHandleCode = true;
|
||||
}
|
||||
} else if (theComposeListIsInclude) {
|
||||
|
||||
/*
|
||||
* If we're doing an expansion specifically looking for a single code, that means we're validating that code.
|
||||
* In the case where we have a ValueSet that explicitly enumerates a collection of codes
|
||||
* (via ValueSet.compose.include.code) in a code system that is unknown we'll assume the code is valid
|
||||
* even iof we can't find the CodeSystem. This is a compromise obviously, since it would be ideal for
|
||||
* CodeSystems to always be known, but realistically there are always going to be CodeSystems that
|
||||
* can't be supplied because of copyright issues, or because they are grammar based. Allowing a VS to
|
||||
* enumerate a set of good codes for them is a nice compromise there.
|
||||
*/
|
||||
for (org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent next : theComposeList) {
|
||||
if (Objects.equals(next.getSystem(), theWantSystemUrlAndVersion)) {
|
||||
Optional<org.hl7.fhir.r5.model.ValueSet.ConceptReferenceComponent> matchingEnumeratedConcept = next.getConcept().stream().filter(t -> Objects.equals(t.getCode(), theWantCode)).findFirst();
|
||||
if (matchingEnumeratedConcept.isPresent()) {
|
||||
CodeSystem.ConceptDefinitionComponent conceptDefinition = new CodeSystem.ConceptDefinitionComponent()
|
||||
.addConcept()
|
||||
.setCode(theWantCode)
|
||||
.setDisplay(matchingEnumeratedConcept.get().getDisplay());
|
||||
List<CodeSystem.ConceptDefinitionComponent> codesList = Collections.singletonList(conceptDefinition);
|
||||
addCodes(includeOrExcludeConceptSystemUrl, includeOrExcludeConceptSystemVersion, codesList, nextCodeList, wantCodes);
|
||||
ableToHandleCode = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
ableToHandleCode = true;
|
||||
}
|
||||
|
||||
if (!ableToHandleCode) {
|
||||
throw new ExpansionCouldNotBeCompletedInternallyException();
|
||||
}
|
||||
|
||||
if (includeOrExcludeSystemResource != null && includeOrExcludeSystemResource.getContent() != CodeSystem.CodeSystemContentMode.NOTPRESENT) {
|
||||
addCodes(includeOrExcludeConceptSystemUrl, includeOrExcludeConceptSystemVersion, includeOrExcludeSystemResource.getConcept(), nextCodeList, wantCodes);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (CanonicalType nextValueSetInclude : nextInclude.getValueSet()) {
|
||||
org.hl7.fhir.r5.model.ValueSet vs = theValueSetLoader.apply(nextValueSetInclude.getValueAsString());
|
||||
if (vs != null) {
|
||||
org.hl7.fhir.r5.model.ValueSet subExpansion = expandValueSetR5(theValidationSupportContext, vs, theCodeSystemLoader, theValueSetLoader, theWantSystemUrlAndVersion, theWantCode);
|
||||
if (subExpansion == null) {
|
||||
throw new ExpansionCouldNotBeCompletedInternallyException();
|
||||
if (isNotBlank(theInclude.getSystem()) && !theInclude.getConcept().isEmpty() && theInclude.getFilter().isEmpty() && theInclude.getValueSet().isEmpty()) {
|
||||
theInclude
|
||||
.getConcept()
|
||||
.stream()
|
||||
.map(t -> new FhirVersionIndependentConcept(theInclude.getSystem(), t.getCode(), t.getDisplay(), theInclude.getVersion()))
|
||||
.forEach(t -> nextCodeList.add(t));
|
||||
ableToHandleCode = true;
|
||||
}
|
||||
for (org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent next : subExpansion.getExpansion().getContains()) {
|
||||
nextCodeList.add(new FhirVersionIndependentConcept(next.getSystem(), next.getCode(), next.getDisplay(), next.getVersion()));
|
||||
|
||||
if (!ableToHandleCode) {
|
||||
failureMessage = getFailureMessageForMissingOrUnusableCodeSystem(includeOrExcludeSystemResource, loadedCodeSystemUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (theComposeListIsInclude) {
|
||||
theConcepts.addAll(nextCodeList);
|
||||
} else {
|
||||
theConcepts.removeAll(nextCodeList);
|
||||
ableToHandleCode = true;
|
||||
}
|
||||
|
||||
if (!ableToHandleCode) {
|
||||
if (includeOrExcludeSystemResource == null && failureMessage == null) {
|
||||
failureMessage = getFailureMessageForMissingOrUnusableCodeSystem(includeOrExcludeSystemResource, loadedCodeSystemUrl);
|
||||
}
|
||||
|
||||
if (includeOrExcludeSystemResource == null) {
|
||||
failureType = FailureType.UNKNOWN_CODE_SYSTEM;
|
||||
}
|
||||
|
||||
throw new ExpansionCouldNotBeCompletedInternallyException(failureMessage, failureType);
|
||||
}
|
||||
|
||||
if (includeOrExcludeSystemResource != null && includeOrExcludeSystemResource.getContent() != CodeSystem.CodeSystemContentMode.NOTPRESENT) {
|
||||
addCodes(includeOrExcludeConceptSystemUrl, includeOrExcludeConceptSystemVersion, includeOrExcludeSystemResource.getConcept(), nextCodeList, wantCodes);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (CanonicalType nextValueSetInclude : theInclude.getValueSet()) {
|
||||
org.hl7.fhir.r5.model.ValueSet vs = valueSetLoader.apply(nextValueSetInclude.getValueAsString());
|
||||
if (vs != null) {
|
||||
org.hl7.fhir.r5.model.ValueSet subExpansion = expandValueSetR5(theValidationSupportContext, vs, theWantSystemUrlAndVersion, theWantCode);
|
||||
if (subExpansion == null) {
|
||||
throw new ExpansionCouldNotBeCompletedInternallyException("Failed to expand ValueSet: " + nextValueSetInclude.getValueAsString(), FailureType.OTHER);
|
||||
}
|
||||
for (org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent next : subExpansion.getExpansion().getContains()) {
|
||||
nextCodeList.add(new FhirVersionIndependentConcept(next.getSystem(), next.getCode(), next.getDisplay(), next.getVersion()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean retVal = false;
|
||||
|
||||
for (FhirVersionIndependentConcept next : nextCodeList) {
|
||||
if (includeOrExcludeSystemResource != null && theWantCode != null) {
|
||||
boolean matches;
|
||||
if (includeOrExcludeSystemResource.getCaseSensitive()) {
|
||||
matches = theWantCode.equals(next.getCode());
|
||||
} else {
|
||||
matches = theWantCode.equalsIgnoreCase(next.getCode());
|
||||
}
|
||||
if (!matches) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
theConsumer.accept(next);
|
||||
retVal = true;
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private Function<String, org.hl7.fhir.r5.model.ValueSet> newValueSetLoader(ValidationSupportContext theValidationSupportContext) {
|
||||
switch (myCtx.getVersion().getVersion()) {
|
||||
case DSTU2:
|
||||
case DSTU2_HL7ORG:
|
||||
return t -> {
|
||||
IBaseResource vs = theValidationSupportContext.getRootValidationSupport().fetchValueSet(t);
|
||||
if (vs instanceof ca.uhn.fhir.model.dstu2.resource.ValueSet) {
|
||||
IParser parserRi = FhirContext.forCached(FhirVersionEnum.DSTU2_HL7ORG).newJsonParser();
|
||||
IParser parserHapi = FhirContext.forDstu2Cached().newJsonParser();
|
||||
ca.uhn.fhir.model.dstu2.resource.ValueSet valueSet = (ca.uhn.fhir.model.dstu2.resource.ValueSet) vs;
|
||||
org.hl7.fhir.dstu2.model.ValueSet valueSetRi = parserRi.parseResource(org.hl7.fhir.dstu2.model.ValueSet.class, parserHapi.encodeResourceToString(valueSet));
|
||||
return (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_10_50.convertResource(valueSetRi, new BaseAdvisor_10_50(false));
|
||||
} else {
|
||||
org.hl7.fhir.dstu2.model.ValueSet valueSet = (org.hl7.fhir.dstu2.model.ValueSet) theValidationSupportContext.getRootValidationSupport().fetchValueSet(t);
|
||||
return (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_10_50.convertResource(valueSet, new BaseAdvisor_10_50(false));
|
||||
}
|
||||
};
|
||||
case DSTU3:
|
||||
return t -> {
|
||||
org.hl7.fhir.dstu3.model.ValueSet valueSet = (org.hl7.fhir.dstu3.model.ValueSet) theValidationSupportContext.getRootValidationSupport().fetchValueSet(t);
|
||||
return (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_30_50.convertResource(valueSet, new BaseAdvisor_30_50(false));
|
||||
};
|
||||
case R4:
|
||||
return t -> {
|
||||
org.hl7.fhir.r4.model.ValueSet valueSet = (org.hl7.fhir.r4.model.ValueSet) theValidationSupportContext.getRootValidationSupport().fetchValueSet(t);
|
||||
return (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_40_50.convertResource(valueSet, new BaseAdvisor_40_50(false));
|
||||
};
|
||||
default:
|
||||
case DSTU2_1:
|
||||
case R5:
|
||||
return t -> (org.hl7.fhir.r5.model.ValueSet) theValidationSupportContext.getRootValidationSupport().fetchValueSet(t);
|
||||
}
|
||||
}
|
||||
|
||||
private Function<String, CodeSystem> newCodeSystemLoader(ValidationSupportContext theValidationSupportContext) {
|
||||
switch (myCtx.getVersion().getVersion()) {
|
||||
case DSTU2:
|
||||
case DSTU2_HL7ORG:
|
||||
return t -> {
|
||||
IBaseResource codeSystem = theValidationSupportContext.getRootValidationSupport().fetchCodeSystem(t);
|
||||
CodeSystem retVal = null;
|
||||
if (codeSystem != null) {
|
||||
retVal = new CodeSystem();
|
||||
if (codeSystem instanceof ca.uhn.fhir.model.dstu2.resource.ValueSet) {
|
||||
ca.uhn.fhir.model.dstu2.resource.ValueSet codeSystemCasted = (ca.uhn.fhir.model.dstu2.resource.ValueSet) codeSystem;
|
||||
retVal.setUrl(codeSystemCasted.getUrl());
|
||||
addCodesDstu2(codeSystemCasted.getCodeSystem().getConcept(), retVal.getConcept());
|
||||
} else {
|
||||
org.hl7.fhir.dstu2.model.ValueSet codeSystemCasted = (org.hl7.fhir.dstu2.model.ValueSet) codeSystem;
|
||||
retVal.setUrl(codeSystemCasted.getUrl());
|
||||
addCodesDstu2Hl7Org(codeSystemCasted.getCodeSystem().getConcept(), retVal.getConcept());
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
};
|
||||
case DSTU3:
|
||||
return t -> {
|
||||
org.hl7.fhir.dstu3.model.CodeSystem codeSystem = (org.hl7.fhir.dstu3.model.CodeSystem) theValidationSupportContext.getRootValidationSupport().fetchCodeSystem(t);
|
||||
return (CodeSystem) VersionConvertorFactory_30_50.convertResource(codeSystem, new BaseAdvisor_30_50(false));
|
||||
};
|
||||
case R4:
|
||||
return t -> {
|
||||
org.hl7.fhir.r4.model.CodeSystem codeSystem = (org.hl7.fhir.r4.model.CodeSystem) theValidationSupportContext.getRootValidationSupport().fetchCodeSystem(t);
|
||||
return (CodeSystem) VersionConvertorFactory_40_50.convertResource(codeSystem, new BaseAdvisor_40_50(false));
|
||||
};
|
||||
case DSTU2_1:
|
||||
case R5:
|
||||
default:
|
||||
return t -> (org.hl7.fhir.r5.model.CodeSystem) theValidationSupportContext.getRootValidationSupport().fetchCodeSystem(t);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private String getFailureMessageForMissingOrUnusableCodeSystem(CodeSystem includeOrExcludeSystemResource, String loadedCodeSystemUrl) {
|
||||
String failureMessage;
|
||||
if (includeOrExcludeSystemResource == null) {
|
||||
failureMessage = "Unable to expand ValueSet because CodeSystem could not be found: " + loadedCodeSystemUrl;
|
||||
} else {
|
||||
assert includeOrExcludeSystemResource.getContent() == CodeSystem.CodeSystemContentMode.NOTPRESENT;
|
||||
failureMessage = "Unable to expand ValueSet because CodeSystem has CodeSystem.content=not-present but contents were not found: " + loadedCodeSystemUrl;
|
||||
}
|
||||
return failureMessage;
|
||||
}
|
||||
|
||||
private void addCodes(String theCodeSystemUrl, String theCodeSystemVersion, List<CodeSystem.ConceptDefinitionComponent> theSource, List<FhirVersionIndependentConcept> theTarget, Set<String> theCodeFilter) {
|
||||
|
@ -636,8 +807,27 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
|||
}
|
||||
}
|
||||
|
||||
private static class ExpansionCouldNotBeCompletedInternallyException extends Exception {
|
||||
|
||||
public enum FailureType {
|
||||
|
||||
UNKNOWN_CODE_SYSTEM,
|
||||
OTHER
|
||||
|
||||
}
|
||||
|
||||
public static class ExpansionCouldNotBeCompletedInternallyException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = -2226561628771483085L;
|
||||
private final FailureType myFailureType;
|
||||
|
||||
public ExpansionCouldNotBeCompletedInternallyException(String theMessage, FailureType theFailureType) {
|
||||
super(theMessage);
|
||||
myFailureType = theFailureType;
|
||||
}
|
||||
|
||||
public FailureType getFailureType() {
|
||||
return myFailureType;
|
||||
}
|
||||
}
|
||||
|
||||
private static void flattenAndConvertCodesDstu2(List<org.hl7.fhir.dstu2.model.ValueSet.ValueSetExpansionContainsComponent> theInput, List<FhirVersionIndependentConcept> theFhirVersionIndependentConcepts) {
|
||||
|
|
|
@ -4,6 +4,7 @@ import ca.uhn.fhir.context.FhirContext;
|
|||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
||||
import org.apache.commons.compress.utils.Sets;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
@ -14,10 +15,13 @@ import org.hl7.fhir.r4.model.ValueSet;
|
|||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
|
@ -75,22 +79,39 @@ public class PrePopulatedValidationSupport extends BaseStaticResourceValidationS
|
|||
* </p>
|
||||
*/
|
||||
public void addCodeSystem(IBaseResource theCodeSystem) {
|
||||
String url = processResourceAndReturnUrl(theCodeSystem, "CodeSystem");
|
||||
addToMap(theCodeSystem, myCodeSystems, url);
|
||||
Set<String> urls = processResourceAndReturnUrls(theCodeSystem, "CodeSystem");
|
||||
addToMap(theCodeSystem, myCodeSystems, urls);
|
||||
}
|
||||
|
||||
private String processResourceAndReturnUrl(IBaseResource theCodeSystem, String theResourceName) {
|
||||
Validate.notNull(theCodeSystem, "the" + theResourceName + " must not be null");
|
||||
RuntimeResourceDefinition resourceDef = getFhirContext().getResourceDefinition(theCodeSystem);
|
||||
private Set<String> processResourceAndReturnUrls(IBaseResource theResource, String theResourceName) {
|
||||
Validate.notNull(theResource, "the" + theResourceName + " must not be null");
|
||||
RuntimeResourceDefinition resourceDef = getFhirContext().getResourceDefinition(theResource);
|
||||
String actualResourceName = resourceDef.getName();
|
||||
Validate.isTrue(actualResourceName.equals(theResourceName), "the" + theResourceName + " must be a " + theResourceName + " - Got: " + actualResourceName);
|
||||
|
||||
Optional<IBase> urlValue = resourceDef.getChildByName("url").getAccessor().getFirstValueOrNull(theCodeSystem);
|
||||
Optional<IBase> urlValue = resourceDef.getChildByName("url").getAccessor().getFirstValueOrNull(theResource);
|
||||
String url = urlValue.map(t -> (((IPrimitiveType<?>) t).getValueAsString())).orElse(null);
|
||||
|
||||
Validate.notNull(url, "the" + theResourceName + ".getUrl() must not return null");
|
||||
Validate.notBlank(url, "the" + theResourceName + ".getUrl() must return a value");
|
||||
return url;
|
||||
|
||||
String urlWithoutVersion;
|
||||
int pipeIdx = url.indexOf('|');
|
||||
if (pipeIdx != -1) {
|
||||
urlWithoutVersion = url.substring(0, pipeIdx);
|
||||
} else {
|
||||
urlWithoutVersion = url;
|
||||
}
|
||||
|
||||
HashSet<String> retVal = Sets.newHashSet(url, urlWithoutVersion);
|
||||
|
||||
Optional<IBase> versionValue = resourceDef.getChildByName("version").getAccessor().getFirstValueOrNull(theResource);
|
||||
String version = versionValue.map(t -> (((IPrimitiveType<?>) t).getValueAsString())).orElse(null);
|
||||
if (isNotBlank(version)) {
|
||||
retVal.add(urlWithoutVersion + "|" + version);
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -108,23 +129,24 @@ public class PrePopulatedValidationSupport extends BaseStaticResourceValidationS
|
|||
* </p>
|
||||
*/
|
||||
public void addStructureDefinition(IBaseResource theStructureDefinition) {
|
||||
String url = processResourceAndReturnUrl(theStructureDefinition, "StructureDefinition");
|
||||
Set<String> url = processResourceAndReturnUrls(theStructureDefinition, "StructureDefinition");
|
||||
addToMap(theStructureDefinition, myStructureDefinitions, url);
|
||||
}
|
||||
|
||||
private <T extends IBaseResource> void addToMap(T theResource, Map<String, T> theMap, String theUrl) {
|
||||
if (isNotBlank(theUrl)) {
|
||||
theMap.put(theUrl, theResource);
|
||||
private <T extends IBaseResource> void addToMap(T theResource, Map<String, T> theMap, Collection<String> theUrls) {
|
||||
for (String urls : theUrls) {
|
||||
if (isNotBlank(urls)) {
|
||||
theMap.put(urls, theResource);
|
||||
|
||||
int lastSlashIdx = theUrl.lastIndexOf('/');
|
||||
if (lastSlashIdx != -1) {
|
||||
theMap.put(theUrl.substring(lastSlashIdx + 1), theResource);
|
||||
int previousSlashIdx = theUrl.lastIndexOf('/', lastSlashIdx - 1);
|
||||
if (previousSlashIdx != -1) {
|
||||
theMap.put(theUrl.substring(previousSlashIdx + 1), theResource);
|
||||
int lastSlashIdx = urls.lastIndexOf('/');
|
||||
if (lastSlashIdx != -1) {
|
||||
theMap.put(urls.substring(lastSlashIdx + 1), theResource);
|
||||
int previousSlashIdx = urls.lastIndexOf('/', lastSlashIdx - 1);
|
||||
if (previousSlashIdx != -1) {
|
||||
theMap.put(urls.substring(previousSlashIdx + 1), theResource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -143,8 +165,8 @@ public class PrePopulatedValidationSupport extends BaseStaticResourceValidationS
|
|||
* </p>
|
||||
*/
|
||||
public void addValueSet(IBaseResource theValueSet) {
|
||||
String url = processResourceAndReturnUrl(theValueSet, "ValueSet");
|
||||
addToMap(theValueSet, myValueSets, url);
|
||||
Set<String> urls = processResourceAndReturnUrls(theValueSet, "ValueSet");
|
||||
addToMap(theValueSet, myValueSets, urls);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -37,19 +37,21 @@ public class UnknownCodeSystemWarningValidationSupport extends BaseValidationSup
|
|||
@Nullable
|
||||
@Override
|
||||
public CodeValidationResult validateCodeInValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) {
|
||||
if (!myAllowNonExistentCodeSystem) {
|
||||
return null;
|
||||
}
|
||||
if (theCodeSystem == null) {
|
||||
return null;
|
||||
}
|
||||
IBaseResource codeSystem = theValidationSupportContext.getRootValidationSupport().fetchCodeSystem(theCodeSystem);
|
||||
if (codeSystem != null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String message = "Unknown code system: " + theCodeSystem;
|
||||
if (!myAllowNonExistentCodeSystem) {
|
||||
return new CodeValidationResult()
|
||||
.setSeverity(IssueSeverity.ERROR)
|
||||
.setMessage(message);
|
||||
}
|
||||
|
||||
throw new TerminologyServiceException(message);
|
||||
return new CodeValidationResult()
|
||||
.setCode(theCode)
|
||||
.setSeverity(IssueSeverity.INFORMATION)
|
||||
.setMessage("Code " + theCodeSystem + "#" + theCode + " was not checked because the CodeSystem is not available");
|
||||
}
|
||||
|
||||
public void setAllowNonExistentCodeSystem(boolean theAllowNonExistentCodeSystem) {
|
||||
|
|
|
@ -5,38 +5,74 @@ import ca.uhn.fhir.context.support.ConceptValidationOptions;
|
|||
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
||||
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4.model.CodeSystem;
|
||||
import org.hl7.fhir.r4.model.CodeType;
|
||||
import org.hl7.fhir.r4.model.Enumerations;
|
||||
import org.hl7.fhir.r4.model.ValueSet;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class InMemoryTerminologyServerValidationSupportTest {
|
||||
public class InMemoryTerminologyServerValidationSupportTest {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(InMemoryTerminologyServerValidationSupportTest.class);
|
||||
private InMemoryTerminologyServerValidationSupport mySvc;
|
||||
private FhirContext myCtx = FhirContext.forR4();
|
||||
private DefaultProfileValidationSupport myDefaultSupport;
|
||||
private ValidationSupportChain myChain;
|
||||
private PrePopulatedValidationSupport myPrePopulated;
|
||||
private CommonCodeSystemsTerminologyService myCommonCodeSystemsTermSvc;
|
||||
|
||||
@BeforeEach
|
||||
public void before( ){
|
||||
public void before() {
|
||||
mySvc = new InMemoryTerminologyServerValidationSupport(myCtx);
|
||||
myDefaultSupport = new DefaultProfileValidationSupport(myCtx);
|
||||
myPrePopulated = new PrePopulatedValidationSupport(myCtx);
|
||||
myChain = new ValidationSupportChain(mySvc,myPrePopulated, myDefaultSupport);
|
||||
myCommonCodeSystemsTermSvc = new CommonCodeSystemsTerminologyService(myCtx);
|
||||
myChain = new ValidationSupportChain(mySvc, myPrePopulated, myDefaultSupport, myCommonCodeSystemsTermSvc);
|
||||
|
||||
// Force load
|
||||
myDefaultSupport.fetchCodeSystem("http://foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateCodeInUnknownCodeSystemWithEnumeratedValueSet() {
|
||||
public void testValidateCodeWithInferredSystem_CommonCodeSystemsCs_BuiltInVs() {
|
||||
|
||||
ValidationSupportContext valCtx = new ValidationSupportContext(myChain);
|
||||
ConceptValidationOptions options = new ConceptValidationOptions().setInferSystem(true);
|
||||
IValidationSupport.CodeValidationResult outcome;
|
||||
|
||||
String valueSetUrl = "http://hl7.org/fhir/ValueSet/mimetypes";
|
||||
|
||||
// ValidateCode
|
||||
outcome = myChain.validateCode(valCtx, options, null, "txt", null, valueSetUrl);
|
||||
assertTrue(outcome.isOk());
|
||||
assertEquals("Code was validated against in-memory expansion of ValueSet: http://hl7.org/fhir/ValueSet/mimetypes", outcome.getMessage());
|
||||
assertEquals("txt", outcome.getCode());
|
||||
|
||||
// ValidateCodeInValueSet
|
||||
IBaseResource valueSet = myChain.fetchValueSet(valueSetUrl);
|
||||
assertNotNull(valueSet);
|
||||
outcome = myChain.validateCodeInValueSet(valCtx, options, null, "txt", null, valueSet);
|
||||
assertTrue(outcome.isOk());
|
||||
assertEquals("Code was validated against in-memory expansion of ValueSet: http://hl7.org/fhir/ValueSet/mimetypes", outcome.getMessage());
|
||||
assertEquals("txt", outcome.getCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateCode_UnknownCodeSystem_EnumeratedValueSet() {
|
||||
ValueSet vs = new ValueSet();
|
||||
vs.setUrl("http://vs");
|
||||
vs
|
||||
|
@ -49,12 +85,75 @@ class InMemoryTerminologyServerValidationSupportTest {
|
|||
|
||||
ValidationSupportContext valCtx = new ValidationSupportContext(myChain);
|
||||
ConceptValidationOptions options = new ConceptValidationOptions();
|
||||
IValidationSupport.CodeValidationResult outcome;
|
||||
|
||||
IValidationSupport.CodeValidationResult outcome = myChain.validateCodeInValueSet(valCtx, options, "http://cs", "code1", null, vs);
|
||||
outcome = myChain.validateCodeInValueSet(valCtx, options, "http://cs", "code1", null, vs);
|
||||
assertEquals("Code was validated against in-memory expansion of ValueSet: http://vs", outcome.getMessage());
|
||||
assertTrue(outcome.isOk());
|
||||
|
||||
outcome = myChain.validateCodeInValueSet(valCtx, options, "http://cs", "code99", null, vs);
|
||||
assertNull(outcome);
|
||||
assertNotNull(outcome);
|
||||
assertFalse(outcome.isOk());
|
||||
assertEquals("Unknown code 'http://cs#code99' for in-memory expansion of ValueSet 'http://vs'", outcome.getMessage());
|
||||
assertEquals(IValidationSupport.IssueSeverity.ERROR, outcome.getSeverity());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateCode_UnknownCodeSystem_EnumeratedValueSet_MultipleIncludes() {
|
||||
ValueSet vs = new ValueSet();
|
||||
vs.setUrl("http://vs");
|
||||
vs
|
||||
.getCompose()
|
||||
.addInclude()
|
||||
.setSystem("http://cs")
|
||||
.addFilter()
|
||||
.setProperty("parent")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("blah");
|
||||
vs
|
||||
.getCompose()
|
||||
.addInclude()
|
||||
.setSystem("http://cs")
|
||||
.addConcept(new ValueSet.ConceptReferenceComponent(new CodeType("code1")))
|
||||
.addConcept(new ValueSet.ConceptReferenceComponent(new CodeType("code2")));
|
||||
myPrePopulated.addValueSet(vs);
|
||||
|
||||
ValidationSupportContext valCtx = new ValidationSupportContext(myChain);
|
||||
ConceptValidationOptions options = new ConceptValidationOptions();
|
||||
IValidationSupport.CodeValidationResult outcome;
|
||||
|
||||
outcome = myChain.validateCodeInValueSet(valCtx, options, "http://cs", "code1", null, vs);
|
||||
assertEquals("Code was validated against in-memory expansion of ValueSet: http://vs", outcome.getMessage());
|
||||
assertTrue(outcome.isOk());
|
||||
|
||||
outcome = myChain.validateCodeInValueSet(valCtx, options, "http://cs", "code99", null, vs);
|
||||
assertNotNull(outcome);
|
||||
assertFalse(outcome.isOk());
|
||||
assertEquals("Failed to expand ValueSet 'http://vs' (in-memory). Could not validate code http://cs#code99. Error was: Unable to expand ValueSet because CodeSystem could not be found: http://cs", outcome.getMessage());
|
||||
assertEquals(IValidationSupport.IssueSeverity.ERROR, outcome.getSeverity());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateCode_UnknownCodeSystem_NonEnumeratedValueSet() {
|
||||
ValueSet vs = new ValueSet();
|
||||
vs.setUrl("http://vs");
|
||||
vs
|
||||
.getCompose()
|
||||
.addInclude()
|
||||
.setSystem("http://cs");
|
||||
myPrePopulated.addValueSet(vs);
|
||||
|
||||
ValidationSupportContext valCtx = new ValidationSupportContext(myChain);
|
||||
ConceptValidationOptions options = new ConceptValidationOptions();
|
||||
IValidationSupport.CodeValidationResult outcome;
|
||||
|
||||
outcome = myChain.validateCodeInValueSet(valCtx, options, "http://cs", "code99", null, vs);
|
||||
assertNotNull(outcome);
|
||||
assertFalse(outcome.isOk());
|
||||
assertEquals("Failed to expand ValueSet 'http://vs' (in-memory). Could not validate code http://cs#code99. Error was: Unable to expand ValueSet because CodeSystem could not be found: http://cs", outcome.getMessage());
|
||||
assertEquals(IValidationSupport.IssueSeverity.ERROR, outcome.getSeverity());
|
||||
|
||||
}
|
||||
|
||||
|
@ -88,6 +187,242 @@ class InMemoryTerminologyServerValidationSupportTest {
|
|||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testExpandValueSet_VsIsEnumeratedWithVersionedSystem_CsOnlyDifferentVersionPresent() {
|
||||
CodeSystem cs = new CodeSystem();
|
||||
cs.setId("snomed-ct-ca-imm");
|
||||
cs.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
cs.setContent(CodeSystem.CodeSystemContentMode.FRAGMENT);
|
||||
cs.setUrl("http://snomed.info/sct");
|
||||
cs.setVersion("http://snomed.info/sct/20611000087101/version/20210331");
|
||||
cs.addConcept().setCode("28571000087109").setDisplay("MODERNA COVID-19 mRNA-1273");
|
||||
myPrePopulated.addCodeSystem(cs);
|
||||
|
||||
ValueSet vs = new ValueSet();
|
||||
vs.setId("vaccinecode");
|
||||
vs.setUrl("http://ehealthontario.ca/fhir/ValueSet/vaccinecode");
|
||||
vs.setVersion("0.1.17");
|
||||
vs.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
ValueSet.ConceptSetComponent vsInclude = vs.getCompose().addInclude();
|
||||
vsInclude.setSystem("http://snomed.info/sct");
|
||||
vsInclude.setVersion("0.17"); // different version
|
||||
vsInclude.addConcept().setCode("28571000087109").setDisplay("MODERNA COVID-19 mRNA-1273");
|
||||
myPrePopulated.addValueSet(vs);
|
||||
|
||||
ValidationSupportContext valCtx = new ValidationSupportContext(myChain);
|
||||
ConceptValidationOptions options = new ConceptValidationOptions();
|
||||
options.setValidateDisplay(true);
|
||||
|
||||
String codeSystemUrl;
|
||||
String valueSetUrl;
|
||||
String code;
|
||||
|
||||
IValidationSupport.ValueSetExpansionOutcome expansion = mySvc.expandValueSet(valCtx, new ValueSetExpansionOptions(), vs);
|
||||
assertNotNull(expansion.getValueSet());
|
||||
assertEquals(1, ((ValueSet)expansion.getValueSet()).getExpansion().getContains().size());
|
||||
|
||||
// Validate code - good
|
||||
codeSystemUrl = "http://snomed.info/sct";
|
||||
valueSetUrl = "http://ehealthontario.ca/fhir/ValueSet/vaccinecode";
|
||||
code = "28571000087109";
|
||||
String display = null;
|
||||
IValidationSupport.CodeValidationResult outcome = mySvc.validateCode(valCtx, options, codeSystemUrl, code, display, valueSetUrl);
|
||||
assertTrue(outcome.isOk());
|
||||
assertEquals("28571000087109", outcome.getCode());
|
||||
assertEquals("MODERNA COVID-19 mRNA-1273", outcome.getDisplay());
|
||||
assertEquals("0.17", outcome.getCodeSystemVersion());
|
||||
|
||||
// Validate code - good code, bad display
|
||||
codeSystemUrl = "http://snomed.info/sct";
|
||||
valueSetUrl = "http://ehealthontario.ca/fhir/ValueSet/vaccinecode";
|
||||
code = "28571000087109";
|
||||
display = "BLAH";
|
||||
outcome = mySvc.validateCode(valCtx, options, codeSystemUrl, code, display, valueSetUrl);
|
||||
assertFalse(outcome.isOk());
|
||||
assertEquals(null, outcome.getCode());
|
||||
assertEquals("MODERNA COVID-19 mRNA-1273", outcome.getDisplay());
|
||||
assertEquals("0.17", outcome.getCodeSystemVersion());
|
||||
|
||||
// Validate code - good code, good display
|
||||
codeSystemUrl = "http://snomed.info/sct";
|
||||
valueSetUrl = "http://ehealthontario.ca/fhir/ValueSet/vaccinecode";
|
||||
code = "28571000087109";
|
||||
display = "MODERNA COVID-19 mRNA-1273";
|
||||
outcome = mySvc.validateCode(valCtx, options, codeSystemUrl, code, display, valueSetUrl);
|
||||
assertTrue(outcome.isOk());
|
||||
assertEquals("28571000087109", outcome.getCode());
|
||||
assertEquals("MODERNA COVID-19 mRNA-1273", outcome.getDisplay());
|
||||
assertEquals("0.17", outcome.getCodeSystemVersion());
|
||||
|
||||
// Validate code - bad code
|
||||
codeSystemUrl = "http://snomed.info/sct";
|
||||
valueSetUrl = "http://ehealthontario.ca/fhir/ValueSet/vaccinecode";
|
||||
code = "BLAH";
|
||||
outcome = mySvc.validateCode(valCtx, options, codeSystemUrl, code, null, valueSetUrl);
|
||||
assertFalse(outcome.isOk());
|
||||
assertEquals(null, outcome.getCode());
|
||||
assertEquals(null, outcome.getDisplay());
|
||||
assertEquals(null, outcome.getCodeSystemVersion());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testExpandValueSet_VsUsesVersionedSystem_CsIsFragmentWithoutCode() {
|
||||
CodeSystem cs = new CodeSystem();
|
||||
cs.setId("snomed-ct-ca-imm");
|
||||
cs.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
cs.setContent(CodeSystem.CodeSystemContentMode.FRAGMENT);
|
||||
cs.setUrl("http://snomed.info/sct");
|
||||
cs.setVersion("http://snomed.info/sct/20611000087101/version/20210331");
|
||||
cs.addConcept().setCode("28571000087109").setDisplay("MODERNA COVID-19 mRNA-1273");
|
||||
myPrePopulated.addCodeSystem(cs);
|
||||
|
||||
ValueSet vs = new ValueSet();
|
||||
vs.setId("vaccinecode");
|
||||
vs.setUrl("http://ehealthontario.ca/fhir/ValueSet/vaccinecode");
|
||||
vs.setVersion("0.1.17");
|
||||
vs.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
ValueSet.ConceptSetComponent vsInclude = vs.getCompose().addInclude();
|
||||
vsInclude.setSystem("http://snomed.info/sct");
|
||||
vsInclude.setVersion("http://snomed.info/sct/20611000087101/version/20210331");
|
||||
vsInclude.addConcept().setCode("28571000087109").setDisplay("MODERNA COVID-19 mRNA-1273");
|
||||
myPrePopulated.addValueSet(vs);
|
||||
|
||||
ValidationSupportContext valCtx = new ValidationSupportContext(myChain);
|
||||
ConceptValidationOptions options = new ConceptValidationOptions();
|
||||
|
||||
String codeSystemUrl;
|
||||
String valueSetUrl;
|
||||
String code;
|
||||
|
||||
IValidationSupport.ValueSetExpansionOutcome expansion = mySvc.expandValueSet(valCtx, new ValueSetExpansionOptions(), vs);
|
||||
assertNull(expansion.getError());
|
||||
ValueSet valueSet = (ValueSet) expansion.getValueSet();
|
||||
assertNotNull(valueSet);
|
||||
assertEquals(1, valueSet.getExpansion().getContains().size());
|
||||
assertEquals("28571000087109", valueSet.getExpansion().getContains().get(0).getCode());
|
||||
assertEquals("MODERNA COVID-19 mRNA-1273", valueSet.getExpansion().getContains().get(0).getDisplay());
|
||||
|
||||
codeSystemUrl = "http://snomed.info/sct";
|
||||
valueSetUrl = "http://ehealthontario.ca/fhir/ValueSet/vaccinecode";
|
||||
code = "28571000087109";
|
||||
IValidationSupport.CodeValidationResult outcome = mySvc.validateCode(valCtx, options, codeSystemUrl, code, null, valueSetUrl);
|
||||
assertTrue(outcome.isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExpandValueSet_VsUsesVersionedSystem_CsIsFragmentWithCode() {
|
||||
CodeSystem cs = new CodeSystem();
|
||||
cs.setId("snomed-ct-ca-imm");
|
||||
cs.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
cs.setContent(CodeSystem.CodeSystemContentMode.FRAGMENT);
|
||||
cs.setUrl("http://snomed.info/sct");
|
||||
cs.setVersion("http://snomed.info/sct/20611000087101/version/20210331");
|
||||
cs.addConcept().setCode("28571000087109").setDisplay("MODERNA COVID-19 mRNA-1273");
|
||||
myPrePopulated.addCodeSystem(cs);
|
||||
|
||||
ValueSet vs = new ValueSet();
|
||||
vs.setId("vaccinecode");
|
||||
vs.setUrl("http://ehealthontario.ca/fhir/ValueSet/vaccinecode");
|
||||
vs.setVersion("http://snomed.info/sct/20611000087101/version/20210331");
|
||||
vs.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
ValueSet.ConceptSetComponent vsInclude = vs.getCompose().addInclude();
|
||||
vsInclude.setSystem("http://snomed.info/sct");
|
||||
vsInclude.setVersion("http://snomed.info/sct/20611000087101/version/20210331");
|
||||
vsInclude.addConcept().setCode("28571000087109").setDisplay("MODERNA COVID-19 mRNA-1273");
|
||||
myPrePopulated.addValueSet(vs);
|
||||
|
||||
ValidationSupportContext valCtx = new ValidationSupportContext(myChain);
|
||||
ConceptValidationOptions options = new ConceptValidationOptions();
|
||||
|
||||
String codeSystemUrl;
|
||||
String valueSetUrl;
|
||||
String code;
|
||||
IValidationSupport.CodeValidationResult outcome;
|
||||
|
||||
// Good code
|
||||
codeSystemUrl = "http://snomed.info/sct";
|
||||
valueSetUrl = "http://ehealthontario.ca/fhir/ValueSet/vaccinecode";
|
||||
code = "28571000087109";
|
||||
outcome = mySvc.validateCode(valCtx, options, codeSystemUrl, code, null, valueSetUrl);
|
||||
assertTrue(outcome.isOk());
|
||||
assertEquals("MODERNA COVID-19 mRNA-1273", outcome.getDisplay());
|
||||
|
||||
// Bad code
|
||||
codeSystemUrl = "http://snomed.info/sct";
|
||||
valueSetUrl = "http://ehealthontario.ca/fhir/ValueSet/vaccinecode";
|
||||
code = "123";
|
||||
outcome = mySvc.validateCode(valCtx, options, codeSystemUrl, code, null, valueSetUrl);
|
||||
assertFalse(outcome.isOk());
|
||||
|
||||
IValidationSupport.ValueSetExpansionOutcome expansion = mySvc.expandValueSet(valCtx, new ValueSetExpansionOptions(), vs);
|
||||
assertNull(expansion.getError());
|
||||
ValueSet valueSet = (ValueSet) expansion.getValueSet();
|
||||
assertNotNull(valueSet);
|
||||
assertEquals(1, valueSet.getExpansion().getContains().size());
|
||||
assertEquals("28571000087109", valueSet.getExpansion().getContains().get(0).getCode());
|
||||
assertEquals("MODERNA COVID-19 mRNA-1273", valueSet.getExpansion().getContains().get(0).getDisplay());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testExpandValueSet_VsUsesVersionedSystem_CsIsCompleteWithCode() {
|
||||
CodeSystem cs = new CodeSystem();
|
||||
cs.setId("snomed-ct-ca-imm");
|
||||
cs.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
cs.setContent(CodeSystem.CodeSystemContentMode.COMPLETE);
|
||||
cs.setUrl("http://snomed.info/sct");
|
||||
cs.setVersion("http://snomed.info/sct/20611000087101/version/20210331");
|
||||
cs.addConcept().setCode("28571000087109").setDisplay("MODERNA COVID-19 mRNA-1273");
|
||||
myPrePopulated.addCodeSystem(cs);
|
||||
|
||||
ValueSet vs = new ValueSet();
|
||||
vs.setId("vaccinecode");
|
||||
vs.setUrl("http://ehealthontario.ca/fhir/ValueSet/vaccinecode");
|
||||
vs.setVersion("http://snomed.info/sct/20611000087101/version/20210331");
|
||||
vs.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
ValueSet.ConceptSetComponent vsInclude = vs.getCompose().addInclude();
|
||||
vsInclude.setSystem("http://snomed.info/sct");
|
||||
vsInclude.setVersion("http://snomed.info/sct/20611000087101/version/20210331");
|
||||
vsInclude.addConcept().setCode("28571000087109").setDisplay("MODERNA COVID-19 mRNA-1273");
|
||||
myPrePopulated.addValueSet(vs);
|
||||
|
||||
ValidationSupportContext valCtx = new ValidationSupportContext(myChain);
|
||||
ConceptValidationOptions options = new ConceptValidationOptions();
|
||||
|
||||
String codeSystemUrl;
|
||||
String valueSetUrl;
|
||||
String code;
|
||||
IValidationSupport.CodeValidationResult outcome;
|
||||
|
||||
// Good code
|
||||
codeSystemUrl = "http://snomed.info/sct";
|
||||
valueSetUrl = "http://ehealthontario.ca/fhir/ValueSet/vaccinecode";
|
||||
code = "28571000087109";
|
||||
outcome = mySvc.validateCode(valCtx, options, codeSystemUrl, code, null, valueSetUrl);
|
||||
assertTrue(outcome.isOk());
|
||||
assertEquals("MODERNA COVID-19 mRNA-1273", outcome.getDisplay());
|
||||
|
||||
// Bad code
|
||||
codeSystemUrl = "http://snomed.info/sct";
|
||||
valueSetUrl = "http://ehealthontario.ca/fhir/ValueSet/vaccinecode";
|
||||
code = "123";
|
||||
outcome = mySvc.validateCode(valCtx, options, codeSystemUrl, code, null, valueSetUrl);
|
||||
assertFalse(outcome.isOk());
|
||||
|
||||
IValidationSupport.ValueSetExpansionOutcome expansion = mySvc.expandValueSet(valCtx, new ValueSetExpansionOptions(), vs);
|
||||
ValueSet valueSet = (ValueSet) expansion.getValueSet();
|
||||
assertNotNull(valueSet);
|
||||
assertEquals(1, valueSet.getExpansion().getContains().size());
|
||||
assertEquals("28571000087109", valueSet.getExpansion().getContains().get(0).getCode());
|
||||
assertEquals("MODERNA COVID-19 mRNA-1273", valueSet.getExpansion().getContains().get(0).getDisplay());
|
||||
}
|
||||
|
||||
|
||||
private static class PrePopulatedValidationSupportDstu2 extends PrePopulatedValidationSupport {
|
||||
private final Map<String, IBaseResource> myDstu2ValueSets;
|
||||
|
||||
|
|
|
@ -2,17 +2,18 @@ package org.hl7.fhir.dstu3.hapi.validation;
|
|||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4.model.CodeSystem;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
public class DefaultProfileValidationSupportTest {
|
||||
|
||||
private static FhirContext ourCtx = FhirContext.forR4Cached();
|
||||
private DefaultProfileValidationSupport mySvc = new DefaultProfileValidationSupport(ourCtx);
|
||||
private static FhirContext ourCtx = FhirContext.forDstu3();
|
||||
|
||||
@Test
|
||||
public void testGetStructureDefinitionsWithRelativeUrls() {
|
||||
|
@ -26,10 +27,17 @@ public class DefaultProfileValidationSupportTest {
|
|||
|
||||
}
|
||||
|
||||
|
||||
@AfterAll
|
||||
public static void afterClassClearContext() {
|
||||
TestUtil.randomizeLocaleAndTimezone();
|
||||
}
|
||||
@Test
|
||||
public void testLoadCodeSystemWithVersion() {
|
||||
CodeSystem cs = (CodeSystem) mySvc.fetchCodeSystem("http://terminology.hl7.org/CodeSystem/v2-0291");
|
||||
assertNotNull(cs);
|
||||
String version = cs.getVersion();
|
||||
assertEquals("2.9", version);
|
||||
|
||||
cs = (CodeSystem) mySvc.fetchCodeSystem("http://terminology.hl7.org/CodeSystem/v2-0291|" + version);
|
||||
assertNotNull(cs);
|
||||
|
||||
cs = (CodeSystem) mySvc.fetchCodeSystem("http://terminology.hl7.org/CodeSystem/v2-0291|999");
|
||||
assertNotNull(cs);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -777,7 +777,7 @@ public class FhirInstanceValidatorDstu3Test {
|
|||
Patient resource = loadResource("/dstu3/nl/nl-core-patient-01.json", Patient.class);
|
||||
ValidationResult results = myVal.validateWithResult(resource);
|
||||
List<SingleValidationMessage> outcome = logResultsAndReturnNonInformationalOnes(results);
|
||||
assertThat(outcome.toString(), containsString("The Coding provided (urn:oid:2.16.840.1.113883.2.4.4.16.34#6030) is not in the value set http://decor.nictiz.nl/fhir/ValueSet/2.16.840.1.113883.2.4.3.11.60.40.2.20.5.1--20171231000000"));
|
||||
assertThat(outcome.toString(), containsString("Could not confirm that the codes provided are in the value set http://decor.nictiz.nl/fhir/ValueSet/2.16.840.1.113883.2.4.3.11.60.40.2.20.5.1--20171231000000"));
|
||||
}
|
||||
|
||||
private void loadNL() throws IOException {
|
||||
|
@ -1180,7 +1180,7 @@ public class FhirInstanceValidatorDstu3Test {
|
|||
ValidationResult output = myVal.validateWithResult(input);
|
||||
logResultsAndReturnAll(output);
|
||||
assertEquals(
|
||||
"The value provided ('notvalidcode') is not in the value set http://hl7.org/fhir/ValueSet/observation-status (http://hl7.org/fhir/ValueSet/observation-status), and a code is required from this value set) (error message = Unknown code 'notvalidcode')",
|
||||
"The value provided ('notvalidcode') is not in the value set http://hl7.org/fhir/ValueSet/observation-status (http://hl7.org/fhir/ValueSet/observation-status), and a code is required from this value set) (error message = Unknown code 'notvalidcode' for in-memory expansion of ValueSet 'http://hl7.org/fhir/ValueSet/observation-status')",
|
||||
output.getMessages().get(0).getMessage());
|
||||
}
|
||||
|
||||
|
|
|
@ -1200,7 +1200,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
|
|||
ValidationResult output = myVal.validateWithResult(input);
|
||||
logResultsAndReturnAll(output);
|
||||
assertEquals(
|
||||
"The value provided ('notvalidcode') is not in the value set http://hl7.org/fhir/ValueSet/observation-status|4.0.1 (http://hl7.org/fhir/ValueSet/observation-status), and a code is required from this value set) (error message = Unknown code 'notvalidcode')",
|
||||
"The value provided ('notvalidcode') is not in the value set http://hl7.org/fhir/ValueSet/observation-status|4.0.1 (http://hl7.org/fhir/ValueSet/observation-status), and a code is required from this value set) (error message = Unknown code 'notvalidcode' for in-memory expansion of ValueSet 'http://hl7.org/fhir/ValueSet/observation-status')",
|
||||
output.getMessages().get(0).getMessage());
|
||||
}
|
||||
|
||||
|
|
|
@ -363,7 +363,7 @@ public class FhirInstanceValidatorR5Test {
|
|||
|
||||
ValidationResult output = myVal.validateWithResult(encoded);
|
||||
List<SingleValidationMessage> errors = logResultsAndReturnNonInformationalOnes(output);
|
||||
assertEquals(1, errors.size());
|
||||
assertEquals(1, errors.size(), ()->errors.toString());
|
||||
assertEquals("The value '%%%2@()()' is not a valid Base64 value", errors.get(0).getMessage());
|
||||
|
||||
}
|
||||
|
@ -383,7 +383,7 @@ public class FhirInstanceValidatorR5Test {
|
|||
|
||||
ValidationResult output = myVal.validateWithResult(encoded);
|
||||
List<SingleValidationMessage> errors = logResultsAndReturnNonInformationalOnes(output);
|
||||
assertEquals(0, errors.size());
|
||||
assertEquals(0, errors.size(), ()->errors.toString());
|
||||
|
||||
}
|
||||
|
||||
|
@ -669,8 +669,8 @@ public class FhirInstanceValidatorR5Test {
|
|||
|
||||
ValidationResult output = myVal.validateWithResult(input);
|
||||
List<SingleValidationMessage> res = logResultsAndReturnNonInformationalOnes(output);
|
||||
assertEquals(2, res.size(), output.toString());
|
||||
assertEquals("A code with no system has no defined meaning. A system should be provided", output.getMessages().get(1).getMessage());
|
||||
assertEquals(1, res.size(), output.toString());
|
||||
assertEquals("A code with no system has no defined meaning. A system should be provided", res.get(0).getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -67,7 +67,7 @@ public class QuestionnaireResponseValidatorR5Test {
|
|||
private static final String CODE_ICC_SCHOOLTYPE_PT = "PT";
|
||||
private static final String ID_VS_SCHOOLTYPE = "ValueSet/schooltype";
|
||||
private static final String SYSTEMURI_ICC_SCHOOLTYPE = "http://ehealthinnovation/icc/ns/schooltype";
|
||||
private static FhirContext ourCtx = FhirContext.forR5();
|
||||
private static final FhirContext ourCtx = FhirContext.forR5Cached();
|
||||
private static DefaultProfileValidationSupport myDefaultValidationSupport = new DefaultProfileValidationSupport(ourCtx);
|
||||
private FhirInstanceValidator myInstanceVal;
|
||||
private FhirValidator myVal;
|
||||
|
@ -184,7 +184,7 @@ public class QuestionnaireResponseValidatorR5Test {
|
|||
ValidationResult errors = myVal.validateWithResult(qa);
|
||||
|
||||
ourLog.info(errors.toString());
|
||||
assertThat("index[" + i + "]: " + errors.toString(), errors.getMessages(), empty());
|
||||
assertThat("index[" + i + "]: " + errors, errors.getMessages(), empty());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -715,7 +715,6 @@ public class QuestionnaireResponseValidatorR5Test {
|
|||
public static void afterClassClearContext() {
|
||||
myDefaultValidationSupport.flush();
|
||||
myDefaultValidationSupport = null;
|
||||
TestUtil.randomizeLocaleAndTimezone();
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue