Fix term uploads and validate against valueset recalculation (#1540)
* Start refactoring terminology delta operations * Work on delta operations * Work on concept saving * Split term services into smaller services * Work on term delta operations * Work on term svcs * Work on term operations * More work on delta uploader * Add a test * Wrk on term service * Fix compile error * Some refactoring * Test fix * Test fix * Test fixes * Test fix * Test fixes * Test fixes * Work on delta * Work on tests# * Test fixes * Improve resequencing logic * Build test * More testing * More build testing * More work on tests * CHange test logging * Fix term service PID issue * Update src/changes/changes.xml Co-Authored-By: Diederik Muylwyk <diederik.muylwyk@gmail.com> * Address review comment * Some cleanup * Test fix * Fix some tests * Work on upload command * Test fixes * Work on validation fixes * Work on validation against pre-expansion * Work on direct validation * A bit of cleanup * Add some javadoc * Force another build * Test fixes * Fix tests * Fix tests
This commit is contained in:
parent
8b2ab51bc6
commit
12a74e6cbc
|
@ -265,7 +265,7 @@ public class ValidatorExamples {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, String theValueSet) {
|
||||
// TODO: implement
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -102,6 +102,11 @@ public interface IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST>
|
|||
*/
|
||||
boolean isCodeSystemSupported(FhirContext theContext, String theSystem);
|
||||
|
||||
/**
|
||||
* Fetch the given ValueSet by URL
|
||||
*/
|
||||
IBaseResource fetchValueSet(FhirContext theContext, String theValueSetUrl);
|
||||
|
||||
/**
|
||||
* Validates that the given code exists and if possible returns a display
|
||||
* name. This method is called to check codes which are found in "example"
|
||||
|
@ -112,7 +117,7 @@ public interface IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST>
|
|||
* @param theDisplay The display name, if it should also be validated
|
||||
* @return Returns a validation result object
|
||||
*/
|
||||
CodeValidationResult<CDCT, IST> validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay);
|
||||
CodeValidationResult<CDCT, IST> validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl);
|
||||
|
||||
/**
|
||||
* Look up a code using the system and code value
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package ca.uhn.fhir.util;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
public class FileUtil {
|
||||
// Use "bytes" instead of just "b" because it reads easier in logs
|
||||
private static final String[] UNITS = new String[]{"Bytes", "kB", "MB", "GB", "TB"};
|
||||
|
||||
public static String formatFileSize(long theBytes) {
|
||||
if (theBytes <= 0) {
|
||||
return "0 " + UNITS[0];
|
||||
}
|
||||
int digitGroups = (int) (Math.log10(theBytes) / Math.log10(1024));
|
||||
digitGroups = Math.min(digitGroups, UNITS.length - 1);
|
||||
return new DecimalFormat("###0.#").format(theBytes / Math.pow(1024, digitGroups)) + " " + UNITS[digitGroups];
|
||||
}
|
||||
|
||||
}
|
|
@ -30,7 +30,11 @@ import com.ctc.wstx.stax.WstxOutputFactory;
|
|||
import org.apache.commons.text.StringEscapeUtils;
|
||||
import org.codehaus.stax2.XMLOutputFactory2;
|
||||
import org.codehaus.stax2.io.EscapingWriterFactory;
|
||||
import org.w3c.dom.Document;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.stream.*;
|
||||
|
@ -1830,7 +1834,9 @@ public class XmlUtil {
|
|||
}
|
||||
}
|
||||
|
||||
public static DocumentBuilderFactory newDocumentBuilderFactory() {
|
||||
public static Document parseDocument(String theInput) throws IOException, SAXException {
|
||||
DocumentBuilder builder = null;
|
||||
try {
|
||||
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
|
||||
docBuilderFactory.setNamespaceAware(true);
|
||||
docBuilderFactory.setXIncludeAware(false);
|
||||
|
@ -1846,7 +1852,12 @@ public class XmlUtil {
|
|||
ourLog.warn("Failed to set feature on XML parser: " + e.toString());
|
||||
}
|
||||
|
||||
return docBuilderFactory;
|
||||
builder = docBuilderFactory.newDocumentBuilder();
|
||||
} catch (ParserConfigurationException e) {
|
||||
throw new ConfigurationException(e);
|
||||
}
|
||||
|
||||
InputSource src = new InputSource(new StringReader(theInput));
|
||||
return builder.parse(src);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package ca.uhn.fhir.util;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class FileUtilTest {
|
||||
|
||||
@Test
|
||||
public void formatFileSize() {
|
||||
assertEquals("0 Bytes", FileUtil.formatFileSize(0));
|
||||
assertEquals("1 Bytes", FileUtil.formatFileSize(1));
|
||||
assertEquals("1.2 kB", FileUtil.formatFileSize(1234));
|
||||
assertEquals("12.1 kB", FileUtil.formatFileSize(12345));
|
||||
assertEquals("11.8 MB", FileUtil.formatFileSize(12345678));
|
||||
assertEquals("103.5 GB", FileUtil.formatFileSize(111111111111L));
|
||||
assertEquals("101.1 TB", FileUtil.formatFileSize(111111111111111L));
|
||||
assertEquals("10105.5 TB", FileUtil.formatFileSize(11111111111111111L));
|
||||
}
|
||||
}
|
|
@ -398,7 +398,7 @@ public abstract class BaseCommand implements Comparable<BaseCommand> {
|
|||
}
|
||||
|
||||
protected IGenericClient newClientWithBaseUrl(CommandLine theCommandLine, String theBaseUrl, String theBasicAuthOptionName, String theBearerTokenOptionName) throws ParseException {
|
||||
myFhirCtx.getRestfulClientFactory().setSocketTimeout(10 * 60 * 1000);
|
||||
myFhirCtx.getRestfulClientFactory().setSocketTimeout((int) DateUtils.MILLIS_PER_HOUR);
|
||||
IGenericClient retVal = myFhirCtx.newRestfulGenericClient(theBaseUrl);
|
||||
|
||||
String basicAuthHeaderValue = getAndParseOptionBasicAuthHeader(theCommandLine, theBasicAuthOptionName);
|
||||
|
|
|
@ -91,7 +91,7 @@ public class LoadingValidationSupportDstu3 implements IValidationSupport {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -98,7 +98,7 @@ public class LoadingValidationSupportR4 implements org.hl7.fhir.r4.hapi.ctx.IVal
|
|||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, String theValueSet) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,18 +27,20 @@ import ca.uhn.fhir.rest.client.api.IGenericClient;
|
|||
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.util.AttachmentUtil;
|
||||
import ca.uhn.fhir.util.FileUtil;
|
||||
import ca.uhn.fhir.util.ParametersUtil;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Charsets;
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.apache.commons.cli.Options;
|
||||
import org.apache.commons.cli.ParseException;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.io.input.CountingInputStream;
|
||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||
import org.hl7.fhir.instance.model.api.ICompositeType;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.*;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
|
@ -46,8 +48,9 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
|
|||
|
||||
public class UploadTerminologyCommand extends BaseCommand {
|
||||
static final String UPLOAD_TERMINOLOGY = "upload-terminology";
|
||||
// TODO: Don't use qualified names for loggers in HAPI CLI.
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(UploadTerminologyCommand.class);
|
||||
private static final long DEFAULT_TRANSFER_SIZE_LIMIT = 10 * FileUtils.ONE_MB;
|
||||
private static long ourTransferSizeLimit = DEFAULT_TRANSFER_SIZE_LIMIT;
|
||||
|
||||
@Override
|
||||
public String getCommandDescription() {
|
||||
|
@ -105,23 +108,25 @@ public class UploadTerminologyCommand extends BaseCommand {
|
|||
|
||||
switch (mode) {
|
||||
case SNAPSHOT:
|
||||
invokeOperation(theCommandLine, termUrl, datafile, client, inputParameters, JpaConstants.OPERATION_UPLOAD_EXTERNAL_CODE_SYSTEM);
|
||||
invokeOperation(termUrl, datafile, client, inputParameters, JpaConstants.OPERATION_UPLOAD_EXTERNAL_CODE_SYSTEM);
|
||||
break;
|
||||
case ADD:
|
||||
invokeOperation(theCommandLine, termUrl, datafile, client, inputParameters, JpaConstants.OPERATION_APPLY_CODESYSTEM_DELTA_ADD);
|
||||
invokeOperation(termUrl, datafile, client, inputParameters, JpaConstants.OPERATION_APPLY_CODESYSTEM_DELTA_ADD);
|
||||
break;
|
||||
case REMOVE:
|
||||
invokeOperation(theCommandLine, termUrl, datafile, client, inputParameters, JpaConstants.OPERATION_APPLY_CODESYSTEM_DELTA_REMOVE);
|
||||
invokeOperation(termUrl, datafile, client, inputParameters, JpaConstants.OPERATION_APPLY_CODESYSTEM_DELTA_REMOVE);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void invokeOperation(CommandLine theCommandLine, String theTermUrl, String[] theDatafile, IGenericClient theClient, IBaseParameters theInputParameters, String theOperationName) throws ParseException {
|
||||
private void invokeOperation(String theTermUrl, String[] theDatafile, IGenericClient theClient, IBaseParameters theInputParameters, String theOperationName) throws ParseException {
|
||||
ParametersUtil.addParameterToParametersUri(myFhirCtx, theInputParameters, TerminologyUploaderProvider.PARAM_SYSTEM, theTermUrl);
|
||||
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream, Charsets.UTF_8);
|
||||
int compressedSourceBytesCount = 0;
|
||||
int compressedFileCount = 0;
|
||||
boolean haveCompressedContents = false;
|
||||
try {
|
||||
for (String nextDataFile : theDatafile) {
|
||||
|
@ -133,19 +138,19 @@ public class UploadTerminologyCommand extends BaseCommand {
|
|||
ZipEntry nextEntry = new ZipEntry(stripPath(nextDataFile));
|
||||
zipOutputStream.putNextEntry(nextEntry);
|
||||
|
||||
IOUtils.copy(fileInputStream, zipOutputStream);
|
||||
CountingInputStream countingInputStream = new CountingInputStream(fileInputStream);
|
||||
IOUtils.copy(countingInputStream, zipOutputStream);
|
||||
haveCompressedContents = true;
|
||||
compressedSourceBytesCount += countingInputStream.getCount();
|
||||
|
||||
zipOutputStream.flush();
|
||||
ourLog.info("Finished compressing {} into {}", nextEntry.getSize(), nextEntry.getCompressedSize());
|
||||
ourLog.info("Finished compressing {}", nextDataFile);
|
||||
|
||||
} else {
|
||||
|
||||
ourLog.info("Adding file: {}", nextDataFile);
|
||||
ICompositeType attachment = AttachmentUtil.newInstance(myFhirCtx);
|
||||
AttachmentUtil.setUrl(myFhirCtx, attachment, "file:" + nextDataFile);
|
||||
AttachmentUtil.setData(myFhirCtx, attachment, IOUtils.toByteArray(fileInputStream));
|
||||
ParametersUtil.addParameterToParameters(myFhirCtx, theInputParameters, TerminologyUploaderProvider.PARAM_FILE, attachment);
|
||||
String fileName = "file:" + nextDataFile;
|
||||
addFileToRequestBundle(theInputParameters, fileName, IOUtils.toByteArray(fileInputStream));
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -158,10 +163,10 @@ public class UploadTerminologyCommand extends BaseCommand {
|
|||
}
|
||||
|
||||
if (haveCompressedContents) {
|
||||
ICompositeType attachment = AttachmentUtil.newInstance(myFhirCtx);
|
||||
AttachmentUtil.setUrl(myFhirCtx, attachment, "file:/files.zip");
|
||||
AttachmentUtil.setData(myFhirCtx, attachment, byteArrayOutputStream.toByteArray());
|
||||
ParametersUtil.addParameterToParameters(myFhirCtx, theInputParameters, TerminologyUploaderProvider.PARAM_FILE, attachment);
|
||||
byte[] compressedBytes = byteArrayOutputStream.toByteArray();
|
||||
ourLog.info("Compressed {} bytes in {} file(s) into {} bytes", FileUtil.formatFileSize(compressedSourceBytesCount), compressedFileCount, FileUtil.formatFileSize(compressedBytes.length));
|
||||
|
||||
addFileToRequestBundle(theInputParameters, "file:/files.zip", compressedBytes);
|
||||
}
|
||||
|
||||
ourLog.info("Beginning upload - This may take a while...");
|
||||
|
@ -190,11 +195,50 @@ public class UploadTerminologyCommand extends BaseCommand {
|
|||
ourLog.info("Response:\n{}", myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(response));
|
||||
}
|
||||
|
||||
private void addFileToRequestBundle(IBaseParameters theInputParameters, String theFileName, byte[] theBytes) {
|
||||
|
||||
byte[] bytes = theBytes;
|
||||
String fileName = theFileName;
|
||||
|
||||
if (bytes.length > ourTransferSizeLimit) {
|
||||
ourLog.info("File size is greater than {} - Going to use a local file reference instead of a direct HTTP transfer. Note that this will only work when executing this command on the same server as the FHIR server itself.", FileUtil.formatFileSize(ourTransferSizeLimit));
|
||||
|
||||
String suffix = fileName.substring(fileName.lastIndexOf("."));
|
||||
try {
|
||||
File tempFile = File.createTempFile("hapi-fhir-cli", suffix);
|
||||
tempFile.deleteOnExit();
|
||||
try (OutputStream fileOutputStream = new FileOutputStream(tempFile, false)) {
|
||||
fileOutputStream.write(bytes);
|
||||
bytes = null;
|
||||
fileName = "localfile:" + tempFile.getAbsolutePath();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new CommandFailureException(e);
|
||||
}
|
||||
}
|
||||
|
||||
ICompositeType attachment = AttachmentUtil.newInstance(myFhirCtx);
|
||||
AttachmentUtil.setUrl(myFhirCtx, attachment, fileName);
|
||||
if (bytes != null) {
|
||||
AttachmentUtil.setData(myFhirCtx, attachment, bytes);
|
||||
}
|
||||
ParametersUtil.addParameterToParameters(myFhirCtx, theInputParameters, TerminologyUploaderProvider.PARAM_FILE, attachment);
|
||||
}
|
||||
|
||||
private enum ModeEnum {
|
||||
SNAPSHOT, ADD, REMOVE
|
||||
}
|
||||
|
||||
public static String stripPath(String thePath) {
|
||||
@VisibleForTesting
|
||||
static void setTransferSizeLimitForUnitTest(long theTransferSizeLimit) {
|
||||
if (theTransferSizeLimit <= 0) {
|
||||
ourTransferSizeLimit = DEFAULT_TRANSFER_SIZE_LIMIT;
|
||||
}else {
|
||||
ourTransferSizeLimit = theTransferSizeLimit;
|
||||
}
|
||||
}
|
||||
|
||||
static String stripPath(String thePath) {
|
||||
String retVal = thePath;
|
||||
if (retVal.contains("/")) {
|
||||
retVal = retVal.substring(retVal.lastIndexOf("/"));
|
||||
|
|
|
@ -37,6 +37,12 @@
|
|||
<logger name="ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl" additivity="false" level="info">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</logger>
|
||||
<logger name="ca.uhn.fhir.jpa.term.TermCodeSystemStorageSvcImpl" additivity="false" level="info">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</logger>
|
||||
<logger name="ca.uhn.fhir.jpa.term.TermDeferredStorageSvcImpl" additivity="false" level="info">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</logger>
|
||||
|
||||
<!--
|
||||
Always log the migrator
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
package ca.uhn.fhir.cli;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.BaseTest;
|
||||
import ca.uhn.fhir.test.BaseTest;
|
||||
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
|
||||
import ca.uhn.fhir.jpa.term.UploadStatistics;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermLoaderSvc;
|
||||
import ca.uhn.fhir.jpa.term.custom.CustomTerminologySet;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.test.utilities.JettyUtil;
|
||||
import com.google.common.base.Charsets;
|
||||
|
@ -185,6 +184,37 @@ public class UploadTerminologyCommandTest extends BaseTest {
|
|||
assertThat(IOUtils.toByteArray(listOfDescriptors.get(0).getInputStream()).length, greaterThan(100));
|
||||
}
|
||||
|
||||
/**
|
||||
* When transferring large files, we use a local file to store the binary instead of
|
||||
* using HTTP to transfer a giant base 64 encoded attachment. Hopefully we can
|
||||
* replace this with a bulk data import at some point when that gets implemented.
|
||||
*/
|
||||
@Test
|
||||
public void testSnapshotLargeFile() throws IOException {
|
||||
UploadTerminologyCommand.setTransferSizeLimitForUnitTest(10);
|
||||
|
||||
writeConceptAndHierarchyFiles();
|
||||
|
||||
when(myTermLoaderSvc.loadCustom(any(), anyList(), any())).thenReturn(new UploadStatistics(100, new IdType("CodeSystem/101")));
|
||||
|
||||
App.main(new String[]{
|
||||
UploadTerminologyCommand.UPLOAD_TERMINOLOGY,
|
||||
"-v", "r4",
|
||||
"-m", "SNAPSHOT",
|
||||
"-t", "http://localhost:" + myPort,
|
||||
"-u", "http://foo",
|
||||
"-d", myConceptsFileName,
|
||||
"-d", myHierarchyFileName
|
||||
});
|
||||
|
||||
verify(myTermLoaderSvc, times(1)).loadCustom(any(), myDescriptorListCaptor.capture(), any());
|
||||
|
||||
List<ITermLoaderSvc.FileDescriptor> listOfDescriptors = myDescriptorListCaptor.getValue();
|
||||
assertEquals(1, listOfDescriptors.size());
|
||||
assertThat(listOfDescriptors.get(0).getFilename(), matchesPattern(".*\\.zip$"));
|
||||
assertThat(IOUtils.toByteArray(listOfDescriptors.get(0).getInputStream()).length, greaterThan(100));
|
||||
}
|
||||
|
||||
|
||||
private void writeConceptAndHierarchyFiles() throws IOException {
|
||||
try (FileWriter w = new FileWriter(myConceptsFile, false)) {
|
||||
|
@ -229,6 +259,8 @@ public class UploadTerminologyCommandTest extends BaseTest {
|
|||
FileUtils.deleteQuietly(myConceptsFile);
|
||||
FileUtils.deleteQuietly(myHierarchyFile);
|
||||
FileUtils.deleteQuietly(myArchiveFile);
|
||||
|
||||
UploadTerminologyCommand.setTransferSizeLimitForUnitTest(-1);
|
||||
}
|
||||
|
||||
@Before
|
||||
|
|
|
@ -290,7 +290,7 @@ public class ValidatorExamples {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
|
||||
// TODO: implement (or return null if your implementation does not support this function)
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -123,7 +123,7 @@ public class IgPackValidationSupportDstu3 implements IValidationSupport {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -74,6 +74,10 @@ public class DaoConfig {
|
|||
)));
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(DaoConfig.class);
|
||||
private static final int DEFAULT_EXPUNGE_BATCH_SIZE = 800;
|
||||
|
||||
// update setter javadoc if default changes
|
||||
public static final int DEFAULT_MAX_EXPANSION_SIZE = 1000;
|
||||
|
||||
private IndexEnabledEnum myIndexMissingFieldsEnabled = IndexEnabledEnum.DISABLED;
|
||||
|
||||
/**
|
||||
|
@ -115,10 +119,7 @@ public class DaoConfig {
|
|||
* update setter javadoc if default changes
|
||||
*/
|
||||
private boolean myIndexContainedResources = true;
|
||||
/**
|
||||
* update setter javadoc if default changes
|
||||
*/
|
||||
private int myMaximumExpansionSize = 5000;
|
||||
private int myMaximumExpansionSize = DEFAULT_MAX_EXPANSION_SIZE;
|
||||
private Integer myMaximumSearchResultCountInTransaction = DEFAULT_MAXIMUM_SEARCH_RESULT_COUNT_IN_TRANSACTION;
|
||||
private ResourceEncodingEnum myResourceEncoding = ResourceEncodingEnum.JSONC;
|
||||
/**
|
||||
|
@ -564,8 +565,12 @@ public class DaoConfig {
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum number of codes that will be added to a valueset expansion before
|
||||
* the operation will be failed as too costly
|
||||
* Sets the maximum number of codes that will be added to an in-memory valueset expansion before
|
||||
* the operation will be failed as too costly. Note that this setting applies only to
|
||||
* in-memory expansions and does not apply to expansions that are being pre-calculated.
|
||||
* <p>
|
||||
* The default value for this setting is 1000.
|
||||
* </p>
|
||||
*/
|
||||
public void setMaximumExpansionSize(int theMaximumExpansionSize) {
|
||||
Validate.isTrue(theMaximumExpansionSize > 0, "theMaximumExpansionSize must be > 0");
|
||||
|
|
|
@ -41,8 +41,8 @@ public interface ITermConceptDao extends JpaRepository<TermConcept, Long> {
|
|||
@Query("SELECT c FROM TermConcept c WHERE c.myCodeSystem = :code_system AND c.myCode = :code")
|
||||
Optional<TermConcept> findByCodeSystemAndCode(@Param("code_system") TermCodeSystemVersion theCodeSystem, @Param("code") String theCode);
|
||||
|
||||
@Query("SELECT t FROM TermConcept t WHERE t.myCodeSystem.myId = :cs_pid")
|
||||
Slice<TermConcept> findByCodeSystemVersion(Pageable thePage, @Param("cs_pid") Long thePid);
|
||||
@Query("SELECT t.myId FROM TermConcept t WHERE t.myCodeSystem.myId = :cs_pid")
|
||||
Slice<Long> findIdsByCodeSystemVersion(Pageable thePage, @Param("cs_pid") Long thePid);
|
||||
|
||||
@Query("SELECT c FROM TermConcept c WHERE c.myCodeSystem = :code_system")
|
||||
List<TermConcept> findByCodeSystemVersion(@Param("code_system") TermCodeSystemVersion theCodeSystem);
|
||||
|
@ -50,8 +50,4 @@ public interface ITermConceptDao extends JpaRepository<TermConcept, Long> {
|
|||
@Query("SELECT t FROM TermConcept t WHERE t.myIndexStatus = null")
|
||||
Page<TermConcept> findResourcesRequiringReindexing(Pageable thePageRequest);
|
||||
|
||||
@Query("UPDATE TermConcept t SET t.myIndexStatus = null")
|
||||
@Modifying
|
||||
int markAllForReindexing();
|
||||
|
||||
}
|
||||
|
|
|
@ -29,8 +29,8 @@ import org.springframework.data.repository.query.Param;
|
|||
|
||||
public interface ITermConceptDesignationDao extends JpaRepository<TermConceptDesignation, Long> {
|
||||
|
||||
@Query("SELECT t FROM TermConceptDesignation t WHERE t.myCodeSystemVersion.myId = :csv_pid")
|
||||
Slice<TermConceptDesignation> findByCodeSystemVersion(Pageable thePage, @Param("csv_pid") Long thePid);
|
||||
@Query("SELECT t.myId FROM TermConceptDesignation t WHERE t.myCodeSystemVersion.myId = :csv_pid")
|
||||
Slice<Long> findIdsByCodeSystemVersion(Pageable thePage, @Param("csv_pid") Long thePid);
|
||||
|
||||
@Query("SELECT COUNT(t) FROM TermConceptDesignation t WHERE t.myCodeSystemVersion.myId = :csv_pid")
|
||||
Integer countByCodeSystemVersion(@Param("csv_pid") Long thePid);
|
||||
|
|
|
@ -37,7 +37,6 @@ public interface ITermConceptParentChildLinkDao extends JpaRepository<TermConcep
|
|||
@Query("SELECT t.myParentPid FROM TermConceptParentChildLink t WHERE t.myChildPid = :child_pid")
|
||||
Collection<Long> findAllWithChild(@Param("child_pid") Long theConceptPid);
|
||||
|
||||
@Query("SELECT t FROM TermConceptParentChildLink t WHERE t.myCodeSystem.myId = :cs_pid")
|
||||
Slice<TermConceptParentChildLink> findByCodeSystemVersion(Pageable thePage, @Param("cs_pid") Long thePid);
|
||||
|
||||
@Query("SELECT t.myPid FROM TermConceptParentChildLink t WHERE t.myCodeSystem.myId = :cs_pid")
|
||||
Slice<Long> findIdsByCodeSystemVersion(Pageable thePage, @Param("cs_pid") Long thePid);
|
||||
}
|
||||
|
|
|
@ -29,8 +29,8 @@ import org.springframework.data.repository.query.Param;
|
|||
|
||||
public interface ITermConceptPropertyDao extends JpaRepository<TermConceptProperty, Long> {
|
||||
|
||||
@Query("SELECT t FROM TermConceptProperty t WHERE t.myCodeSystemVersion.myId = :cs_pid")
|
||||
Slice<TermConceptProperty> findByCodeSystemVersion(Pageable thePage, @Param("cs_pid") Long thePid);
|
||||
@Query("SELECT t.myId FROM TermConceptProperty t WHERE t.myCodeSystemVersion.myId = :cs_pid")
|
||||
Slice<Long> findIdsByCodeSystemVersion(Pageable thePage, @Param("cs_pid") Long thePid);
|
||||
|
||||
@Query("SELECT COUNT(t) FROM TermConceptProperty t WHERE t.myCodeSystemVersion.myId = :cs_pid")
|
||||
Integer countByCodeSystemVersion(@Param("cs_pid") Long thePid);
|
||||
|
|
|
@ -93,7 +93,7 @@ public class JpaValidationSupportDstu3 extends BaseJpaValidationSupport implemen
|
|||
|
||||
@Override
|
||||
@Transactional(value = TxType.SUPPORTS)
|
||||
public CodeValidationResult validateCode(FhirContext theCtx, String theCodeSystem, String theCode, String theDisplay) {
|
||||
public CodeValidationResult validateCode(FhirContext theCtx, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -99,7 +99,7 @@ public class FhirResourceDaoValueSetR4 extends FhirResourceDaoR4<ValueSet> imple
|
|||
}
|
||||
}
|
||||
if (allSystemsAreSuppportedByTerminologyService) {
|
||||
return myTerminologySvc.expandValueSet(theSource);
|
||||
return myTerminologySvc.expandValueSetInMemory(theSource, null);
|
||||
}
|
||||
|
||||
HapiWorkerContext workerContext = new HapiWorkerContext(getContext(), myValidationSupport);
|
||||
|
@ -111,12 +111,6 @@ public class FhirResourceDaoValueSetR4 extends FhirResourceDaoR4<ValueSet> imple
|
|||
|
||||
return retVal;
|
||||
|
||||
// ValueSetExpansionComponent expansion = outcome.getValueset().getExpansion();
|
||||
//
|
||||
// ValueSet retVal = new ValueSet();
|
||||
// retVal.getMeta().setLastUpdated(new Date());
|
||||
// retVal.setExpansion(expansion);
|
||||
// return retVal;
|
||||
}
|
||||
|
||||
private ValueSet doExpand(ValueSet theSource, int theOffset, int theCount) {
|
||||
|
|
|
@ -85,7 +85,7 @@ public class JpaValidationSupportR4 extends BaseJpaValidationSupport implements
|
|||
|
||||
@Override
|
||||
@Transactional(value = TxType.SUPPORTS)
|
||||
public IValidationSupport.CodeValidationResult validateCode(FhirContext theCtx, String theCodeSystem, String theCode, String theDisplay) {
|
||||
public IValidationSupport.CodeValidationResult validateCode(FhirContext theCtx, String theCodeSystem, String theCode, String theDisplay, String theSystemUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -87,7 +87,7 @@ public class JpaValidationSupportR5 extends BaseJpaValidationSupport implements
|
|||
|
||||
@Override
|
||||
@Transactional(value = TxType.SUPPORTS)
|
||||
public CodeValidationResult validateCode(FhirContext theCtx, String theCodeSystem, String theCode, String theDisplay) {
|
||||
public CodeValidationResult validateCode(FhirContext theCtx, String theCodeSystem, String theCode, String theDisplay, String theSystemUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ package ca.uhn.fhir.jpa.entity;
|
|||
import ca.uhn.fhir.context.support.IContextValidationSupport;
|
||||
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
|
||||
import ca.uhn.fhir.jpa.search.DeferConceptIndexingInterceptor;
|
||||
import ca.uhn.fhir.jpa.term.VersionIndependentConcept;
|
||||
import ca.uhn.fhir.util.ValidateUtil;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
|
@ -407,4 +408,7 @@ public class TermConcept implements Serializable {
|
|||
}
|
||||
|
||||
|
||||
public VersionIndependentConcept toVersionIndependentConcept() {
|
||||
return new VersionIndependentConcept(myCodeSystem.getCodeSystem().getCodeSystemUri(), myCode);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,6 @@ import ca.uhn.fhir.rest.annotation.OperationParam;
|
|||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||
import ca.uhn.fhir.util.AttachmentUtil;
|
||||
import ca.uhn.fhir.util.ParametersUtil;
|
||||
import ca.uhn.fhir.util.ValidateUtil;
|
||||
|
@ -40,7 +39,10 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.*;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -86,7 +88,6 @@ public class TerminologyUploaderProvider extends BaseJpaProvider {
|
|||
public IBaseParameters uploadSnapshot(
|
||||
HttpServletRequest theServletRequest,
|
||||
@OperationParam(name = PARAM_SYSTEM, min = 1, typeName = "uri") IPrimitiveType<String> theCodeSystemUrl,
|
||||
@OperationParam(name = "localfile", min = 1, max = OperationParam.MAX_UNLIMITED, typeName = "string") List<IPrimitiveType<String>> theLocalFile,
|
||||
@OperationParam(name = PARAM_FILE, min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "attachment") List<ICompositeType> theFiles,
|
||||
RequestDetails theRequestDetails
|
||||
) {
|
||||
|
@ -97,66 +98,15 @@ public class TerminologyUploaderProvider extends BaseJpaProvider {
|
|||
throw new InvalidRequestException("Missing mandatory parameter: " + PARAM_SYSTEM);
|
||||
}
|
||||
|
||||
if (theLocalFile == null || theLocalFile.size() == 0) {
|
||||
if (theFiles == null || theFiles.size() == 0) {
|
||||
throw new InvalidRequestException("No 'localfile' or 'package' parameter, or package had no data");
|
||||
throw new InvalidRequestException("No '" + PARAM_FILE + "' parameter, or package had no data");
|
||||
}
|
||||
for (ICompositeType next : theFiles) {
|
||||
ValidateUtil.isTrueOrThrowInvalidRequest(myCtx.getElementDefinition(next.getClass()).getName().equals("Attachment"), "Package must be of type Attachment");
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
List<ITermLoaderSvc.FileDescriptor> localFiles = new ArrayList<>();
|
||||
if (theLocalFile != null && theLocalFile.size() > 0) {
|
||||
for (IPrimitiveType<String> nextLocalFile : theLocalFile) {
|
||||
if (isNotBlank(nextLocalFile.getValue())) {
|
||||
ourLog.info("Reading in local file: {}", nextLocalFile.getValue());
|
||||
File nextFile = new File(nextLocalFile.getValue());
|
||||
if (!nextFile.exists() || !nextFile.isFile()) {
|
||||
throw new InvalidRequestException("Unknown file: " + nextFile.getName());
|
||||
}
|
||||
localFiles.add(new ITermLoaderSvc.FileDescriptor() {
|
||||
@Override
|
||||
public String getFilename() {
|
||||
return nextFile.getAbsolutePath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() {
|
||||
try {
|
||||
return new FileInputStream(nextFile);
|
||||
} catch (FileNotFoundException theE) {
|
||||
throw new InternalErrorException(theE);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (theFiles != null) {
|
||||
for (ICompositeType nextPackage : theFiles) {
|
||||
final String url = AttachmentUtil.getOrCreateUrl(myCtx, nextPackage).getValueAsString();
|
||||
|
||||
if (isBlank(url)) {
|
||||
throw new UnprocessableEntityException("Package is missing mandatory url element");
|
||||
}
|
||||
|
||||
localFiles.add(new ITermLoaderSvc.FileDescriptor() {
|
||||
@Override
|
||||
public String getFilename() {
|
||||
return url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() {
|
||||
byte[] data = AttachmentUtil.getOrCreateData(myCtx, nextPackage).getValue();
|
||||
return new ByteArrayInputStream(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
List<ITermLoaderSvc.FileDescriptor> localFiles = convertAttachmentsToFileDescriptors(theFiles);
|
||||
|
||||
String codeSystemUrl = theCodeSystemUrl.getValue();
|
||||
codeSystemUrl = trim(codeSystemUrl);
|
||||
|
@ -266,13 +216,31 @@ public class TerminologyUploaderProvider extends BaseJpaProvider {
|
|||
private List<ITermLoaderSvc.FileDescriptor> convertAttachmentsToFileDescriptors(@OperationParam(name = PARAM_FILE, min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "attachment") List<ICompositeType> theFiles) {
|
||||
List<ITermLoaderSvc.FileDescriptor> files = new ArrayList<>();
|
||||
for (ICompositeType next : theFiles) {
|
||||
byte[] nextData = AttachmentUtil.getOrCreateData(myCtx, next).getValue();
|
||||
|
||||
String nextUrl = AttachmentUtil.getOrCreateUrl(myCtx, next).getValue();
|
||||
ValidateUtil.isTrueOrThrowInvalidRequest(nextData != null && nextData.length > 0, "Missing Attachment.data value");
|
||||
ValidateUtil.isNotBlankOrThrowUnprocessableEntity(nextUrl, "Missing Attachment.url value");
|
||||
|
||||
byte[] nextData;
|
||||
if (nextUrl.startsWith("localfile:")) {
|
||||
String nextLocalFile = nextUrl.substring("localfile:".length());
|
||||
|
||||
|
||||
if (isNotBlank(nextLocalFile)) {
|
||||
ourLog.info("Reading in local file: {}", nextLocalFile);
|
||||
File nextFile = new File(nextLocalFile);
|
||||
if (!nextFile.exists() || !nextFile.isFile()) {
|
||||
throw new InvalidRequestException("Unknown file: " + nextFile.getName());
|
||||
}
|
||||
files.add(new FileBackedFileDescriptor(nextFile));
|
||||
}
|
||||
|
||||
} else {
|
||||
nextData = AttachmentUtil.getOrCreateData(myCtx, next).getValue();
|
||||
ValidateUtil.isTrueOrThrowInvalidRequest(nextData != null && nextData.length > 0, "Missing Attachment.data value");
|
||||
files.add(new ITermLoaderSvc.ByteArrayFileDescriptor(nextUrl, nextData));
|
||||
}
|
||||
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
|
@ -284,4 +252,25 @@ public class TerminologyUploaderProvider extends BaseJpaProvider {
|
|||
}
|
||||
|
||||
|
||||
private static class FileBackedFileDescriptor implements ITermLoaderSvc.FileDescriptor {
|
||||
private final File myNextFile;
|
||||
|
||||
public FileBackedFileDescriptor(File theNextFile) {
|
||||
myNextFile = theNextFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFilename() {
|
||||
return myNextFile.getAbsolutePath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() {
|
||||
try {
|
||||
return new FileInputStream(myNextFile);
|
||||
} catch (FileNotFoundException theE) {
|
||||
throw new InternalErrorException(theE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.term;
|
|||
*/
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.context.support.IContextValidationSupport;
|
||||
import ca.uhn.fhir.jpa.dao.*;
|
||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult;
|
||||
|
@ -30,10 +31,11 @@ import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
|
|||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.model.sched.ISchedulerService;
|
||||
import ca.uhn.fhir.jpa.model.sched.ScheduledJobDefinition;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
|
||||
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.ScrollableResultsIterator;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
|
@ -59,7 +61,9 @@ import org.hibernate.search.jpa.FullTextQuery;
|
|||
import org.hibernate.search.query.dsl.BooleanJunction;
|
||||
import org.hibernate.search.query.dsl.QueryBuilder;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||
import org.hl7.fhir.instance.model.api.IBaseCoding;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
import org.hl7.fhir.r4.model.*;
|
||||
import org.hl7.fhir.r4.model.codesystems.ConceptSubsumptionOutcome;
|
||||
|
@ -75,6 +79,8 @@ import org.springframework.transaction.PlatformTransactionManager;
|
|||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.interceptor.NoRollbackRuleAttribute;
|
||||
import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
@ -151,6 +157,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc, ApplicationCo
|
|||
private ITermDeferredStorageSvc myDeferredStorageSvc;
|
||||
@Autowired(required = false)
|
||||
private ITermCodeSystemStorageSvc myConceptStorageSvc;
|
||||
private IContextValidationSupport myValidationSupport;
|
||||
|
||||
private void addCodeIfNotAlreadyAdded(IValueSetConceptAccumulator theValueSetCodeAccumulator, Set<String> theAddedCodes, TermConcept theConcept, boolean theAdd, AtomicInteger theCodeCounter) {
|
||||
String codeSystem = theConcept.getCodeSystemVersion().getCodeSystem().getCodeSystemUri();
|
||||
|
@ -193,7 +200,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc, ApplicationCo
|
|||
if (retVal) {
|
||||
if (theSetToPopulate.size() >= myDaoConfig.getMaximumExpansionSize()) {
|
||||
String msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "expansionTooLarge", myDaoConfig.getMaximumExpansionSize());
|
||||
throw new InvalidRequestException(msg);
|
||||
throw new ExpansionTooCostlyException(msg);
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
|
@ -232,7 +239,6 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc, ApplicationCo
|
|||
myTranslationWithReverseCache.invalidateAll();
|
||||
}
|
||||
|
||||
|
||||
public void deleteConceptMap(ResourceTable theResourceTable) {
|
||||
// Get existing entity so it can be deleted.
|
||||
Optional<TermConceptMap> optionalExistingTermConceptMapById = myConceptMapDao.findTermConceptMapByResourcePid(theResourceTable.getId());
|
||||
|
@ -288,18 +294,18 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc, ApplicationCo
|
|||
deleteValueSet(theResourceTable);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.REQUIRED)
|
||||
public ValueSet expandValueSet(ValueSet theValueSetToExpand) {
|
||||
public ValueSet expandValueSetInMemory(ValueSet theValueSetToExpand, VersionIndependentConcept theWantConceptOrNull) {
|
||||
|
||||
ValueSetExpansionComponentWithConceptAccumulator expansionComponent = new ValueSetExpansionComponentWithConceptAccumulator();
|
||||
int maxCapacity = myDaoConfig.getMaximumExpansionSize();
|
||||
ValueSetExpansionComponentWithConceptAccumulator expansionComponent = new ValueSetExpansionComponentWithConceptAccumulator(myContext, maxCapacity);
|
||||
expansionComponent.setIdentifier(UUID.randomUUID().toString());
|
||||
expansionComponent.setTimestamp(new Date());
|
||||
|
||||
AtomicInteger codeCounter = new AtomicInteger(0);
|
||||
|
||||
expandValueSet(theValueSetToExpand, expansionComponent, codeCounter);
|
||||
expandValueSet(theValueSetToExpand, expansionComponent, codeCounter, theWantConceptOrNull);
|
||||
|
||||
expansionComponent.setTotal(codeCounter.get());
|
||||
|
||||
|
@ -327,7 +333,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc, ApplicationCo
|
|||
|
||||
if (!optionalTermValueSet.isPresent()) {
|
||||
ourLog.warn("ValueSet is not present in terminology tables. Will perform in-memory expansion without parameters. {}", getValueSetInfo(theValueSetToExpand));
|
||||
return expandValueSet(theValueSetToExpand); // In-memory expansion.
|
||||
return expandValueSetInMemory(theValueSetToExpand, null); // In-memory expansion.
|
||||
}
|
||||
|
||||
TermValueSet termValueSet = optionalTermValueSet.get();
|
||||
|
@ -335,7 +341,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc, ApplicationCo
|
|||
if (termValueSet.getExpansionStatus() != TermValueSetPreExpansionStatusEnum.EXPANDED) {
|
||||
ourLog.warn("{} is present in terminology tables but not ready for persistence-backed invocation of operation $expand. Will perform in-memory expansion without parameters. Current status: {} | {}",
|
||||
getValueSetInfo(theValueSetToExpand), termValueSet.getExpansionStatus().name(), termValueSet.getExpansionStatus().getDescription());
|
||||
return expandValueSet(theValueSetToExpand); // In-memory expansion.
|
||||
return expandValueSetInMemory(theValueSetToExpand, null); // In-memory expansion.
|
||||
}
|
||||
|
||||
ValueSet.ValueSetExpansionComponent expansionComponent = new ValueSet.ValueSetExpansionComponent();
|
||||
|
@ -431,11 +437,11 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc, ApplicationCo
|
|||
@Override
|
||||
@Transactional(propagation = Propagation.REQUIRED)
|
||||
public void expandValueSet(ValueSet theValueSetToExpand, IValueSetConceptAccumulator theValueSetCodeAccumulator) {
|
||||
expandValueSet(theValueSetToExpand, theValueSetCodeAccumulator, new AtomicInteger(0));
|
||||
expandValueSet(theValueSetToExpand, theValueSetCodeAccumulator, new AtomicInteger(0), null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
private void expandValueSet(ValueSet theValueSetToExpand, IValueSetConceptAccumulator theValueSetCodeAccumulator, AtomicInteger theCodeCounter) {
|
||||
private void expandValueSet(ValueSet theValueSetToExpand, IValueSetConceptAccumulator theValueSetCodeAccumulator, AtomicInteger theCodeCounter, VersionIndependentConcept theWantConceptOrNull) {
|
||||
Set<String> addedCodes = new HashSet<>();
|
||||
|
||||
StopWatch sw = new StopWatch();
|
||||
|
@ -449,7 +455,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc, ApplicationCo
|
|||
int queryIndex = i;
|
||||
Boolean shouldContinue = myTxTemplate.execute(t -> {
|
||||
boolean add = true;
|
||||
return expandValueSetHandleIncludeOrExclude(theValueSetCodeAccumulator, addedCodes, include, add, theCodeCounter, queryIndex);
|
||||
return expandValueSetHandleIncludeOrExclude(theValueSetCodeAccumulator, addedCodes, include, add, theCodeCounter, queryIndex, theWantConceptOrNull);
|
||||
});
|
||||
if (!shouldContinue) {
|
||||
break;
|
||||
|
@ -457,6 +463,12 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc, ApplicationCo
|
|||
}
|
||||
}
|
||||
|
||||
// If the accumulator filled up, abort
|
||||
if (theValueSetCodeAccumulator.getCapacityRemaining() != null && theValueSetCodeAccumulator.getCapacityRemaining() <= 0) {
|
||||
String msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "expansionTooLarge", myDaoConfig.getMaximumExpansionSize());
|
||||
throw new ExpansionTooCostlyException(msg);
|
||||
}
|
||||
|
||||
// Handle excludes
|
||||
ourLog.debug("Handling excludes");
|
||||
for (ValueSet.ConceptSetComponent exclude : theValueSetToExpand.getCompose().getExclude()) {
|
||||
|
@ -464,7 +476,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc, ApplicationCo
|
|||
int queryIndex = i;
|
||||
Boolean shouldContinue = myTxTemplate.execute(t -> {
|
||||
boolean add = false;
|
||||
return expandValueSetHandleIncludeOrExclude(theValueSetCodeAccumulator, addedCodes, exclude, add, theCodeCounter, queryIndex);
|
||||
return expandValueSetHandleIncludeOrExclude(theValueSetCodeAccumulator, addedCodes, exclude, add, theCodeCounter, queryIndex, null);
|
||||
});
|
||||
if (!shouldContinue) {
|
||||
break;
|
||||
|
@ -515,8 +527,8 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc, ApplicationCo
|
|||
return sb.toString();
|
||||
}
|
||||
|
||||
protected List<VersionIndependentConcept> expandValueSetAndReturnVersionIndependentConcepts(org.hl7.fhir.r4.model.ValueSet theValueSetToExpandR4) {
|
||||
org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent expandedR4 = expandValueSet(theValueSetToExpandR4).getExpansion();
|
||||
protected List<VersionIndependentConcept> expandValueSetAndReturnVersionIndependentConcepts(org.hl7.fhir.r4.model.ValueSet theValueSetToExpandR4, VersionIndependentConcept theWantConceptOrNull) {
|
||||
org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent expandedR4 = expandValueSetInMemory(theValueSetToExpandR4, theWantConceptOrNull).getExpansion();
|
||||
|
||||
ArrayList<VersionIndependentConcept> retVal = new ArrayList<>();
|
||||
for (org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent nextContains : expandedR4.getContains()) {
|
||||
|
@ -529,13 +541,18 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc, ApplicationCo
|
|||
/**
|
||||
* @return Returns true if there are potentially more results to process.
|
||||
*/
|
||||
private Boolean expandValueSetHandleIncludeOrExclude(IValueSetConceptAccumulator theValueSetCodeAccumulator, Set<String> theAddedCodes, ValueSet.ConceptSetComponent theIncludeOrExclude, boolean theAdd, AtomicInteger theCodeCounter, int theQueryIndex) {
|
||||
private Boolean expandValueSetHandleIncludeOrExclude(IValueSetConceptAccumulator theValueSetCodeAccumulator, Set<String> theAddedCodes, ValueSet.ConceptSetComponent theIncludeOrExclude, boolean theAdd, AtomicInteger theCodeCounter, int theQueryIndex, VersionIndependentConcept theWantConceptOrNull) {
|
||||
|
||||
String system = theIncludeOrExclude.getSystem();
|
||||
boolean hasSystem = isNotBlank(system);
|
||||
boolean hasValueSet = theIncludeOrExclude.getValueSet().size() > 0;
|
||||
|
||||
if (hasSystem) {
|
||||
|
||||
if (theWantConceptOrNull != null && !system.equals(theWantConceptOrNull.getSystem())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ourLog.info("Starting {} expansion around CodeSystem: {}", (theAdd ? "inclusion" : "exclusion"), system);
|
||||
|
||||
TermCodeSystem cs = myCodeSystemDao.findByCodeSystemUri(system);
|
||||
|
@ -561,6 +578,10 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc, ApplicationCo
|
|||
|
||||
bool.must(qb.keyword().onField("myCodeSystemVersionPid").matching(csv.getPid()).createQuery());
|
||||
|
||||
if (theWantConceptOrNull != null) {
|
||||
bool.must(qb.keyword().onField("myCode").matching(theWantConceptOrNull.getCode()).createQuery());
|
||||
}
|
||||
|
||||
/*
|
||||
* Filters
|
||||
*/
|
||||
|
@ -603,6 +624,21 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc, ApplicationCo
|
|||
AtomicInteger count = new AtomicInteger(0);
|
||||
|
||||
int maxResultsPerBatch = 10000;
|
||||
|
||||
/*
|
||||
* If the accumulator is bounded, we may reduce the size of the query to
|
||||
* Lucene in order to be more efficient.
|
||||
*/
|
||||
if (theAdd) {
|
||||
Integer accumulatorCapacityRemaining = theValueSetCodeAccumulator.getCapacityRemaining();
|
||||
if (accumulatorCapacityRemaining != null) {
|
||||
maxResultsPerBatch = Math.min(maxResultsPerBatch, accumulatorCapacityRemaining + 1);
|
||||
}
|
||||
if (maxResultsPerBatch <= 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
jpaQuery.setMaxResults(maxResultsPerBatch);
|
||||
jpaQuery.setFirstResult(theQueryIndex * maxResultsPerBatch);
|
||||
|
||||
|
@ -618,7 +654,11 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc, ApplicationCo
|
|||
count.incrementAndGet();
|
||||
countForBatch.incrementAndGet();
|
||||
TermConcept concept = (TermConcept) next;
|
||||
try {
|
||||
addCodeIfNotAlreadyAdded(theValueSetCodeAccumulator, theAddedCodes, concept, theAdd, theCodeCounter);
|
||||
} catch (ExpansionTooCostlyException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ourLog.info("Batch expansion for {} with starting index of {} produced {} results in {}ms", (theAdd ? "inclusion" : "exclusion"), firstResult, countForBatch, swForBatch.getMillis());
|
||||
|
@ -641,6 +681,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc, ApplicationCo
|
|||
if (!theIncludeOrExclude.getConcept().isEmpty()) {
|
||||
for (ValueSet.ConceptReferenceComponent next : theIncludeOrExclude.getConcept()) {
|
||||
String nextCode = next.getCode();
|
||||
if (theWantConceptOrNull == null || theWantConceptOrNull.getCode().equals(nextCode)) {
|
||||
if (isNoneBlank(system, nextCode) && !theAddedCodes.contains(system + "|" + nextCode)) {
|
||||
CodeSystem.ConceptDefinitionComponent code = findCode(codeSystemFromContext.getConcept(), nextCode);
|
||||
if (code != null) {
|
||||
|
@ -653,8 +694,13 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc, ApplicationCo
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
List<CodeSystem.ConceptDefinitionComponent> concept = codeSystemFromContext.getConcept();
|
||||
concept = concept
|
||||
.stream()
|
||||
.filter(t -> theWantConceptOrNull == null || t.getCode().equals(theWantConceptOrNull.getCode()))
|
||||
.collect(Collectors.toList());
|
||||
addConceptsToList(theValueSetCodeAccumulator, theAddedCodes, system, concept, theAdd);
|
||||
}
|
||||
|
||||
|
@ -1217,7 +1263,6 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc, ApplicationCo
|
|||
return myCodeSystemDao.findByCodeSystemUri(theSystem);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext theApplicationContext) throws BeansException {
|
||||
myApplicationContext = theApplicationContext;
|
||||
|
@ -1226,7 +1271,12 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc, ApplicationCo
|
|||
@PostConstruct
|
||||
public void start() {
|
||||
myValueSetResourceDao = myApplicationContext.getBean(IFhirResourceDaoValueSet.class);
|
||||
myTxTemplate = new TransactionTemplate(myTransactionManager);
|
||||
if (myContext.getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.DSTU3)) {
|
||||
myValidationSupport = myApplicationContext.getBean(IContextValidationSupport.class);
|
||||
}
|
||||
RuleBasedTransactionAttribute rules = new RuleBasedTransactionAttribute();
|
||||
rules.getRollbackRules().add(new NoRollbackRuleAttribute(ExpansionTooCostlyException.class));
|
||||
myTxTemplate = new TransactionTemplate(myTransactionManager, rules);
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
|
@ -1492,6 +1542,8 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc, ApplicationCo
|
|||
return new IFhirResourceDaoCodeSystem.SubsumesResult(subsumes);
|
||||
}
|
||||
|
||||
protected abstract ValueSet toCanonicalValueSet(IBaseResource theValueSet);
|
||||
|
||||
protected IContextValidationSupport.LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) {
|
||||
TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager);
|
||||
return txTemplate.execute(t -> {
|
||||
|
@ -1753,11 +1805,36 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc, ApplicationCo
|
|||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
protected void throwInvalidValueSet(String theValueSet) {
|
||||
void throwInvalidValueSet(String theValueSet) {
|
||||
throw new ResourceNotFoundException("Unknown ValueSet: " + UrlUtil.escapeUrlParam(theValueSet));
|
||||
}
|
||||
|
||||
Optional<VersionIndependentConcept> validateCodeInValueSet(String theValueSetUrl, String theCodeSystem, String theCode) {
|
||||
IBaseResource valueSet = myValidationSupport.fetchValueSet(myContext, theValueSetUrl);
|
||||
|
||||
// If we don't have a PID, this came from some source other than the JPA
|
||||
// database, so we don't need to check if it's pre-expanded or not
|
||||
if (valueSet instanceof IAnyResource) {
|
||||
Long pid = IDao.RESOURCE_PID.get((IAnyResource) valueSet);
|
||||
if (pid != null) {
|
||||
if (isValueSetPreExpandedForCodeValidation(valueSet)) {
|
||||
ValidateCodeResult outcome = validateCodeIsInPreExpandedValueSet(valueSet, theCodeSystem, theCode, null, null, null);
|
||||
if (outcome != null && outcome.isResult()) {
|
||||
return Optional.of(new VersionIndependentConcept(theCodeSystem, theCode));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ValueSet canonicalValueSet = toCanonicalValueSet(valueSet);
|
||||
VersionIndependentConcept wantConcept = new VersionIndependentConcept(theCodeSystem, theCode);
|
||||
List<VersionIndependentConcept> expansionOutcome = expandValueSetAndReturnVersionIndependentConcepts(canonicalValueSet, wantConcept);
|
||||
return expansionOutcome
|
||||
.stream()
|
||||
.filter(t -> t.getSystem().equals(theCodeSystem) && t.getCode().equals(theCode))
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
public static class PreExpandValueSetsJob implements Job {
|
||||
|
||||
@Autowired
|
||||
|
@ -1881,5 +1958,4 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc, ApplicationCo
|
|||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ package ca.uhn.fhir.jpa.term;
|
|||
|
||||
import ca.uhn.fhir.jpa.entity.TermConceptDesignation;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collection;
|
||||
|
||||
public interface IValueSetConceptAccumulator {
|
||||
|
@ -32,4 +33,9 @@ public interface IValueSetConceptAccumulator {
|
|||
|
||||
void excludeConcept(String theSystem, String theCode);
|
||||
|
||||
@Nullable
|
||||
default Integer getCapacityRemaining() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -451,7 +451,7 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc {
|
|||
// Parent/Child links
|
||||
{
|
||||
String descriptor = "parent/child links";
|
||||
Supplier<Slice<TermConceptParentChildLink>> loader = () -> myConceptParentChildLinkDao.findByCodeSystemVersion(page1000, theCodeSystemVersionPid);
|
||||
Supplier<Slice<Long>> loader = () -> myConceptParentChildLinkDao.findIdsByCodeSystemVersion(page1000, theCodeSystemVersionPid);
|
||||
Supplier<Integer> counter = () -> myConceptParentChildLinkDao.countByCodeSystemVersion(theCodeSystemVersionPid);
|
||||
doDelete(descriptor, loader, counter, myConceptParentChildLinkDao);
|
||||
}
|
||||
|
@ -459,7 +459,7 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc {
|
|||
// Properties
|
||||
{
|
||||
String descriptor = "concept properties";
|
||||
Supplier<Slice<TermConceptProperty>> loader = () -> myConceptPropertyDao.findByCodeSystemVersion(page1000, theCodeSystemVersionPid);
|
||||
Supplier<Slice<Long>> loader = () -> myConceptPropertyDao.findIdsByCodeSystemVersion(page1000, theCodeSystemVersionPid);
|
||||
Supplier<Integer> counter = () -> myConceptPropertyDao.countByCodeSystemVersion(theCodeSystemVersionPid);
|
||||
doDelete(descriptor, loader, counter, myConceptPropertyDao);
|
||||
}
|
||||
|
@ -467,7 +467,7 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc {
|
|||
// Designations
|
||||
{
|
||||
String descriptor = "concept designations";
|
||||
Supplier<Slice<TermConceptDesignation>> loader = () -> myConceptDesignationDao.findByCodeSystemVersion(page1000, theCodeSystemVersionPid);
|
||||
Supplier<Slice<Long>> loader = () -> myConceptDesignationDao.findIdsByCodeSystemVersion(page1000, theCodeSystemVersionPid);
|
||||
Supplier<Integer> counter = () -> myConceptDesignationDao.countByCodeSystemVersion(theCodeSystemVersionPid);
|
||||
doDelete(descriptor, loader, counter, myConceptDesignationDao);
|
||||
}
|
||||
|
@ -477,7 +477,7 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc {
|
|||
String descriptor = "concepts";
|
||||
// For some reason, concepts are much slower to delete, so use a smaller batch size
|
||||
PageRequest page100 = PageRequest.of(0, 100);
|
||||
Supplier<Slice<TermConcept>> loader = () -> myConceptDao.findByCodeSystemVersion(page100, theCodeSystemVersionPid);
|
||||
Supplier<Slice<Long>> loader = () -> myConceptDao.findIdsByCodeSystemVersion(page100, theCodeSystemVersionPid);
|
||||
Supplier<Integer> counter = () -> myConceptDao.countByCodeSystemVersion(theCodeSystemVersionPid);
|
||||
doDelete(descriptor, loader, counter, myConceptDao);
|
||||
}
|
||||
|
@ -718,14 +718,14 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc {
|
|||
}
|
||||
|
||||
|
||||
private <T> void doDelete(String theDescriptor, Supplier<Slice<T>> theLoader, Supplier<Integer> theCounter, JpaRepository<T, ?> theDao) {
|
||||
private <T> void doDelete(String theDescriptor, Supplier<Slice<Long>> theLoader, Supplier<Integer> theCounter, JpaRepository<T, Long> theDao) {
|
||||
int count;
|
||||
ourLog.info(" * Deleting {}", theDescriptor);
|
||||
int totalCount = theCounter.get();
|
||||
StopWatch sw = new StopWatch();
|
||||
count = 0;
|
||||
while (true) {
|
||||
Slice<T> link = theLoader.get();
|
||||
Slice<Long> link = theLoader.get();
|
||||
if (!link.hasContent()) {
|
||||
break;
|
||||
}
|
||||
|
@ -733,7 +733,7 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc {
|
|||
TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager);
|
||||
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
|
||||
txTemplate.execute(t -> {
|
||||
theDao.deleteAll(link);
|
||||
link.forEach(id -> theDao.deleteById(id));
|
||||
return null;
|
||||
});
|
||||
|
||||
|
|
|
@ -183,6 +183,15 @@ public class TermDeferredStorageSvcImpl implements ITermDeferredStorageSvc {
|
|||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
|
||||
if (!isDeferredConcepts() &&
|
||||
!isConceptLinksToSaveLater() &&
|
||||
!isDeferredValueSets() &&
|
||||
!isDeferredConceptMaps()) {
|
||||
return;
|
||||
}
|
||||
|
||||
TransactionTemplate tt = new TransactionTemplate(myTransactionMgr);
|
||||
tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
|
||||
if (isDeferredConceptsOrConceptLinksToSaveLater()) {
|
||||
|
@ -206,6 +215,7 @@ public class TermDeferredStorageSvcImpl implements ITermDeferredStorageSvc {
|
|||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStorageQueueEmpty() {
|
||||
|
|
|
@ -72,6 +72,11 @@ public class TermReadSvcDstu2 extends BaseTermReadSvcImpl {
|
|||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ValueSet toCanonicalValueSet(IBaseResource theValueSet) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBaseResource expandValueSet(IBaseResource theValueSetToExpand) {
|
||||
throw new UnsupportedOperationException();
|
||||
|
|
|
@ -3,7 +3,6 @@ package ca.uhn.fhir.jpa.term;
|
|||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult;
|
||||
import ca.uhn.fhir.jpa.entity.TermConcept;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermReadSvcDstu3;
|
||||
|
@ -103,8 +102,8 @@ public class TermReadSvcDstu3 extends BaseTermReadSvcImpl implements IValidation
|
|||
|
||||
try {
|
||||
org.hl7.fhir.r4.model.ValueSet valueSetToExpandR4;
|
||||
valueSetToExpandR4 = VersionConvertor_30_40.convertValueSet(valueSetToExpand);
|
||||
org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent expandedR4 = super.expandValueSet(valueSetToExpandR4).getExpansion();
|
||||
valueSetToExpandR4 = toCanonicalValueSet(valueSetToExpand);
|
||||
org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent expandedR4 = super.expandValueSetInMemory(valueSetToExpandR4, null).getExpansion();
|
||||
return VersionConvertor_30_40.convertValueSetExpansionComponent(expandedR4);
|
||||
} catch (FHIRException e) {
|
||||
throw new InternalErrorException(e);
|
||||
|
@ -117,21 +116,28 @@ public class TermReadSvcDstu3 extends BaseTermReadSvcImpl implements IValidation
|
|||
|
||||
try {
|
||||
org.hl7.fhir.r4.model.ValueSet valueSetToExpandR4;
|
||||
valueSetToExpandR4 = VersionConvertor_30_40.convertValueSet(valueSetToExpand);
|
||||
org.hl7.fhir.r4.model.ValueSet expandedR4 = super.expandValueSet(valueSetToExpandR4);
|
||||
valueSetToExpandR4 = toCanonicalValueSet(valueSetToExpand);
|
||||
org.hl7.fhir.r4.model.ValueSet expandedR4 = super.expandValueSetInMemory(valueSetToExpandR4, null);
|
||||
return VersionConvertor_30_40.convertValueSet(expandedR4);
|
||||
} catch (FHIRException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected org.hl7.fhir.r4.model.ValueSet toCanonicalValueSet(IBaseResource theValueSet) throws FHIRException {
|
||||
org.hl7.fhir.r4.model.ValueSet valueSetToExpandR4;
|
||||
valueSetToExpandR4 = VersionConvertor_30_40.convertValueSet((ValueSet) theValueSet);
|
||||
return valueSetToExpandR4;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBaseResource expandValueSet(IBaseResource theInput, int theOffset, int theCount) {
|
||||
ValueSet valueSetToExpand = (ValueSet) theInput;
|
||||
|
||||
try {
|
||||
org.hl7.fhir.r4.model.ValueSet valueSetToExpandR4;
|
||||
valueSetToExpandR4 = VersionConvertor_30_40.convertValueSet(valueSetToExpand);
|
||||
valueSetToExpandR4 = toCanonicalValueSet(valueSetToExpand);
|
||||
org.hl7.fhir.r4.model.ValueSet expandedR4 = super.expandValueSet(valueSetToExpandR4, theOffset, theCount);
|
||||
return VersionConvertor_30_40.convertValueSet(expandedR4);
|
||||
} catch (FHIRException e) {
|
||||
|
@ -145,7 +151,7 @@ public class TermReadSvcDstu3 extends BaseTermReadSvcImpl implements IValidation
|
|||
|
||||
try {
|
||||
org.hl7.fhir.r4.model.ValueSet valueSetToExpandR4;
|
||||
valueSetToExpandR4 = VersionConvertor_30_40.convertValueSet(valueSetToExpand);
|
||||
valueSetToExpandR4 = toCanonicalValueSet(valueSetToExpand);
|
||||
super.expandValueSet(valueSetToExpandR4, theValueSetCodeAccumulator);
|
||||
} catch (FHIRException e) {
|
||||
throw new InternalErrorException(e);
|
||||
|
@ -162,13 +168,13 @@ public class TermReadSvcDstu3 extends BaseTermReadSvcImpl implements IValidation
|
|||
|
||||
org.hl7.fhir.r4.model.ValueSet valueSetToExpandR4;
|
||||
try {
|
||||
valueSetToExpandR4 = VersionConvertor_30_40.convertValueSet(vs);
|
||||
valueSetToExpandR4 = toCanonicalValueSet(vs);
|
||||
} catch (FHIRException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
|
||||
|
||||
return expandValueSetAndReturnVersionIndependentConcepts(valueSetToExpandR4);
|
||||
return expandValueSetAndReturnVersionIndependentConcepts(valueSetToExpandR4, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -262,7 +268,7 @@ public class TermReadSvcDstu3 extends BaseTermReadSvcImpl implements IValidation
|
|||
|
||||
org.hl7.fhir.r4.model.ValueSet valueSetR4;
|
||||
try {
|
||||
valueSetR4 = VersionConvertor_30_40.convertValueSet(valueSet);
|
||||
valueSetR4 = toCanonicalValueSet(valueSet);
|
||||
} catch (FHIRException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
|
@ -277,25 +283,30 @@ public class TermReadSvcDstu3 extends BaseTermReadSvcImpl implements IValidation
|
|||
|
||||
@CoverageIgnore
|
||||
@Override
|
||||
public IValidationSupport.CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
||||
public IValidationSupport.CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
|
||||
Optional<VersionIndependentConcept> codeOpt = Optional.empty();
|
||||
boolean haveValidated = false;
|
||||
|
||||
if (isNotBlank(theValueSetUrl)) {
|
||||
codeOpt = super.validateCodeInValueSet(theValueSetUrl, theCodeSystem, theCode);
|
||||
haveValidated = true;
|
||||
}
|
||||
|
||||
if (!haveValidated) {
|
||||
TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager);
|
||||
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
|
||||
return txTemplate.execute(t->{
|
||||
Optional<TermConcept> codeOpt = myTerminologySvc.findCode(theCodeSystem, theCode);
|
||||
if (codeOpt.isPresent()) {
|
||||
codeOpt = txTemplate.execute(t -> findCode(theCodeSystem, theCode).map(c->c.toVersionIndependentConcept()));
|
||||
}
|
||||
|
||||
if (codeOpt != null && codeOpt.isPresent()) {
|
||||
VersionIndependentConcept code = codeOpt.get();
|
||||
ConceptDefinitionComponent def = new ConceptDefinitionComponent();
|
||||
TermConcept code = codeOpt.get();
|
||||
def.setCode(code.getCode());
|
||||
def.setDisplay(code.getDisplay());
|
||||
IValidationSupport.CodeValidationResult retVal = new IValidationSupport.CodeValidationResult(def);
|
||||
retVal.setProperties(code.toValidationProperties());
|
||||
retVal.setCodeSystemName(code.getCodeSystemVersion().getCodeSystem().getName());
|
||||
return retVal;
|
||||
}
|
||||
|
||||
return new IValidationSupport.CodeValidationResult(IssueSeverity.ERROR, "Unknown code {" + theCodeSystem + "}" + theCode);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -3,9 +3,9 @@ package ca.uhn.fhir.jpa.term;
|
|||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult;
|
||||
import ca.uhn.fhir.jpa.entity.TermConcept;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermReadSvcR4;
|
||||
import ca.uhn.fhir.jpa.term.ex.ExpansionTooCostlyException;
|
||||
import ca.uhn.fhir.util.CoverageIgnore;
|
||||
import org.hl7.fhir.instance.model.api.IBaseDatatype;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
@ -21,6 +21,7 @@ import org.springframework.transaction.PlatformTransactionManager;
|
|||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import javax.transaction.Transactional;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
@ -90,13 +91,13 @@ public class TermReadSvcR4 extends BaseTermReadSvcImpl implements ITermReadSvcR4
|
|||
super.throwInvalidValueSet(theValueSet);
|
||||
}
|
||||
|
||||
return expandValueSetAndReturnVersionIndependentConcepts(vs);
|
||||
return expandValueSetAndReturnVersionIndependentConcepts(vs, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBaseResource expandValueSet(IBaseResource theInput) {
|
||||
ValueSet valueSetToExpand = (ValueSet) theInput;
|
||||
return super.expandValueSet(valueSetToExpand);
|
||||
return super.expandValueSetInMemory(valueSetToExpand, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -111,11 +112,12 @@ public class TermReadSvcR4 extends BaseTermReadSvcImpl implements ITermReadSvcR4
|
|||
super.expandValueSet(valueSetToExpand, theValueSetCodeAccumulator);
|
||||
}
|
||||
|
||||
@Transactional(dontRollbackOn = {ExpansionTooCostlyException.class})
|
||||
@Override
|
||||
public ValueSetExpander.ValueSetExpansionOutcome expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) {
|
||||
ValueSet valueSetToExpand = new ValueSet();
|
||||
valueSetToExpand.getCompose().addInclude(theInclude);
|
||||
ValueSet expanded = super.expandValueSet(valueSetToExpand);
|
||||
ValueSet expanded = super.expandValueSetInMemory(valueSetToExpand, null);
|
||||
return new ValueSetExpander.ValueSetExpansionOutcome(expanded);
|
||||
}
|
||||
|
||||
|
@ -214,25 +216,37 @@ public class TermReadSvcR4 extends BaseTermReadSvcImpl implements ITermReadSvcR4
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ValueSet toCanonicalValueSet(IBaseResource theValueSet) {
|
||||
return (ValueSet) theValueSet;
|
||||
}
|
||||
|
||||
@CoverageIgnore
|
||||
@Override
|
||||
public IValidationSupport.CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
||||
public IValidationSupport.CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
|
||||
Optional<VersionIndependentConcept> codeOpt = Optional.empty();
|
||||
boolean haveValidated = false;
|
||||
|
||||
if (isNotBlank(theValueSetUrl)) {
|
||||
codeOpt = super.validateCodeInValueSet(theValueSetUrl, theCodeSystem, theCode);
|
||||
haveValidated = true;
|
||||
}
|
||||
|
||||
if (!haveValidated) {
|
||||
TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager);
|
||||
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
|
||||
return txTemplate.execute(t-> {
|
||||
Optional<TermConcept> codeOpt = findCode(theCodeSystem, theCode);
|
||||
if (codeOpt.isPresent()) {
|
||||
TermConcept code = codeOpt.get();
|
||||
codeOpt = txTemplate.execute(t -> findCode(theCodeSystem, theCode).map(c->c.toVersionIndependentConcept()));
|
||||
}
|
||||
|
||||
if (codeOpt != null && codeOpt.isPresent()) {
|
||||
VersionIndependentConcept code = codeOpt.get();
|
||||
ConceptDefinitionComponent def = new ConceptDefinitionComponent();
|
||||
def.setCode(code.getCode());
|
||||
def.setDisplay(code.getDisplay());
|
||||
IValidationSupport.CodeValidationResult retVal = new IValidationSupport.CodeValidationResult(def);
|
||||
retVal.setProperties(code.toValidationProperties());
|
||||
return retVal;
|
||||
}
|
||||
|
||||
return new IValidationSupport.CodeValidationResult(IssueSeverity.ERROR, "Unknown code {" + theCodeSystem + "}" + theCode);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -3,7 +3,6 @@ package ca.uhn.fhir.jpa.term;
|
|||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult;
|
||||
import ca.uhn.fhir.jpa.entity.TermConcept;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermReadSvcR5;
|
||||
import ca.uhn.fhir.util.CoverageIgnore;
|
||||
|
@ -91,26 +90,26 @@ public class TermReadSvcR5 extends BaseTermReadSvcImpl implements IValidationSup
|
|||
super.throwInvalidValueSet(theValueSet);
|
||||
}
|
||||
|
||||
return expandValueSetAndReturnVersionIndependentConcepts(org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(valueSetR5));
|
||||
return expandValueSetAndReturnVersionIndependentConcepts(org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(valueSetR5), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBaseResource expandValueSet(IBaseResource theInput) {
|
||||
org.hl7.fhir.r4.model.ValueSet valueSetToExpand = org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet((ValueSet) theInput);
|
||||
org.hl7.fhir.r4.model.ValueSet valueSetR4 = super.expandValueSet(valueSetToExpand);
|
||||
org.hl7.fhir.r4.model.ValueSet valueSetToExpand = toCanonicalValueSet(theInput);
|
||||
org.hl7.fhir.r4.model.ValueSet valueSetR4 = super.expandValueSetInMemory(valueSetToExpand, null);
|
||||
return org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(valueSetR4);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBaseResource expandValueSet(IBaseResource theInput, int theOffset, int theCount) {
|
||||
org.hl7.fhir.r4.model.ValueSet valueSetToExpand = org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet((ValueSet) theInput);
|
||||
org.hl7.fhir.r4.model.ValueSet valueSetToExpand = toCanonicalValueSet(theInput);
|
||||
org.hl7.fhir.r4.model.ValueSet valueSetR4 = super.expandValueSet(valueSetToExpand, theOffset, theCount);
|
||||
return org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(valueSetR4);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void expandValueSet(IBaseResource theValueSetToExpand, IValueSetConceptAccumulator theValueSetCodeAccumulator) {
|
||||
org.hl7.fhir.r4.model.ValueSet valueSetToExpand = org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet((ValueSet) theValueSetToExpand);
|
||||
org.hl7.fhir.r4.model.ValueSet valueSetToExpand = toCanonicalValueSet(theValueSetToExpand);
|
||||
super.expandValueSet(valueSetToExpand, theValueSetCodeAccumulator);
|
||||
}
|
||||
|
||||
|
@ -118,7 +117,7 @@ public class TermReadSvcR5 extends BaseTermReadSvcImpl implements IValidationSup
|
|||
public ValueSetExpander.ValueSetExpansionOutcome expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) {
|
||||
ValueSet valueSetToExpand = new ValueSet();
|
||||
valueSetToExpand.getCompose().addInclude(theInclude);
|
||||
org.hl7.fhir.r4.model.ValueSet expandedR4 = super.expandValueSet(org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(valueSetToExpand));
|
||||
org.hl7.fhir.r4.model.ValueSet expandedR4 = super.expandValueSetInMemory(org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(valueSetToExpand), null);
|
||||
return new ValueSetExpander.ValueSetExpansionOutcome(org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(expandedR4));
|
||||
}
|
||||
|
||||
|
@ -221,23 +220,30 @@ public class TermReadSvcR5 extends BaseTermReadSvcImpl implements IValidationSup
|
|||
|
||||
@CoverageIgnore
|
||||
@Override
|
||||
public IValidationSupport.CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
||||
public IValidationSupport.CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
|
||||
Optional<VersionIndependentConcept> codeOpt = Optional.empty();
|
||||
boolean haveValidated = false;
|
||||
|
||||
if (isNotBlank(theValueSetUrl)) {
|
||||
codeOpt = super.validateCodeInValueSet(theValueSetUrl, theCodeSystem, theCode);
|
||||
haveValidated = true;
|
||||
}
|
||||
|
||||
if (!haveValidated) {
|
||||
TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager);
|
||||
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
|
||||
return txTemplate.execute(t -> {
|
||||
Optional<TermConcept> codeOpt = findCode(theCodeSystem, theCode);
|
||||
if (codeOpt.isPresent()) {
|
||||
TermConcept code = codeOpt.get();
|
||||
codeOpt = txTemplate.execute(t -> findCode(theCodeSystem, theCode).map(c->c.toVersionIndependentConcept()));
|
||||
}
|
||||
|
||||
if (codeOpt != null && codeOpt.isPresent()) {
|
||||
VersionIndependentConcept code = codeOpt.get();
|
||||
ConceptDefinitionComponent def = new ConceptDefinitionComponent();
|
||||
def.setCode(code.getCode());
|
||||
def.setDisplay(code.getDisplay());
|
||||
IValidationSupport.CodeValidationResult retVal = new IValidationSupport.CodeValidationResult(def);
|
||||
retVal.setProperties(code.toValidationProperties());
|
||||
return retVal;
|
||||
}
|
||||
|
||||
return new IValidationSupport.CodeValidationResult(IssueSeverity.ERROR, "Unknown code {" + theCodeSystem + "}" + theCode);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -249,7 +255,7 @@ public class TermReadSvcR5 extends BaseTermReadSvcImpl implements IValidationSup
|
|||
public ValidateCodeResult validateCodeIsInPreExpandedValueSet(IBaseResource theValueSet, String theSystem, String theCode, String theDisplay, IBaseDatatype theCoding, IBaseDatatype theCodeableConcept) {
|
||||
ValidateUtil.isNotNullOrThrowUnprocessableEntity(theValueSet, "ValueSet must not be null");
|
||||
ValueSet valueSet = (ValueSet) theValueSet;
|
||||
org.hl7.fhir.r4.model.ValueSet valueSetR4 = org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(valueSet);
|
||||
org.hl7.fhir.r4.model.ValueSet valueSetR4 = toCanonicalValueSet(valueSet);
|
||||
|
||||
Coding coding = (Coding) theCoding;
|
||||
org.hl7.fhir.r4.model.Coding codingR4 = null;
|
||||
|
@ -269,11 +275,16 @@ public class TermReadSvcR5 extends BaseTermReadSvcImpl implements IValidationSup
|
|||
return super.validateCodeIsInPreExpandedValueSet(valueSetR4, theSystem, theCode, theDisplay, codingR4, codeableConceptR4);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected org.hl7.fhir.r4.model.ValueSet toCanonicalValueSet(IBaseResource theValueSet) throws org.hl7.fhir.exceptions.FHIRException {
|
||||
return org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet((ValueSet) theValueSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValueSetPreExpandedForCodeValidation(IBaseResource theValueSet) {
|
||||
ValidateUtil.isNotNullOrThrowUnprocessableEntity(theValueSet, "ValueSet must not be null");
|
||||
ValueSet valueSet = (ValueSet) theValueSet;
|
||||
org.hl7.fhir.r4.model.ValueSet valueSetR4 = org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(valueSet);
|
||||
org.hl7.fhir.r4.model.ValueSet valueSetR4 = toCanonicalValueSet(valueSet);
|
||||
return super.isValueSetPreExpandedForCodeValidation(valueSetR4);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,22 +20,40 @@ package ca.uhn.fhir.jpa.term;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.entity.TermConceptDesignation;
|
||||
import ca.uhn.fhir.jpa.term.ex.ExpansionTooCostlyException;
|
||||
import ca.uhn.fhir.model.api.annotation.Block;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import org.hl7.fhir.r4.model.ValueSet;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collection;
|
||||
|
||||
@Block()
|
||||
public class ValueSetExpansionComponentWithConceptAccumulator extends ValueSet.ValueSetExpansionComponent implements IValueSetConceptAccumulator {
|
||||
private final int myMaxResults = 50000;
|
||||
private final int myMaxCapacity;
|
||||
private final FhirContext myContext;
|
||||
private int myConceptsCount;
|
||||
|
||||
public ValueSetExpansionComponentWithConceptAccumulator() {
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param theMaxCapacity The maximum number of results this accumulator will accept before throwing
|
||||
* an {@link InternalErrorException}
|
||||
*/
|
||||
ValueSetExpansionComponentWithConceptAccumulator(FhirContext theContext, int theMaxCapacity) {
|
||||
myContext = theContext;
|
||||
myMaxCapacity = theMaxCapacity;
|
||||
myConceptsCount = 0;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Integer getCapacityRemaining() {
|
||||
return myMaxCapacity - myConceptsCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void includeConcept(String theSystem, String theCode, String theDisplay) {
|
||||
incrementConceptsCount();
|
||||
|
@ -76,8 +94,9 @@ public class ValueSetExpansionComponentWithConceptAccumulator extends ValueSet.V
|
|||
}
|
||||
|
||||
private void incrementConceptsCount() {
|
||||
if (++myConceptsCount > myMaxResults) {
|
||||
throw new InternalErrorException("Expansion produced too many (>= " + myMaxResults + ") results");
|
||||
if (++myConceptsCount > myMaxCapacity) {
|
||||
String msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "expansionTooLarge", myMaxCapacity);
|
||||
throw new ExpansionTooCostlyException(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ import java.util.Set;
|
|||
*/
|
||||
public interface ITermReadSvc {
|
||||
|
||||
ValueSet expandValueSet(ValueSet theValueSetToExpand);
|
||||
ValueSet expandValueSetInMemory(ValueSet theValueSetToExpand, VersionIndependentConcept theWantConceptOrNull);
|
||||
|
||||
ValueSet expandValueSet(ValueSet theValueSetToExpand, int theOffset, int theCount);
|
||||
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package ca.uhn.fhir.jpa.term.ex;
|
||||
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
|
||||
public class ExpansionTooCostlyException extends InternalErrorException {
|
||||
|
||||
public ExpansionTooCostlyException(String theMessage) {
|
||||
super(theMessage);
|
||||
}
|
||||
|
||||
}
|
|
@ -4,7 +4,7 @@ import ca.uhn.fhir.context.FhirContext;
|
|||
import ca.uhn.fhir.interceptor.api.IInterceptorService;
|
||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||
import ca.uhn.fhir.interceptor.executor.InterceptorService;
|
||||
import ca.uhn.fhir.jpa.BaseTest;
|
||||
import ca.uhn.fhir.test.BaseTest;
|
||||
import ca.uhn.fhir.jpa.bulk.IBulkDataExportSvc;
|
||||
import ca.uhn.fhir.jpa.entity.TermConcept;
|
||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||
|
|
|
@ -11,6 +11,7 @@ import ca.uhn.fhir.jpa.term.TermReindexingSvcImpl;
|
|||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
import ca.uhn.fhir.rest.param.TokenParamModifier;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||
|
@ -722,7 +723,7 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
|
|||
try {
|
||||
myObservationDao.search(params).size();
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
} catch (InternalErrorException e) {
|
||||
assertEquals("Expansion of ValueSet produced too many codes (maximum 1) - Operation aborted!", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import ca.uhn.fhir.jpa.term.TermReindexingSvcImpl;
|
|||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
import ca.uhn.fhir.rest.param.TokenParamModifier;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||
|
@ -837,7 +838,7 @@ public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test {
|
|||
try {
|
||||
myObservationDao.search(params).size();
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
} catch (InternalErrorException e) {
|
||||
assertEquals("Expansion of ValueSet produced too many codes (maximum 1) - Operation aborted!", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,9 @@ package ca.uhn.fhir.jpa.dao.r4;
|
|||
|
||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
|
||||
import ca.uhn.fhir.jpa.term.custom.CustomTerminologySet;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.api.ValidationModeEnum;
|
||||
|
@ -34,6 +36,7 @@ import java.io.IOException;
|
|||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.awaitility.Awaitility.await;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.*;
|
||||
|
@ -43,7 +46,182 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
|||
@Autowired
|
||||
private IValidatorModule myValidatorModule;
|
||||
@Autowired
|
||||
private ITermReadSvc myTerminologySvc;
|
||||
private ITermReadSvc myTermReadSvc;
|
||||
@Autowired
|
||||
private ITermCodeSystemStorageSvc myTermCodeSystemStorageSvcc;
|
||||
|
||||
/**
|
||||
* Create a loinc valueset that expands to more results than the expander is willing to do
|
||||
* in memory, and make sure we can still validate correctly, even if we're using
|
||||
* the in-memory expander
|
||||
*/
|
||||
@Test
|
||||
public void testValidateCode_InMemoryExpansionAgainstHugeValueSet() throws Exception {
|
||||
myDaoConfig.setPreExpandValueSets(false);
|
||||
|
||||
ValueSet vs = new ValueSet();
|
||||
vs.setUrl("http://example.com/fhir/ValueSet/observation-vitalsignresult");
|
||||
vs.getCompose().addInclude().setSystem("http://loinc.org");
|
||||
myValueSetDao.create(vs);
|
||||
|
||||
assertFalse(myTermReadSvc.isValueSetPreExpandedForCodeValidation(vs));
|
||||
|
||||
// Load the profile, which is just the Vital Signs profile modified to accept all loinc codes
|
||||
// and not just certain ones
|
||||
StructureDefinition profile = loadResourceFromClasspath(StructureDefinition.class, "/r4/profile-vitalsigns-all-loinc.json");
|
||||
myStructureDefinitionDao.create(profile, mySrd);
|
||||
|
||||
// Add a bunch of codes
|
||||
CustomTerminologySet codesToAdd = new CustomTerminologySet();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
codesToAdd.addRootConcept("CODE" + i, "Display " + i);
|
||||
}
|
||||
myTermCodeSystemStorageSvc.applyDeltaCodeSystemsAdd("http://loinc.org", codesToAdd);
|
||||
|
||||
myDaoConfig.setMaximumExpansionSize(50);
|
||||
|
||||
Observation obs = new Observation();
|
||||
obs.getMeta().addProfile("http://example.com/fhir/StructureDefinition/vitalsigns-2");
|
||||
obs.getText().setDivAsString("<div>Hello</div>");
|
||||
obs.getCategoryFirstRep().addCoding().setSystem("http://terminology.hl7.org/CodeSystem/observation-category").setCode("vital-signs");
|
||||
obs.setSubject(new Reference("Patient/123"));
|
||||
obs.addPerformer(new Reference("Practitioner/123"));
|
||||
obs.setEffective(DateTimeType.now());
|
||||
obs.setStatus(ObservationStatus.FINAL);
|
||||
obs.setValue(new StringType("This is the value"));
|
||||
|
||||
OperationOutcome oo;
|
||||
|
||||
// Valid code
|
||||
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||
obs.getCode().getCodingFirstRep().setSystem("http://loinc.org").setCode("CODE3").setDisplay("Display 3");
|
||||
oo = validateAndReturnOutcome(obs);
|
||||
assertEquals(encode(oo), "No issues detected during validation", oo.getIssueFirstRep().getDiagnostics());
|
||||
|
||||
// Invalid code
|
||||
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||
obs.getCode().getCodingFirstRep().setSystem("http://loinc.org").setCode("non-existing-code").setDisplay("Display 3");
|
||||
oo = validateAndReturnOutcome(obs);
|
||||
assertEquals(encode(oo), "None of the codes provided are in the value set http://example.com/fhir/ValueSet/observation-vitalsignresult (http://example.com/fhir/ValueSet/observation-vitalsignresult, and a code from this value set is required) (codes = http://loinc.org#non-existing-code)", oo.getIssueFirstRep().getDiagnostics());
|
||||
|
||||
// Valid code with no system
|
||||
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||
obs.getCode().getCodingFirstRep().setSystem(null).setCode("CODE3").setDisplay("Display 3");
|
||||
oo = validateAndReturnOutcome(obs);
|
||||
assertEquals(encode(oo), "None of the codes provided are in the value set http://example.com/fhir/ValueSet/observation-vitalsignresult (http://example.com/fhir/ValueSet/observation-vitalsignresult, and a code from this value set is required) (codes = null#CODE3)", oo.getIssueFirstRep().getDiagnostics());
|
||||
|
||||
// Valid code with wrong system
|
||||
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||
obs.getCode().getCodingFirstRep().setSystem("http://foo").setCode("CODE3").setDisplay("Display 3");
|
||||
oo = validateAndReturnOutcome(obs);
|
||||
assertEquals(encode(oo), "Code http://foo/CODE3 was not validated because the code system is not present", oo.getIssueFirstRep().getDiagnostics());
|
||||
|
||||
// Code that exists but isn't in the valueset
|
||||
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||
obs.getCode().getCodingFirstRep().setSystem("http://terminology.hl7.org/CodeSystem/observation-category").setCode("vital-signs").setDisplay("Display 3");
|
||||
oo = validateAndReturnOutcome(obs);
|
||||
assertEquals(encode(oo), "None of the codes provided are in the value set http://example.com/fhir/ValueSet/observation-vitalsignresult (http://example.com/fhir/ValueSet/observation-vitalsignresult, and a code from this value set is required) (codes = http://terminology.hl7.org/CodeSystem/observation-category#vital-signs)", oo.getIssueFirstRep().getDiagnostics());
|
||||
|
||||
// Invalid code in built-in VS/CS
|
||||
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||
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(encode(oo), "Unknown code: http://terminology.hl7.org/CodeSystem/observation-category / FOO", oo.getIssueFirstRep().getDiagnostics());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a loinc valueset that expands to more results than the expander is willing to do
|
||||
* in memory, and make sure we can still validate correctly, even if we're using
|
||||
* the in-memory expander
|
||||
*/
|
||||
@Test
|
||||
public void testValidateCode_PreExpansionAgainstHugeValueSet() throws Exception {
|
||||
myDaoConfig.setPreExpandValueSets(true);
|
||||
|
||||
// Add a bunch of codes
|
||||
CustomTerminologySet codesToAdd = new CustomTerminologySet();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
codesToAdd.addRootConcept("CODE" + i, "Display " + i);
|
||||
}
|
||||
myTermCodeSystemStorageSvc.applyDeltaCodeSystemsAdd("http://loinc.org", codesToAdd);
|
||||
|
||||
// Create a valueset
|
||||
ValueSet vs = new ValueSet();
|
||||
vs.setUrl("http://example.com/fhir/ValueSet/observation-vitalsignresult");
|
||||
vs.getCompose().addInclude().setSystem("http://loinc.org");
|
||||
myValueSetDao.create(vs);
|
||||
myTermReadSvc.preExpandDeferredValueSetsToTerminologyTables();
|
||||
|
||||
await().until(()->myTermReadSvc.isValueSetPreExpandedForCodeValidation(vs));
|
||||
|
||||
// Load the profile, which is just the Vital Signs profile modified to accept all loinc codes
|
||||
// and not just certain ones
|
||||
StructureDefinition profile = loadResourceFromClasspath(StructureDefinition.class, "/r4/profile-vitalsigns-all-loinc.json");
|
||||
myStructureDefinitionDao.create(profile, mySrd);
|
||||
|
||||
Observation obs = new Observation();
|
||||
obs.getMeta().addProfile("http://example.com/fhir/StructureDefinition/vitalsigns-2");
|
||||
obs.getText().setDivAsString("<div>Hello</div>");
|
||||
obs.getCategoryFirstRep().addCoding().setSystem("http://terminology.hl7.org/CodeSystem/observation-category").setCode("vital-signs");
|
||||
obs.setSubject(new Reference("Patient/123"));
|
||||
obs.addPerformer(new Reference("Practitioner/123"));
|
||||
obs.setEffective(DateTimeType.now());
|
||||
obs.setStatus(ObservationStatus.FINAL);
|
||||
obs.setValue(new StringType("This is the value"));
|
||||
|
||||
OperationOutcome oo;
|
||||
|
||||
// Valid code
|
||||
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||
obs.getCode().getCodingFirstRep().setSystem("http://loinc.org").setCode("CODE3").setDisplay("Display 3");
|
||||
oo = validateAndReturnOutcome(obs);
|
||||
assertEquals(encode(oo), "No issues detected during validation", oo.getIssueFirstRep().getDiagnostics());
|
||||
|
||||
// Invalid code
|
||||
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||
obs.getCode().getCodingFirstRep().setSystem("http://loinc.org").setCode("non-existing-code").setDisplay("Display 3");
|
||||
oo = validateAndReturnOutcome(obs);
|
||||
assertEquals(encode(oo), "None of the codes provided are in the value set http://example.com/fhir/ValueSet/observation-vitalsignresult (http://example.com/fhir/ValueSet/observation-vitalsignresult, and a code from this value set is required) (codes = http://loinc.org#non-existing-code)", oo.getIssueFirstRep().getDiagnostics());
|
||||
|
||||
// Valid code with no system
|
||||
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||
obs.getCode().getCodingFirstRep().setSystem(null).setCode("CODE3").setDisplay("Display 3");
|
||||
oo = validateAndReturnOutcome(obs);
|
||||
assertEquals(encode(oo), "None of the codes provided are in the value set http://example.com/fhir/ValueSet/observation-vitalsignresult (http://example.com/fhir/ValueSet/observation-vitalsignresult, and a code from this value set is required) (codes = null#CODE3)", oo.getIssueFirstRep().getDiagnostics());
|
||||
|
||||
// Valid code with wrong system
|
||||
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||
obs.getCode().getCodingFirstRep().setSystem("http://foo").setCode("CODE3").setDisplay("Display 3");
|
||||
oo = validateAndReturnOutcome(obs);
|
||||
assertEquals(encode(oo), "Code http://foo/CODE3 was not validated because the code system is not present", oo.getIssueFirstRep().getDiagnostics());
|
||||
|
||||
// Code that exists but isn't in the valueset
|
||||
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||
obs.getCode().getCodingFirstRep().setSystem("http://terminology.hl7.org/CodeSystem/observation-category").setCode("vital-signs").setDisplay("Display 3");
|
||||
oo = validateAndReturnOutcome(obs);
|
||||
assertEquals(encode(oo), "None of the codes provided are in the value set http://example.com/fhir/ValueSet/observation-vitalsignresult (http://example.com/fhir/ValueSet/observation-vitalsignresult, and a code from this value set is required) (codes = http://terminology.hl7.org/CodeSystem/observation-category#vital-signs)", oo.getIssueFirstRep().getDiagnostics());
|
||||
|
||||
// Invalid code in built-in VS/CS
|
||||
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||
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(encode(oo), "Unknown code: http://terminology.hl7.org/CodeSystem/observation-category / FOO", oo.getIssueFirstRep().getDiagnostics());
|
||||
|
||||
}
|
||||
|
||||
|
||||
private OperationOutcome validateAndReturnOutcome(Observation theObs) {
|
||||
try {
|
||||
MethodOutcome outcome = myObservationDao.validate(theObs, null, null, null, ValidationModeEnum.CREATE, null, mySrd);
|
||||
return (OperationOutcome) outcome.getOperationOutcome();
|
||||
} catch (PreconditionFailedException e) {
|
||||
return (OperationOutcome) e.getOperationOutcome();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testValidateStructureDefinition() throws Exception {
|
||||
|
@ -280,7 +458,8 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
|||
val.setBestPracticeWarningLevel(IResourceValidator.BestPracticeWarningLevel.Warning);
|
||||
|
||||
myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences());
|
||||
|
||||
myDaoConfig.setMaximumExpansionSize(DaoConfig.DEFAULT_MAX_EXPANSION_SIZE);
|
||||
myDaoConfig.setPreExpandValueSets(new DaoConfig().isPreExpandValueSets());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -2,6 +2,8 @@ package ca.uhn.fhir.jpa.dao.r4;
|
|||
|
||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult;
|
||||
import ca.uhn.fhir.jpa.term.custom.CustomTerminologySet;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
|
@ -23,6 +25,7 @@ public class FhirResourceDaoR4ValueSetTest extends BaseJpaR4Test {
|
|||
@After
|
||||
public void after() {
|
||||
myDaoConfig.setPreExpandValueSets(new DaoConfig().isPreExpandValueSets());
|
||||
myDaoConfig.setMaximumExpansionSize(new DaoConfig().getMaximumExpansionSize());
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
|
@ -230,37 +233,26 @@ public class FhirResourceDaoR4ValueSetTest extends BaseJpaR4Test {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testExpandByIdentifier() {
|
||||
ValueSet expanded = myValueSetDao.expandByIdentifier("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", "11378");
|
||||
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
|
||||
ourLog.info(resp);
|
||||
//@formatter:off
|
||||
assertThat(resp, stringContainsInOrder(
|
||||
"<code value=\"11378-7\"/>",
|
||||
"<display value=\"Systolic blood pressure at First encounter\"/>"));
|
||||
//@formatter:on
|
||||
|
||||
assertThat(resp, not(containsString("<code value=\"8450-9\"/>")));
|
||||
public void testExpandByValueSet_ExceedsMaxSize() {
|
||||
// Add a bunch of codes
|
||||
CustomTerminologySet codesToAdd = new CustomTerminologySet();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
codesToAdd.addRootConcept("CODE" + i, "Display " + i);
|
||||
}
|
||||
myTermCodeSystemStorageSvc.applyDeltaCodeSystemsAdd("http://loinc.org", codesToAdd);
|
||||
myDaoConfig.setMaximumExpansionSize(50);
|
||||
|
||||
/**
|
||||
* This type of expansion doesn't really make sense..
|
||||
*/
|
||||
@Test
|
||||
@Ignore
|
||||
public void testExpandByValueSet() throws IOException {
|
||||
ValueSet toExpand = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml");
|
||||
ValueSet expanded = myValueSetDao.expand(toExpand, "11378");
|
||||
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
|
||||
ourLog.info(resp);
|
||||
//@formatter:off
|
||||
assertThat(resp, stringContainsInOrder(
|
||||
"<code value=\"11378-7\"/>",
|
||||
"<display value=\"Systolic blood pressure at First encounter\"/>"));
|
||||
//@formatter:on
|
||||
ValueSet vs = new ValueSet();
|
||||
vs.setUrl("http://example.com/fhir/ValueSet/observation-vitalsignresult");
|
||||
vs.getCompose().addInclude().setSystem("http://loinc.org");
|
||||
myValueSetDao.create(vs);
|
||||
|
||||
assertThat(resp, not(containsString("<code value=\"8450-9\"/>")));
|
||||
try {
|
||||
myValueSetDao.expand(vs, null);
|
||||
fail();
|
||||
} catch (InternalErrorException e) {
|
||||
assertEquals("Expansion of ValueSet produced too many codes (maximum 50) - Operation aborted!", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -108,7 +108,7 @@ public class TerminologyUploaderProviderR4Test extends BaseResourceProviderR4Tes
|
|||
.execute();
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("HTTP 400 Bad Request: No 'localfile' or 'package' parameter, or package had no data", e.getMessage());
|
||||
assertEquals("HTTP 400 Bad Request: No 'file' parameter, or package had no data", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -163,7 +163,7 @@ public class TerminologyUploaderProviderR4Test extends BaseResourceProviderR4Tes
|
|||
.onType(CodeSystem.class)
|
||||
.named("upload-external-code-system")
|
||||
.withParameter(Parameters.class, TerminologyUploaderProvider.PARAM_SYSTEM, new UriType(ITermLoaderSvc.SCT_URI))
|
||||
.andParameter("localfile", new StringType(tempFile.getAbsolutePath()))
|
||||
.andParameter(TerminologyUploaderProvider.PARAM_FILE, new Attachment().setUrl("localfile:"+tempFile.getAbsolutePath()))
|
||||
.execute();
|
||||
|
||||
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package ca.uhn.fhir.jpa.term;
|
||||
|
||||
import ca.uhn.fhir.jpa.BaseTest;
|
||||
import ca.uhn.fhir.test.BaseTest;
|
||||
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
|
||||
import ca.uhn.fhir.jpa.entity.TermConcept;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
|
|
|
@ -0,0 +1,187 @@
|
|||
package ca.uhn.fhir.jpa.term;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test;
|
||||
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
|
||||
import ca.uhn.fhir.jpa.entity.TermConcept;
|
||||
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.r4.model.CodeSystem;
|
||||
import org.hl7.fhir.r4.model.ValueSet;
|
||||
import org.hl7.fhir.r4.model.codesystems.HttpVerb;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
|
||||
@TestPropertySource(properties = {
|
||||
"scheduling_disabled=true"
|
||||
})
|
||||
public abstract class BaseTermR4Test extends BaseJpaR4Test {
|
||||
|
||||
IIdType myExtensionalCsId;
|
||||
IIdType myExtensionalVsId;
|
||||
Long myExtensionalCsIdOnResourceTable;
|
||||
Long myExtensionalVsIdOnResourceTable;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
myDaoConfig.setAllowExternalReferences(true);
|
||||
}
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences());
|
||||
myDaoConfig.setPreExpandValueSets(new DaoConfig().isPreExpandValueSets());
|
||||
myDaoConfig.setMaximumExpansionSize(DaoConfig.DEFAULT_MAX_EXPANSION_SIZE);
|
||||
}
|
||||
|
||||
IIdType createCodeSystem() {
|
||||
CodeSystem codeSystem = new CodeSystem();
|
||||
codeSystem.setUrl(CS_URL);
|
||||
codeSystem.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT);
|
||||
codeSystem.setName("SYSTEM NAME");
|
||||
codeSystem.setVersion("SYSTEM VERSION");
|
||||
IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified();
|
||||
|
||||
ResourceTable table = myResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalArgumentException::new);
|
||||
|
||||
TermCodeSystemVersion cs = new TermCodeSystemVersion();
|
||||
cs.setResource(table);
|
||||
|
||||
TermConcept parent;
|
||||
parent = new TermConcept(cs, "ParentWithNoChildrenA");
|
||||
cs.getConcepts().add(parent);
|
||||
parent = new TermConcept(cs, "ParentWithNoChildrenB");
|
||||
cs.getConcepts().add(parent);
|
||||
parent = new TermConcept(cs, "ParentWithNoChildrenC");
|
||||
cs.getConcepts().add(parent);
|
||||
|
||||
TermConcept parentA = new TermConcept(cs, "ParentA");
|
||||
cs.getConcepts().add(parentA);
|
||||
|
||||
TermConcept childAA = new TermConcept(cs, "childAA");
|
||||
parentA.addChild(childAA, TermConceptParentChildLink.RelationshipTypeEnum.ISA);
|
||||
|
||||
TermConcept childAAA = new TermConcept(cs, "childAAA");
|
||||
childAAA.addPropertyString("propA", "valueAAA");
|
||||
childAAA.addPropertyString("propB", "foo");
|
||||
childAA.addChild(childAAA, TermConceptParentChildLink.RelationshipTypeEnum.ISA);
|
||||
|
||||
TermConcept childAAB = new TermConcept(cs, "childAAB");
|
||||
childAAB.addPropertyString("propA", "valueAAB");
|
||||
childAAB.addPropertyString("propB", "foo");
|
||||
childAAB.addDesignation()
|
||||
.setUseSystem("D1S")
|
||||
.setUseCode("D1C")
|
||||
.setUseDisplay("D1D")
|
||||
.setValue("D1V");
|
||||
childAA.addChild(childAAB, TermConceptParentChildLink.RelationshipTypeEnum.ISA);
|
||||
|
||||
TermConcept childAB = new TermConcept(cs, "childAB");
|
||||
parentA.addChild(childAB, TermConceptParentChildLink.RelationshipTypeEnum.ISA);
|
||||
|
||||
TermConcept parentB = new TermConcept(cs, "ParentB");
|
||||
cs.getConcepts().add(parentB);
|
||||
|
||||
myTermCodeSystemStorageSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", "SYSTEM VERSION", cs, table);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void loadAndPersistCodeSystemAndValueSet() throws IOException {
|
||||
loadAndPersistCodeSystem();
|
||||
loadAndPersistValueSet(HttpVerb.POST);
|
||||
}
|
||||
|
||||
void loadAndPersistCodeSystemAndValueSetWithDesignations(HttpVerb theVerb) throws IOException {
|
||||
loadAndPersistCodeSystemWithDesignations(theVerb);
|
||||
loadAndPersistValueSet(theVerb);
|
||||
}
|
||||
|
||||
void loadAndPersistCodeSystemAndValueSetWithDesignationsAndExclude(HttpVerb theVerb) throws IOException {
|
||||
loadAndPersistCodeSystemWithDesignations(theVerb);
|
||||
loadAndPersistValueSetWithExclude(theVerb);
|
||||
}
|
||||
|
||||
void loadAndPersistCodeSystem() throws IOException {
|
||||
CodeSystem codeSystem = loadResourceFromClasspath(CodeSystem.class, "/extensional-case-3-cs.xml");
|
||||
codeSystem.setId("CodeSystem/cs");
|
||||
persistCodeSystem(codeSystem, HttpVerb.POST);
|
||||
}
|
||||
|
||||
void loadAndPersistCodeSystemWithDesignations(HttpVerb theVerb) throws IOException {
|
||||
CodeSystem codeSystem = loadResourceFromClasspath(CodeSystem.class, "/extensional-case-3-cs-with-designations.xml");
|
||||
codeSystem.setId("CodeSystem/cs");
|
||||
persistCodeSystem(codeSystem, theVerb);
|
||||
}
|
||||
|
||||
@SuppressWarnings("EnumSwitchStatementWhichMissesCases")
|
||||
private void persistCodeSystem(CodeSystem theCodeSystem, HttpVerb theVerb) {
|
||||
switch (theVerb) {
|
||||
case POST:
|
||||
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
|
||||
@Override
|
||||
protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) {
|
||||
myExtensionalCsId = myCodeSystemDao.create(theCodeSystem, mySrd).getId().toUnqualifiedVersionless();
|
||||
}
|
||||
});
|
||||
break;
|
||||
case PUT:
|
||||
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
|
||||
@Override
|
||||
protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) {
|
||||
myExtensionalCsId = myCodeSystemDao.update(theCodeSystem, mySrd).getId().toUnqualifiedVersionless();
|
||||
}
|
||||
});
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("HTTP verb is not supported: " + theVerb);
|
||||
}
|
||||
myExtensionalCsIdOnResourceTable = myCodeSystemDao.readEntity(myExtensionalCsId, null).getId();
|
||||
}
|
||||
|
||||
void loadAndPersistValueSet(HttpVerb theVerb) throws IOException {
|
||||
ValueSet valueSet = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml");
|
||||
valueSet.setId("ValueSet/vs");
|
||||
persistValueSet(valueSet, theVerb);
|
||||
}
|
||||
|
||||
private void loadAndPersistValueSetWithExclude(HttpVerb theVerb) throws IOException {
|
||||
ValueSet valueSet = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs-with-exclude.xml");
|
||||
valueSet.setId("ValueSet/vs");
|
||||
persistValueSet(valueSet, theVerb);
|
||||
}
|
||||
|
||||
@SuppressWarnings("EnumSwitchStatementWhichMissesCases")
|
||||
private void persistValueSet(ValueSet theValueSet, HttpVerb theVerb) {
|
||||
switch (theVerb) {
|
||||
case POST:
|
||||
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
|
||||
@Override
|
||||
protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) {
|
||||
myExtensionalVsId = myValueSetDao.create(theValueSet, mySrd).getId().toUnqualifiedVersionless();
|
||||
}
|
||||
});
|
||||
break;
|
||||
case PUT:
|
||||
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
|
||||
@Override
|
||||
protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) {
|
||||
myExtensionalVsId = myValueSetDao.update(theValueSet, mySrd).getId().toUnqualifiedVersionless();
|
||||
}
|
||||
});
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("HTTP verb is not supported: " + theVerb);
|
||||
}
|
||||
myExtensionalVsIdOnResourceTable = myValueSetDao.readEntity(myExtensionalVsId, null).getId();
|
||||
}
|
||||
|
||||
}
|
|
@ -38,12 +38,10 @@ import static org.junit.Assert.*;
|
|||
|
||||
public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(TerminologySvcImplDstu3Test.class);
|
||||
|
||||
@Rule
|
||||
public final ExpectedException expectedException = ExpectedException.none();
|
||||
|
||||
private static final String CS_URL = "http://example.com/my_code_system";
|
||||
private static final String CS_URL_2 = "http://example.com/my_code_system2";
|
||||
@Rule
|
||||
public final ExpectedException expectedException = ExpectedException.none();
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
|
@ -118,7 +116,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
TermConcept parentA = new TermConcept(cs, "CS2");
|
||||
cs.getConcepts().add(parentA);
|
||||
|
||||
myTermCodeSystemStorageSvc.storeNewCodeSystemVersion(table.getId(), CS_URL_2, "SYSTEM NAME", "SYSTEM VERSION" , cs, table);
|
||||
myTermCodeSystemStorageSvc.storeNewCodeSystemVersion(table.getId(), CS_URL_2, "SYSTEM NAME", "SYSTEM VERSION", cs, table);
|
||||
|
||||
}
|
||||
|
||||
|
@ -187,7 +185,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
code2.getDisplay());
|
||||
cs.getConcepts().add(code4);
|
||||
|
||||
myTermCodeSystemStorageSvc.storeNewCodeSystemVersion(table.getId(), LOINC_URI, "SYSTEM NAME", "SYSTEM VERSION" , cs, table);
|
||||
myTermCodeSystemStorageSvc.storeNewCodeSystemVersion(table.getId(), LOINC_URI, "SYSTEM NAME", "SYSTEM VERSION", cs, table);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -203,7 +201,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
TermCodeSystemVersion cs = new TermCodeSystemVersion();
|
||||
cs.setResource(table);
|
||||
|
||||
myTermCodeSystemStorageSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", "SYSTEM VERSION" , cs, table);
|
||||
myTermCodeSystemStorageSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", "SYSTEM VERSION", cs, table);
|
||||
|
||||
// Update
|
||||
cs = new TermCodeSystemVersion();
|
||||
|
@ -212,7 +210,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
id = myCodeSystemDao.update(codeSystem, null, true, true, mySrd).getId().toUnqualified();
|
||||
table = myResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalArgumentException::new);
|
||||
cs.setResource(table);
|
||||
myTermCodeSystemStorageSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", "SYSTEM VERSION" , cs, table);
|
||||
myTermCodeSystemStorageSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", "SYSTEM VERSION", cs, table);
|
||||
|
||||
// Try to update to a different resource
|
||||
codeSystem = new CodeSystem();
|
||||
|
@ -246,7 +244,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
ValueSet.ConceptSetComponent include = vs.getCompose().addInclude();
|
||||
include.setSystem(CS_URL);
|
||||
include.addConcept().setCode("childAAB");
|
||||
ValueSet outcome = myTermSvc.expandValueSet(vs);
|
||||
ValueSet outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
|
||||
List<String> codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("childAAB"));
|
||||
|
@ -280,7 +278,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("propA")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("valueAAA");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("childAAA"));
|
||||
|
||||
|
@ -293,7 +291,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("propB")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("foo");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("childAAA", "childAAB"));
|
||||
|
||||
|
@ -306,7 +304,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("propA")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("valueAAA");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, empty());
|
||||
}
|
||||
|
@ -333,7 +331,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("copyright")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("3rdParty");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("50015-7", "43343-3", "43343-4"));
|
||||
|
||||
|
@ -350,7 +348,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("copyright")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("3rdparty");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("50015-7", "43343-3", "43343-4"));
|
||||
}
|
||||
|
@ -377,7 +375,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("copyright")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("LOINC");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("47239-9"));
|
||||
|
||||
|
@ -394,7 +392,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("copyright")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("loinc");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("47239-9"));
|
||||
}
|
||||
|
@ -417,7 +415,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("copyright")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("3rdParty");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("47239-9"));
|
||||
|
||||
|
@ -430,7 +428,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("copyright")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("3rdparty");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("47239-9"));
|
||||
}
|
||||
|
@ -453,7 +451,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("copyright")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("LOINC");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("50015-7", "43343-3", "43343-4"));
|
||||
|
||||
|
@ -466,7 +464,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("copyright")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("loinc");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("50015-7", "43343-3", "43343-4"));
|
||||
}
|
||||
|
@ -490,7 +488,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
|
||||
expectedException.expect(InvalidRequestException.class);
|
||||
expectedException.expectMessage("Don't know how to handle op=ISA on property copyright");
|
||||
myTermSvc.expandValueSet(vs);
|
||||
myTermSvc.expandValueSetInMemory(vs, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -513,7 +511,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
|
||||
expectedException.expect(InvalidRequestException.class);
|
||||
expectedException.expectMessage("Invalid filter, property copyright is LOINC-specific and cannot be used with system: http://example.com/my_code_system");
|
||||
myTermSvc.expandValueSet(vs);
|
||||
myTermSvc.expandValueSetInMemory(vs, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -535,7 +533,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
|
||||
expectedException.expect(InvalidRequestException.class);
|
||||
expectedException.expectMessage("Don't know how to handle value=bogus on property copyright");
|
||||
myTermSvc.expandValueSet(vs);
|
||||
myTermSvc.expandValueSetInMemory(vs, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -560,7 +558,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("ancestor")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("50015-7");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("50015-7"));
|
||||
|
||||
|
@ -577,7 +575,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("ancestor")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("43343-3");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("50015-7", "43343-3"));
|
||||
|
||||
|
@ -594,7 +592,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("ancestor")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("43343-4");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("50015-7", "43343-3", "43343-4", "47239-9"));
|
||||
|
||||
|
@ -611,7 +609,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("ancestor")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("47239-9");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("50015-7", "43343-3", "43343-4", "47239-9"));
|
||||
}
|
||||
|
@ -638,7 +636,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("ancestor")
|
||||
.setOp(ValueSet.FilterOperator.IN)
|
||||
.setValue("50015-7,43343-3,43343-4,47239-9");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("50015-7"));
|
||||
}
|
||||
|
@ -661,7 +659,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("ancestor")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("50015-7");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("43343-3", "43343-4", "47239-9"));
|
||||
|
||||
|
@ -674,7 +672,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("ancestor")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("43343-3");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("43343-4", "47239-9"));
|
||||
|
||||
|
@ -687,7 +685,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("ancestor")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("43343-4");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
assertEquals(0, outcome.getExpansion().getContains().size());
|
||||
|
||||
// Include
|
||||
|
@ -699,7 +697,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("ancestor")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("47239-9");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
assertEquals(0, outcome.getExpansion().getContains().size());
|
||||
}
|
||||
|
||||
|
@ -721,7 +719,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("ancestor")
|
||||
.setOp(ValueSet.FilterOperator.IN)
|
||||
.setValue("50015-7,43343-3,43343-4,47239-9");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("43343-3", "43343-4", "47239-9"));
|
||||
}
|
||||
|
@ -745,7 +743,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
|
||||
expectedException.expect(InvalidRequestException.class);
|
||||
expectedException.expectMessage("Don't know how to handle op=ISA on property ancestor");
|
||||
myTermSvc.expandValueSet(vs);
|
||||
myTermSvc.expandValueSetInMemory(vs, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -768,7 +766,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
|
||||
expectedException.expect(InvalidRequestException.class);
|
||||
expectedException.expectMessage("Invalid filter, property ancestor is LOINC-specific and cannot be used with system: http://example.com/my_code_system");
|
||||
myTermSvc.expandValueSet(vs);
|
||||
myTermSvc.expandValueSetInMemory(vs, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -793,7 +791,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("child")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("50015-7");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("50015-7", "43343-3", "43343-4", "47239-9"));
|
||||
|
||||
|
@ -810,7 +808,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("child")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("43343-3");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("43343-3", "43343-4", "47239-9"));
|
||||
|
||||
|
@ -827,7 +825,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("child")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("43343-4");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("50015-7", "43343-4", "47239-9"));
|
||||
|
||||
|
@ -844,7 +842,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("child")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("47239-9");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("50015-7", "43343-4", "47239-9"));
|
||||
}
|
||||
|
@ -871,7 +869,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("child")
|
||||
.setOp(ValueSet.FilterOperator.IN)
|
||||
.setValue("50015-7,43343-3,43343-4,47239-9");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("43343-4", "47239-9"));
|
||||
}
|
||||
|
@ -894,7 +892,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("child")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("50015-7");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
assertEquals(0, outcome.getExpansion().getContains().size());
|
||||
|
||||
// Include
|
||||
|
@ -906,7 +904,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("child")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("43343-3");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("50015-7"));
|
||||
|
||||
|
@ -919,7 +917,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("child")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("43343-4");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("43343-3"));
|
||||
|
||||
|
@ -932,7 +930,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("child")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("47239-9");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("43343-3"));
|
||||
}
|
||||
|
@ -955,7 +953,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("child")
|
||||
.setOp(ValueSet.FilterOperator.IN)
|
||||
.setValue("50015-7,43343-3,43343-4,47239-9");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("50015-7", "43343-3"));
|
||||
}
|
||||
|
@ -979,7 +977,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
|
||||
expectedException.expect(InvalidRequestException.class);
|
||||
expectedException.expectMessage("Don't know how to handle op=ISA on property child");
|
||||
myTermSvc.expandValueSet(vs);
|
||||
myTermSvc.expandValueSetInMemory(vs, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1002,7 +1000,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
|
||||
expectedException.expect(InvalidRequestException.class);
|
||||
expectedException.expectMessage("Invalid filter, property child is LOINC-specific and cannot be used with system: http://example.com/my_code_system");
|
||||
myTermSvc.expandValueSet(vs);
|
||||
myTermSvc.expandValueSetInMemory(vs, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1027,7 +1025,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("descendant")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("50015-7");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("50015-7", "43343-3", "43343-4", "47239-9"));
|
||||
|
||||
|
@ -1044,7 +1042,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("descendant")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("43343-3");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("43343-3", "43343-4", "47239-9"));
|
||||
|
||||
|
@ -1061,7 +1059,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("descendant")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("43343-4");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("43343-4", "47239-9"));
|
||||
|
||||
|
@ -1078,7 +1076,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("descendant")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("47239-9");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("43343-4", "47239-9"));
|
||||
}
|
||||
|
@ -1105,7 +1103,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("descendant")
|
||||
.setOp(ValueSet.FilterOperator.IN)
|
||||
.setValue("50015-7,43343-3,43343-4,47239-9");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("43343-4", "47239-9"));
|
||||
}
|
||||
|
@ -1128,7 +1126,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("descendant")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("50015-7");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
assertEquals(0, outcome.getExpansion().getContains().size());
|
||||
|
||||
// Include
|
||||
|
@ -1140,7 +1138,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("descendant")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("43343-3");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("50015-7"));
|
||||
|
||||
|
@ -1153,7 +1151,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("descendant")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("43343-4");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("50015-7", "43343-3"));
|
||||
|
||||
|
@ -1166,7 +1164,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("descendant")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("47239-9");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("50015-7", "43343-3"));
|
||||
}
|
||||
|
@ -1189,7 +1187,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("descendant")
|
||||
.setOp(ValueSet.FilterOperator.IN)
|
||||
.setValue("50015-7,43343-3,43343-4,47239-9");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("50015-7", "43343-3"));
|
||||
}
|
||||
|
@ -1213,7 +1211,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
|
||||
expectedException.expect(InvalidRequestException.class);
|
||||
expectedException.expectMessage("Don't know how to handle op=ISA on property descendant");
|
||||
myTermSvc.expandValueSet(vs);
|
||||
myTermSvc.expandValueSetInMemory(vs, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1236,7 +1234,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
|
||||
expectedException.expect(InvalidRequestException.class);
|
||||
expectedException.expectMessage("Invalid filter, property descendant is LOINC-specific and cannot be used with system: http://example.com/my_code_system");
|
||||
myTermSvc.expandValueSet(vs);
|
||||
myTermSvc.expandValueSetInMemory(vs, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1261,7 +1259,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("parent")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("50015-7");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("50015-7", "43343-4", "47239-9"));
|
||||
|
||||
|
@ -1278,7 +1276,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("parent")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("43343-3");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("50015-7", "43343-3"));
|
||||
|
||||
|
@ -1295,7 +1293,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("parent")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("43343-4");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("50015-7", "43343-3", "43343-4", "47239-9"));
|
||||
|
||||
|
@ -1312,7 +1310,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("parent")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("47239-9");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("50015-7", "43343-3", "43343-4", "47239-9"));
|
||||
}
|
||||
|
@ -1339,7 +1337,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("parent")
|
||||
.setOp(ValueSet.FilterOperator.IN)
|
||||
.setValue("50015-7,43343-3,43343-4,47239-9");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("50015-7"));
|
||||
}
|
||||
|
@ -1362,7 +1360,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("parent")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("50015-7");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("43343-3"));
|
||||
|
||||
|
@ -1375,7 +1373,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("parent")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("43343-3");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("43343-4", "47239-9"));
|
||||
|
||||
|
@ -1388,7 +1386,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("parent")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("43343-4");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
assertEquals(0, outcome.getExpansion().getContains().size());
|
||||
|
||||
// Include
|
||||
|
@ -1400,7 +1398,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("parent")
|
||||
.setOp(ValueSet.FilterOperator.EQUAL)
|
||||
.setValue("47239-9");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
assertEquals(0, outcome.getExpansion().getContains().size());
|
||||
}
|
||||
|
||||
|
@ -1422,7 +1420,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("parent")
|
||||
.setOp(ValueSet.FilterOperator.IN)
|
||||
.setValue("50015-7,43343-3,43343-4,47239-9");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("43343-3", "43343-4", "47239-9"));
|
||||
}
|
||||
|
@ -1446,7 +1444,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
|
||||
expectedException.expect(InvalidRequestException.class);
|
||||
expectedException.expectMessage("Don't know how to handle op=ISA on property parent");
|
||||
myTermSvc.expandValueSet(vs);
|
||||
myTermSvc.expandValueSetInMemory(vs, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1469,7 +1467,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
|
||||
expectedException.expect(InvalidRequestException.class);
|
||||
expectedException.expectMessage("Invalid filter, property parent is LOINC-specific and cannot be used with system: http://example.com/my_code_system");
|
||||
myTermSvc.expandValueSet(vs);
|
||||
myTermSvc.expandValueSetInMemory(vs, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1494,7 +1492,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("SYSTEM")
|
||||
.setOp(ValueSet.FilterOperator.REGEX)
|
||||
.setValue(".*\\^Donor$");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("43343-3", "43343-4", "47239-9"));
|
||||
}
|
||||
|
@ -1521,7 +1519,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("HELLO")
|
||||
.setOp(ValueSet.FilterOperator.REGEX)
|
||||
.setValue("12345-1|12345-2");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("50015-7", "47239-9"));
|
||||
}
|
||||
|
@ -1544,7 +1542,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("SYSTEM")
|
||||
.setOp(ValueSet.FilterOperator.REGEX)
|
||||
.setValue(".*\\^Donor$");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("50015-7"));
|
||||
|
||||
|
@ -1557,7 +1555,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("SYSTEM")
|
||||
.setOp(ValueSet.FilterOperator.REGEX)
|
||||
.setValue("\\^Donor$");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("50015-7"));
|
||||
|
||||
|
@ -1570,7 +1568,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("SYSTEM")
|
||||
.setOp(ValueSet.FilterOperator.REGEX)
|
||||
.setValue("\\^Dono$");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, empty());
|
||||
|
||||
|
@ -1583,7 +1581,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("SYSTEM")
|
||||
.setOp(ValueSet.FilterOperator.REGEX)
|
||||
.setValue("^Donor$");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, empty());
|
||||
|
||||
|
@ -1596,7 +1594,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("SYSTEM")
|
||||
.setOp(ValueSet.FilterOperator.REGEX)
|
||||
.setValue("\\^Dono");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("50015-7"));
|
||||
|
||||
|
@ -1609,7 +1607,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.setProperty("SYSTEM")
|
||||
.setOp(ValueSet.FilterOperator.REGEX)
|
||||
.setValue("^Ser$");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("43343-3", "43343-4"));
|
||||
|
||||
|
@ -1624,7 +1622,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
ValueSet vs = new ValueSet();
|
||||
ValueSet.ConceptSetComponent include = vs.getCompose().addInclude();
|
||||
include.setSystem(CS_URL);
|
||||
ValueSet outcome = myTermSvc.expandValueSet(vs);
|
||||
ValueSet outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("ParentWithNoChildrenA", "ParentWithNoChildrenB", "ParentWithNoChildrenC", "ParentA", "childAAA", "childAAB", "childAA", "childAB", "ParentB"));
|
||||
|
@ -1741,7 +1739,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
ValueSet.ConceptSetComponent include = vs.getCompose().addInclude();
|
||||
include.setSystem(CS_URL);
|
||||
include.addConcept().setCode("childAAB");
|
||||
ValueSet outcome = myTermSvc.expandValueSet(vs);
|
||||
ValueSet outcome = myTermSvc.expandValueSetInMemory(vs, null);
|
||||
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("childAAB"));
|
||||
|
@ -1782,7 +1780,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
child.addChild(parent, RelationshipTypeEnum.ISA);
|
||||
|
||||
try {
|
||||
myTermCodeSystemStorageSvc.storeNewCodeSystemVersion(table.getId(), "http://foo", "SYSTEM NAME", "SYSTEM VERSION" , cs, table);
|
||||
myTermCodeSystemStorageSvc.storeNewCodeSystemVersion(table.getId(), "http://foo", "SYSTEM NAME", "SYSTEM VERSION", cs, table);
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("CodeSystem contains circular reference around code parent", e.getMessage());
|
||||
|
@ -1894,7 +1892,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
@Test
|
||||
public void testCustomValueSetExpansion() {
|
||||
|
||||
CodeSystem cs= new CodeSystem();
|
||||
CodeSystem cs = new CodeSystem();
|
||||
cs.setUrl("http://codesystems-r-us");
|
||||
cs.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT);
|
||||
IIdType csId = myCodeSystemDao.create(cs).getId().toUnqualifiedVersionless();
|
||||
|
@ -1904,10 +1902,10 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
version.getConcepts().add(new TermConcept(version, "B"));
|
||||
version.getConcepts().add(new TermConcept(version, "C"));
|
||||
version.getConcepts().add(new TermConcept(version, "D"));
|
||||
runInTransaction(()->{
|
||||
runInTransaction(() -> {
|
||||
ResourceTable resTable = myEntityManager.find(ResourceTable.class, csId.getIdPartAsLong());
|
||||
version.setResource(resTable);
|
||||
myTermCodeSystemStorageSvc.storeNewCodeSystemVersion(csId.getIdPartAsLong(), cs.getUrl(), "My System", "SYSTEM VERSION" , version, resTable);
|
||||
myTermCodeSystemStorageSvc.storeNewCodeSystemVersion(csId.getIdPartAsLong(), cs.getUrl(), "My System", "SYSTEM VERSION", version, resTable);
|
||||
});
|
||||
|
||||
org.hl7.fhir.dstu3.model.ValueSet vs = new org.hl7.fhir.dstu3.model.ValueSet();
|
||||
|
@ -1927,15 +1925,16 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
.map(t -> t.getCode())
|
||||
.sorted()
|
||||
.collect(Collectors.toList());
|
||||
assertEquals(Lists.newArrayList("A","C"), expansionCodes);
|
||||
assertEquals(Lists.newArrayList("A", "C"), expansionCodes);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testValidateCodeWithProperties() {
|
||||
createCodeSystem();
|
||||
IValidationSupport.CodeValidationResult code = myValidationSupport.validateCode(myFhirCtx, CS_URL, "childAAB", null);
|
||||
IValidationSupport.CodeValidationResult code = myValidationSupport.validateCode(myFhirCtx, CS_URL, "childAAB", null, null);
|
||||
assertEquals(true, code.isOk());
|
||||
assertEquals(2, code.getProperties().size());
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -361,11 +361,6 @@ public abstract class BaseSearchParamRegistry<SP extends IBaseResource> implemen
|
|||
return refreshCacheRetrier.runWithRetry();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setSearchParamProviderForUnitTest(ISearchParamProvider theSearchParamProvider) {
|
||||
mySearchParamProvider = theSearchParamProvider;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void registerScheduledJob() {
|
||||
ScheduledJobDefinition jobDetail = new ScheduledJobDefinition();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package org.hl7.fhir.dstu2016may.hapi.validation;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import org.apache.commons.io.Charsets;
|
||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.dstu2016may.model.*;
|
||||
|
@ -10,7 +10,10 @@ import org.hl7.fhir.dstu2016may.model.CodeSystem.ConceptDefinitionComponent;
|
|||
import org.hl7.fhir.dstu2016may.model.ValueSet.ConceptReferenceComponent;
|
||||
import org.hl7.fhir.dstu2016may.model.ValueSet.ConceptSetComponent;
|
||||
import org.hl7.fhir.dstu2016may.model.ValueSet.ValueSetExpansionComponent;
|
||||
import org.hl7.fhir.dstu2016may.terminologies.ValueSetExpander;
|
||||
import org.hl7.fhir.dstu2016may.terminologies.ValueSetExpanderSimple;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
|
@ -258,7 +261,33 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
|
||||
if (isNotBlank(theValueSetUrl)) {
|
||||
HapiWorkerContext workerContext = new HapiWorkerContext(theContext, this);
|
||||
ValueSetExpander expander = new ValueSetExpanderSimple(workerContext, workerContext);
|
||||
try {
|
||||
ValueSet valueSet = fetchValueSet(theContext, theValueSetUrl);
|
||||
if (valueSet != null) {
|
||||
ValueSetExpander.ValueSetExpansionOutcome expanded = expander.expand(valueSet);
|
||||
Optional<ValueSet.ValueSetExpansionContainsComponent> haveMatch = expanded
|
||||
.getValueset()
|
||||
.getExpansion()
|
||||
.getContains()
|
||||
.stream()
|
||||
.filter(t -> (theCodeSystem == null || t.getSystem().equals(theCodeSystem)) && t.getCode().equals(theCode))
|
||||
.findFirst();
|
||||
if (haveMatch.isPresent()) {
|
||||
return new CodeValidationResult(new ConceptDefinitionComponent(new CodeType(theCode)));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return new CodeValidationResult(OperationOutcome.IssueSeverity.WARNING, e.getMessage());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (theCodeSystem != null) {
|
||||
CodeSystem cs = fetchCodeSystem(theContext, theCodeSystem);
|
||||
if (cs != null) {
|
||||
boolean caseSensitive = true;
|
||||
|
@ -272,13 +301,14 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
|
|||
return retVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new CodeValidationResult(OperationOutcome.IssueSeverity.WARNING, "Unknown code: " + theCodeSystem + " / " + theCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) {
|
||||
return validateCode(theContext, theSystem, theCode, null).asLookupCodeResult(theSystem, theCode);
|
||||
return validateCode(theContext, theSystem, theCode, null, null).asLookupCodeResult(theSystem, theCode);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -29,7 +29,6 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirInstanceValidator.class);
|
||||
private BestPracticeWarningLevel myBestPracticeWarningLevel;
|
||||
private DocumentBuilderFactory myDocBuilderFactory;
|
||||
private StructureDefinition myStructureDefintion;
|
||||
private IValidationSupport myValidationSupport;
|
||||
|
||||
|
@ -49,7 +48,6 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
* The validation support
|
||||
*/
|
||||
public FhirInstanceValidator(IValidationSupport theValidationSupport) {
|
||||
myDocBuilderFactory = XmlUtil.newDocumentBuilderFactory();
|
||||
myValidationSupport = theValidationSupport;
|
||||
}
|
||||
|
||||
|
@ -139,9 +137,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
if (theEncoding == EncodingEnum.XML) {
|
||||
Document document;
|
||||
try {
|
||||
DocumentBuilder builder = myDocBuilderFactory.newDocumentBuilder();
|
||||
InputSource src = new InputSource(new StringReader(theInput));
|
||||
document = builder.parse(src);
|
||||
document = XmlUtil.parseDocument(theInput);
|
||||
} catch (Exception e2) {
|
||||
ourLog.error("Failure to parse XML input", e2);
|
||||
ValidationMessage m = new ValidationMessage();
|
||||
|
|
|
@ -177,7 +177,7 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander
|
|||
|
||||
@Override
|
||||
public ValidationResult validateCode(String theSystem, String theCode, String theDisplay) {
|
||||
CodeValidationResult result = myValidationSupport.validateCode(myCtx, theSystem, theCode, theDisplay);
|
||||
CodeValidationResult result = myValidationSupport.validateCode(myCtx, theSystem, theCode, theDisplay, null);
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -231,7 +231,7 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander
|
|||
/*
|
||||
* The following valueset is a special case, since the BCP codesystem is very difficult to expand
|
||||
*/
|
||||
if (theVs != null && "http://hl7.org/fhir/ValueSet/languages".equals(theVs.getId())) {
|
||||
if (theVs != null && "http://hl7.org/fhir/ValueSet/languages".equals(theVs.getUrl())) {
|
||||
ValueSet expansion = new ValueSet();
|
||||
for (ConceptSetComponent nextInclude : theVs.getCompose().getInclude()) {
|
||||
for (ConceptReferenceComponent nextConcept : nextInclude.getConcept()) {
|
||||
|
|
|
@ -51,6 +51,7 @@ public interface IValidationSupport
|
|||
* Canonical Uri of the ValueSet
|
||||
* @return The valueset (must not be null, but can be an empty ValueSet)
|
||||
*/
|
||||
@Override
|
||||
ValueSet fetchValueSet(FhirContext theContext, String uri);
|
||||
|
||||
/**
|
||||
|
@ -95,10 +96,13 @@ public interface IValidationSupport
|
|||
* The code, e.g. "<code>1234-5</code>"
|
||||
* @param theDisplay
|
||||
* The display name, if it should also be validated
|
||||
* @param theValueSetUrl When validating that a code exists as a part of a specific ValueSet, the ValueSet URI
|
||||
* will be provided in this parameter value. If <code>null</code>, the validation should simply
|
||||
* confirm that the code exists.
|
||||
* @return Returns a validation result object
|
||||
*/
|
||||
@Override
|
||||
CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay);
|
||||
CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl);
|
||||
|
||||
class CodeValidationResult extends IContextValidationSupport.CodeValidationResult<ConceptDefinitionComponent, OperationOutcome.IssueSeverity> {
|
||||
|
||||
|
|
|
@ -28,9 +28,9 @@ public class PrePopulatedValidationSupport implements IValidationSupport {
|
|||
* Constructor
|
||||
*/
|
||||
public PrePopulatedValidationSupport() {
|
||||
myStructureDefinitions = new HashMap<String, StructureDefinition>();
|
||||
myValueSets = new HashMap<String, ValueSet>();
|
||||
myCodeSystems = new HashMap<String, CodeSystem>();
|
||||
myStructureDefinitions = new HashMap<>();
|
||||
myValueSets = new HashMap<>();
|
||||
myCodeSystems = new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -134,7 +134,7 @@ public class PrePopulatedValidationSupport implements IValidationSupport {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -134,13 +134,13 @@ public class ValidationSupportChain implements IValidationSupport {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(FhirContext theCtx, String theCodeSystem, String theCode, String theDisplay) {
|
||||
public CodeValidationResult validateCode(FhirContext theCtx, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
|
||||
for (IValidationSupport next : myChain) {
|
||||
if (next.isCodeSystemSupported(theCtx, theCodeSystem)) {
|
||||
return next.validateCode(theCtx, theCodeSystem, theCode, theDisplay);
|
||||
if (theCodeSystem != null && next.isCodeSystemSupported(theCtx, theCodeSystem)) {
|
||||
return next.validateCode(theCtx, theCodeSystem, theCode, theDisplay, theValueSetUrl);
|
||||
}
|
||||
}
|
||||
return myChain.get(0).validateCode(theCtx, theCodeSystem, theCode, theDisplay);
|
||||
return myChain.get(0).validateCode(theCtx, theCodeSystem, theCode, theDisplay, theValueSetUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.hl7.fhir.dstu3.hapi.ctx;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||
import org.apache.commons.io.Charsets;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
@ -12,6 +13,8 @@ import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent;
|
|||
import org.hl7.fhir.dstu3.model.ValueSet.ConceptReferenceComponent;
|
||||
import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent;
|
||||
import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent;
|
||||
import org.hl7.fhir.dstu3.terminologies.ValueSetExpander;
|
||||
import org.hl7.fhir.dstu3.terminologies.ValueSetExpanderSimple;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
|
||||
|
||||
|
@ -280,7 +283,33 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
|
||||
if (isNotBlank(theValueSetUrl)) {
|
||||
HapiWorkerContext workerContext = new HapiWorkerContext(theContext, this);
|
||||
ValueSetExpander expander = new ValueSetExpanderSimple(workerContext, workerContext);
|
||||
try {
|
||||
ValueSet valueSet = fetchValueSet(theContext, theValueSetUrl);
|
||||
if (valueSet != null) {
|
||||
ValueSetExpander.ValueSetExpansionOutcome expanded = expander.expand(valueSet, null);
|
||||
Optional<ValueSet.ValueSetExpansionContainsComponent> haveMatch = expanded
|
||||
.getValueset()
|
||||
.getExpansion()
|
||||
.getContains()
|
||||
.stream()
|
||||
.filter(t -> (theCodeSystem == null || t.getSystem().equals(theCodeSystem)) && t.getCode().equals(theCode))
|
||||
.findFirst();
|
||||
if (haveMatch.isPresent()) {
|
||||
return new CodeValidationResult(new ConceptDefinitionComponent(new CodeType(theCode)));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return new CodeValidationResult(IssueSeverity.WARNING, e.getMessage());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (theCodeSystem != null) {
|
||||
CodeSystem cs = fetchCodeSystem(theContext, theCodeSystem);
|
||||
if (cs != null) {
|
||||
boolean caseSensitive = true;
|
||||
|
@ -294,13 +323,14 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
|
|||
return retVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new CodeValidationResult(IssueSeverity.WARNING, "Unknown code: " + theCodeSystem + " / " + theCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) {
|
||||
return validateCode(theContext, theSystem, theCode, null).asLookupCodeResult(theSystem, theCode);
|
||||
return validateCode(theContext, theSystem, theCode, null, null).asLookupCodeResult(theSystem, theCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -275,7 +275,7 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander
|
|||
|
||||
@Override
|
||||
public ValidationResult validateCode(String theSystem, String theCode, String theDisplay) {
|
||||
IValidationSupport.CodeValidationResult result = myValidationSupport.validateCode(myCtx, theSystem, theCode, theDisplay);
|
||||
IValidationSupport.CodeValidationResult result = myValidationSupport.validateCode(myCtx, theSystem, theCode, theDisplay, null);
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -303,7 +303,6 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
boolean caseSensitive = true;
|
||||
if (isNotBlank(theSystem)) {
|
||||
CodeSystem system = fetchCodeSystem(theSystem);
|
||||
|
@ -326,32 +325,36 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander
|
|||
/*
|
||||
* The following valueset is a special case, since the BCP codesystem is very difficult to expand
|
||||
*/
|
||||
if (theVs != null && "http://hl7.org/fhir/ValueSet/languages".equals(theVs.getId())) {
|
||||
ValueSet expansion = new ValueSet();
|
||||
for (ConceptSetComponent nextInclude : theVs.getCompose().getInclude()) {
|
||||
for (ConceptReferenceComponent nextConcept : nextInclude.getConcept()) {
|
||||
expansion.getExpansion().addContains().setCode(nextConcept.getCode()).setDisplay(nextConcept.getDisplay());
|
||||
}
|
||||
}
|
||||
expandedValueSet = new ValueSetExpansionOutcome(expansion);
|
||||
if (theVs != null && "http://hl7.org/fhir/ValueSet/languages".equals(theVs.getUrl())) {
|
||||
ConceptDefinitionComponent definition = new ConceptDefinitionComponent();
|
||||
definition.setCode(theSystem);
|
||||
definition.setDisplay(theCode);
|
||||
return new ValidationResult(definition);
|
||||
}
|
||||
|
||||
/*
|
||||
* We'll just accept all mimetypes, since this is pretty much impossible to exhaustively
|
||||
* validate.
|
||||
* The following valueset is a special case, since the mime types codesystem is very difficult to expand
|
||||
*/
|
||||
if (theVs != null && "http://hl7.org/fhir/ValueSet/mimetypes".equals(theVs.getUrl())) {
|
||||
ConceptDefinitionComponent definition = new ConceptDefinitionComponent();
|
||||
definition.setCode(wantCode);
|
||||
definition.setDisplay(wantCode);
|
||||
ValidationResult retVal = new ValidationResult(definition);
|
||||
return retVal;
|
||||
definition.setCode(theSystem);
|
||||
definition.setDisplay(theCode);
|
||||
return new ValidationResult(definition);
|
||||
}
|
||||
|
||||
if (expandedValueSet == null) {
|
||||
if (theVs != null && isNotBlank(theVs.getUrl())) {
|
||||
IValidationSupport.CodeValidationResult outcome = myValidationSupport.validateCode(myCtx, theSystem, theCode, theDisplay, theVs.getUrl());
|
||||
if (outcome != null && outcome.isOk()) {
|
||||
ConceptDefinitionComponent definition = new ConceptDefinitionComponent();
|
||||
definition.setCode(theCode);
|
||||
definition.setDisplay(outcome.getDisplay());
|
||||
return new ValidationResult(definition);
|
||||
}
|
||||
} else {
|
||||
expandedValueSet = expand(theVs, null);
|
||||
}
|
||||
|
||||
if (expandedValueSet != null) {
|
||||
for (ValueSetExpansionContainsComponent next : expandedValueSet.getValueset().getExpansion().getContains()) {
|
||||
String nextCode = next.getCode();
|
||||
if (!caseSensitive) {
|
||||
|
@ -368,6 +371,7 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new ValidationResult(IssueSeverity.ERROR, "Unknown code[" + theCode + "] in system[" + theSystem + "]");
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ public interface IValidationSupport
|
|||
* @param uri Canonical Uri of the ValueSet
|
||||
* @return The valueset (must not be null, but can be an empty ValueSet)
|
||||
*/
|
||||
@Override
|
||||
ValueSet fetchValueSet(FhirContext theContext, String uri);
|
||||
|
||||
@Override
|
||||
|
@ -69,10 +70,13 @@ public interface IValidationSupport
|
|||
* @param theCodeSystem The code system, e.g. "<code>http://loinc.org</code>"
|
||||
* @param theCode The code, e.g. "<code>1234-5</code>"
|
||||
* @param theDisplay The display name, if it should also be validated
|
||||
* @param theValueSetUrl When validating that a code exists as a part of a specific ValueSet, the ValueSet URI
|
||||
* will be provided in this parameter value. If <code>null</code>, the validation should simply
|
||||
* confirm that the code exists.
|
||||
* @return Returns a validation result object
|
||||
*/
|
||||
@Override
|
||||
CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay);
|
||||
CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl);
|
||||
|
||||
/**
|
||||
* Generate a snapshot from the given differential profile.
|
||||
|
|
|
@ -14,6 +14,9 @@ import org.junit.*;
|
|||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class XmlUtilDstu3Test {
|
||||
|
||||
|
@ -72,11 +75,11 @@ public class XmlUtilDstu3Test {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testApplyUnsupportedFeature() {
|
||||
assertNotNull(XmlUtil.newDocumentBuilderFactory());
|
||||
public void testApplyUnsupportedFeature() throws IOException, SAXException {
|
||||
assertNotNull(XmlUtil.parseDocument("<document></document>"));
|
||||
|
||||
XmlUtil.setThrowExceptionForUnitTest(new ParserConfigurationException("AA"));
|
||||
assertNotNull(XmlUtil.newDocumentBuilderFactory());
|
||||
assertNotNull(XmlUtil.parseDocument("<document></document>"));
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
|
|
|
@ -117,6 +117,10 @@
|
|||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Testing -->
|
||||
<dependency>
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.hl7.fhir.r4.hapi.ctx;
|
|||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
@ -13,6 +14,7 @@ import org.hl7.fhir.r4.model.ValueSet.ConceptReferenceComponent;
|
|||
import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent;
|
||||
import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent;
|
||||
import org.hl7.fhir.r4.terminologies.ValueSetExpander;
|
||||
import org.hl7.fhir.r4.terminologies.ValueSetExpanderSimple;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -294,7 +296,32 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
|
||||
if (isNotBlank(theValueSetUrl)) {
|
||||
ValueSetExpander expander = new ValueSetExpanderSimple(new HapiWorkerContext(theContext, this));
|
||||
try {
|
||||
ValueSet valueSet = fetchValueSet(theContext, theValueSetUrl);
|
||||
if (valueSet != null) {
|
||||
ValueSetExpander.ValueSetExpansionOutcome expanded = expander.expand(valueSet, null);
|
||||
Optional<ValueSet.ValueSetExpansionContainsComponent> haveMatch = expanded
|
||||
.getValueset()
|
||||
.getExpansion()
|
||||
.getContains()
|
||||
.stream()
|
||||
.filter(t -> (theCodeSystem == null || t.getSystem().equals(theCodeSystem)) && t.getCode().equals(theCode))
|
||||
.findFirst();
|
||||
if (haveMatch.isPresent()) {
|
||||
return new CodeValidationResult(new ConceptDefinitionComponent(new CodeType(theCode)));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return new CodeValidationResult(IssueSeverity.WARNING, e.getMessage());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (theCodeSystem != null) {
|
||||
CodeSystem cs = fetchCodeSystem(theContext, theCodeSystem);
|
||||
if (cs != null) {
|
||||
boolean caseSensitive = true;
|
||||
|
@ -308,13 +335,14 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
|
|||
return retVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new CodeValidationResult(IssueSeverity.WARNING, "Unknown code: " + theCodeSystem + " / " + theCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) {
|
||||
return validateCode(theContext, theSystem, theCode, null).asLookupCodeResult(theSystem, theCode);
|
||||
return validateCode(theContext, theSystem, theCode, null, null).asLookupCodeResult(theSystem, theCode);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -177,7 +177,7 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander
|
|||
|
||||
@Override
|
||||
public ValidationResult validateCode(TerminologyServiceOptions theOptions, String theSystem, String theCode, String theDisplay) {
|
||||
CodeValidationResult result = myValidationSupport.validateCode(myCtx, theSystem, theCode, theDisplay);
|
||||
CodeValidationResult result = myValidationSupport.validateCode(myCtx, theSystem, theCode, theDisplay, null);
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -227,29 +227,36 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander
|
|||
/*
|
||||
* The following valueset is a special case, since the BCP codesystem is very difficult to expand
|
||||
*/
|
||||
if (theVs != null && "http://hl7.org/fhir/ValueSet/languages".equals(theVs.getId())) {
|
||||
ValueSet expansion = new ValueSet();
|
||||
for (ConceptSetComponent nextInclude : theVs.getCompose().getInclude()) {
|
||||
for (ConceptReferenceComponent nextConcept : nextInclude.getConcept()) {
|
||||
expansion.getExpansion().addContains().setCode(nextConcept.getCode()).setDisplay(nextConcept.getDisplay());
|
||||
}
|
||||
}
|
||||
expandedValueSet = new ValueSetExpansionOutcome(expansion);
|
||||
if (theVs != null && "http://hl7.org/fhir/ValueSet/languages".equals(theVs.getUrl())) {
|
||||
ConceptDefinitionComponent definition = new ConceptDefinitionComponent();
|
||||
definition.setCode(theSystem);
|
||||
definition.setDisplay(theCode);
|
||||
return new ValidationResult(definition);
|
||||
}
|
||||
|
||||
/*
|
||||
* The following valueset is a special case, since the mime types codesystem is very difficult to expand
|
||||
*/
|
||||
if (theVs != null && "http://hl7.org/fhir/ValueSet/mimetypes".equals(theVs.getId())) {
|
||||
ValueSet expansion = new ValueSet();
|
||||
expansion.getExpansion().addContains().setCode(theCode).setSystem(theSystem).setDisplay(theDisplay);
|
||||
expandedValueSet = new ValueSetExpansionOutcome(expansion);
|
||||
if (theVs != null && "http://hl7.org/fhir/ValueSet/mimetypes".equals(theVs.getUrl())) {
|
||||
ConceptDefinitionComponent definition = new ConceptDefinitionComponent();
|
||||
definition.setCode(theSystem);
|
||||
definition.setDisplay(theCode);
|
||||
return new ValidationResult(definition);
|
||||
}
|
||||
|
||||
if (expandedValueSet == null) {
|
||||
if (theVs != null && isNotBlank(theVs.getUrl())) {
|
||||
CodeValidationResult outcome = myValidationSupport.validateCode(myCtx, theSystem, theCode, theDisplay, theVs.getUrl());
|
||||
if (outcome != null && outcome.isOk()) {
|
||||
ConceptDefinitionComponent definition = new ConceptDefinitionComponent();
|
||||
definition.setCode(theCode);
|
||||
definition.setDisplay(outcome.getDisplay());
|
||||
return new ValidationResult(definition);
|
||||
}
|
||||
} else {
|
||||
expandedValueSet = expand(theVs, null);
|
||||
}
|
||||
|
||||
if (expandedValueSet != null) {
|
||||
for (ValueSetExpansionContainsComponent next : expandedValueSet.getValueset().getExpansion().getContains()) {
|
||||
String nextCode = next.getCode();
|
||||
if (!caseSensitive) {
|
||||
|
@ -266,6 +273,7 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new ValidationResult(IssueSeverity.ERROR, "Unknown code[" + theCode + "] in system[" + theSystem + "]");
|
||||
}
|
||||
|
@ -330,12 +338,12 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setLogger(ILoggingService theLogger) {
|
||||
public ILoggingService getLogger() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ILoggingService getLogger() {
|
||||
public void setLogger(ILoggingService theLogger) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
@ -349,6 +357,11 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander
|
|||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUcumService(UcumService ucumService) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNoTerminologyServer() {
|
||||
return false;
|
||||
|
@ -384,11 +397,6 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander
|
|||
return fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + typeName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUcumService(UcumService ucumService) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLinkForUrl(String corePath, String url) {
|
||||
throw new UnsupportedOperationException();
|
||||
|
|
|
@ -3,6 +3,7 @@ package org.hl7.fhir.r4.hapi.ctx;
|
|||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.support.IContextValidationSupport;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4.context.IWorkerContext;
|
||||
import org.hl7.fhir.r4.model.CodeSystem;
|
||||
import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionComponent;
|
||||
import org.hl7.fhir.r4.model.StructureDefinition;
|
||||
|
@ -46,6 +47,7 @@ public interface IValidationSupport
|
|||
* @param uri Canonical Uri of the ValueSet
|
||||
* @return The valueset (must not be null, but can be an empty ValueSet)
|
||||
*/
|
||||
@Override
|
||||
ValueSet fetchValueSet(FhirContext theContext, String uri);
|
||||
|
||||
/**
|
||||
|
@ -90,10 +92,13 @@ public interface IValidationSupport
|
|||
* @param theCodeSystem The code system, e.g. "<code>http://loinc.org</code>"
|
||||
* @param theCode The code, e.g. "<code>1234-5</code>"
|
||||
* @param theDisplay The display name, if it should also be validated
|
||||
* @param theValueSetUrl When validating that a code exists as a part of a specific ValueSet, the ValueSet URI
|
||||
* will be provided in this parameter value. If <code>null</code>, the validation should simply
|
||||
* confirm that the code exists.
|
||||
* @return Returns a validation result object
|
||||
*/
|
||||
@Override
|
||||
CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay);
|
||||
CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl);
|
||||
|
||||
class CodeValidationResult extends IContextValidationSupport.CodeValidationResult<ConceptDefinitionComponent, IssueSeverity> {
|
||||
|
||||
|
|
|
@ -1,20 +1,21 @@
|
|||
package org.hl7.fhir.r4.test.support;
|
||||
|
||||
import ca.uhn.fhir.util.XmlUtil;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.gson.*;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.hl7.fhir.r4.context.IWorkerContext;
|
||||
import org.hl7.fhir.utilities.CSFile;
|
||||
import org.hl7.fhir.utilities.TextFile;
|
||||
import org.hl7.fhir.utilities.Utilities;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NamedNodeMap;
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import java.io.*;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -172,30 +173,8 @@ public class TestingUtilities {
|
|||
}
|
||||
|
||||
private static Document loadXml(InputStream fn) throws Exception {
|
||||
DocumentBuilderFactory factory = XmlUtil.newDocumentBuilderFactory();
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
return builder.parse(fn);
|
||||
}
|
||||
|
||||
public static String checkJsonIsSame(String f1, String f2) throws JsonSyntaxException, FileNotFoundException, IOException {
|
||||
String result = compareJson(f1, f2);
|
||||
if (result != null && SHOW_DIFF) {
|
||||
String diff = Utilities.path(System.getenv("ProgramFiles(X86)"), "WinMerge", "WinMergeU.exe");
|
||||
List<String> command = new ArrayList<String>();
|
||||
command.add("\"" + diff + "\" \"" + f1 + "\" \"" + f2 + "\"");
|
||||
|
||||
ProcessBuilder builder = new ProcessBuilder(command);
|
||||
builder.directory(new CSFile("c:\\temp"));
|
||||
builder.start();
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static String compareJson(String f1, String f2) throws JsonSyntaxException, FileNotFoundException, IOException {
|
||||
JsonObject o1 = (JsonObject) new com.google.gson.JsonParser().parse(TextFile.fileToString(f1));
|
||||
JsonObject o2 = (JsonObject) new com.google.gson.JsonParser().parse(TextFile.fileToString(f2));
|
||||
return compareObjects("", o1, o2);
|
||||
String input = IOUtils.toString(fn, Charsets.UTF_8);
|
||||
return XmlUtil.parseDocument(input);
|
||||
}
|
||||
|
||||
private static String compareObjects(String path, JsonObject o1, JsonObject o2) {
|
||||
|
@ -250,9 +229,9 @@ public class TestingUtilities {
|
|||
JsonArray a2 = (JsonArray) n2;
|
||||
|
||||
if (a1.size() != a2.size())
|
||||
return "array properties differ at " + path + ": count " + Integer.toString(a1.size()) + "/" + Integer.toString(a2.size());
|
||||
return "array properties differ at " + path + ": count " + a1.size() + "/" + a2.size();
|
||||
for (int i = 0; i < a1.size(); i++) {
|
||||
String s = compareNodes(path + "[" + Integer.toString(i) + "]", a1.get(i), a2.get(i));
|
||||
String s = compareNodes(path + "[" + i + "]", a1.get(i), a2.get(i));
|
||||
if (!Utilities.noString(s))
|
||||
return s;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package org.hl7.fhir.r5.hapi.ctx;
|
|||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.support.IContextValidationSupport;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
@ -14,6 +15,7 @@ import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceComponent;
|
|||
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
|
||||
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent;
|
||||
import org.hl7.fhir.r5.terminologies.ValueSetExpander;
|
||||
import org.hl7.fhir.r5.terminologies.ValueSetExpanderSimple;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -295,7 +297,32 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
|
||||
if (isNotBlank(theValueSetUrl)) {
|
||||
ValueSetExpander expander = new ValueSetExpanderSimple(new HapiWorkerContext(theContext, this));
|
||||
try {
|
||||
ValueSet valueSet = fetchValueSet(theContext, theValueSetUrl);
|
||||
if (valueSet != null) {
|
||||
ValueSetExpander.ValueSetExpansionOutcome expanded = expander.expand(valueSet, null);
|
||||
Optional<ValueSet.ValueSetExpansionContainsComponent> haveMatch = expanded
|
||||
.getValueset()
|
||||
.getExpansion()
|
||||
.getContains()
|
||||
.stream()
|
||||
.filter(t -> (theCodeSystem == null || t.getSystem().equals(theCodeSystem)) && t.getCode().equals(theCode))
|
||||
.findFirst();
|
||||
if (haveMatch.isPresent()) {
|
||||
return new CodeValidationResult(new ConceptDefinitionComponent(new CodeType(theCode)));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return new CodeValidationResult(IssueSeverity.WARNING, e.getMessage());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (theCodeSystem != null) {
|
||||
CodeSystem cs = fetchCodeSystem(theContext, theCodeSystem);
|
||||
if (cs != null) {
|
||||
boolean caseSensitive = true;
|
||||
|
@ -309,13 +336,14 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
|
|||
return retVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new CodeValidationResult(IssueSeverity.WARNING, "Unknown code: " + theCodeSystem + " / " + theCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IContextValidationSupport.LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) {
|
||||
return validateCode(theContext, theSystem, theCode, null).asLookupCodeResult(theSystem, theCode);
|
||||
return validateCode(theContext, theSystem, theCode, null, null).asLookupCodeResult(theSystem, theCode);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -176,7 +176,7 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander
|
|||
|
||||
@Override
|
||||
public ValidationResult validateCode(TerminologyServiceOptions theOptions, String theSystem, String theCode, String theDisplay) {
|
||||
CodeValidationResult result = myValidationSupport.validateCode(myCtx, theSystem, theCode, theDisplay);
|
||||
CodeValidationResult result = myValidationSupport.validateCode(myCtx, theSystem, theCode, theDisplay, null);
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -226,29 +226,36 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander
|
|||
/*
|
||||
* The following valueset is a special case, since the BCP codesystem is very difficult to expand
|
||||
*/
|
||||
if (theVs != null && "http://hl7.org/fhir/ValueSet/languages".equals(theVs.getId())) {
|
||||
ValueSet expansion = new ValueSet();
|
||||
for (ConceptSetComponent nextInclude : theVs.getCompose().getInclude()) {
|
||||
for (ConceptReferenceComponent nextConcept : nextInclude.getConcept()) {
|
||||
expansion.getExpansion().addContains().setCode(nextConcept.getCode()).setDisplay(nextConcept.getDisplay());
|
||||
}
|
||||
}
|
||||
expandedValueSet = new ValueSetExpansionOutcome(expansion);
|
||||
if (theVs != null && "http://hl7.org/fhir/ValueSet/languages".equals(theVs.getUrl())) {
|
||||
ConceptDefinitionComponent definition = new ConceptDefinitionComponent();
|
||||
definition.setCode(theSystem);
|
||||
definition.setDisplay(theCode);
|
||||
return new ValidationResult(definition);
|
||||
}
|
||||
|
||||
/*
|
||||
* The following valueset is a special case, since the mime types codesystem is very difficult to expand
|
||||
*/
|
||||
if (theVs != null && "http://hl7.org/fhir/ValueSet/mimetypes".equals(theVs.getId())) {
|
||||
ValueSet expansion = new ValueSet();
|
||||
expansion.getExpansion().addContains().setCode(theCode).setSystem(theSystem).setDisplay(theDisplay);
|
||||
expandedValueSet = new ValueSetExpansionOutcome(expansion);
|
||||
if (theVs != null && "http://hl7.org/fhir/ValueSet/mimetypes".equals(theVs.getUrl())) {
|
||||
ConceptDefinitionComponent definition = new ConceptDefinitionComponent();
|
||||
definition.setCode(theSystem);
|
||||
definition.setDisplay(theCode);
|
||||
return new ValidationResult(definition);
|
||||
}
|
||||
|
||||
if (expandedValueSet == null) {
|
||||
if (theVs != null && isNotBlank(theVs.getUrl())) {
|
||||
CodeValidationResult outcome = myValidationSupport.validateCode(myCtx, theSystem, theCode, theDisplay, theVs.getUrl());
|
||||
if (outcome != null && outcome.isOk()) {
|
||||
ConceptDefinitionComponent definition = new ConceptDefinitionComponent();
|
||||
definition.setCode(theCode);
|
||||
definition.setDisplay(outcome.getDisplay());
|
||||
return new ValidationResult(definition);
|
||||
}
|
||||
} else {
|
||||
expandedValueSet = expand(theVs, null);
|
||||
}
|
||||
|
||||
if (expandedValueSet != null) {
|
||||
for (ValueSetExpansionContainsComponent next : expandedValueSet.getValueset().getExpansion().getContains()) {
|
||||
String nextCode = next.getCode();
|
||||
if (!caseSensitive) {
|
||||
|
@ -265,10 +272,12 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new ValidationResult(IssueSeverity.ERROR, "Unknown code[" + theCode + "] in system[" + theSystem + "]");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ValidationResult validateCode(TerminologyServiceOptions theOptions, String code, ValueSet vs) {
|
||||
return validateCode(theOptions, null, code, null, vs);
|
||||
|
|
|
@ -46,6 +46,7 @@ public interface IValidationSupport
|
|||
* @param uri Canonical Uri of the ValueSet
|
||||
* @return The valueset (must not be null, but can be an empty ValueSet)
|
||||
*/
|
||||
@Override
|
||||
ValueSet fetchValueSet(FhirContext theContext, String uri);
|
||||
|
||||
/**
|
||||
|
@ -94,10 +95,13 @@ public interface IValidationSupport
|
|||
* @param theCodeSystem The code system, e.g. "<code>http://loinc.org</code>"
|
||||
* @param theCode The code, e.g. "<code>1234-5</code>"
|
||||
* @param theDisplay The display name, if it should also be validated
|
||||
* @param theValueSetUrl When validating that a code exists as a part of a specific ValueSet, the ValueSet URI
|
||||
* will be provided in this parameter value. If <code>null</code>, the validation should simply
|
||||
* confirm that the code exists.
|
||||
* @return Returns a validation result object
|
||||
*/
|
||||
@Override
|
||||
CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay);
|
||||
CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl);
|
||||
|
||||
class CodeValidationResult extends IContextValidationSupport.CodeValidationResult<ConceptDefinitionComponent, IssueSeverity> {
|
||||
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
package ca.uhn.fhir.test;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class BaseTest {
|
||||
|
||||
protected String loadResource(String theClasspath) throws IOException {
|
||||
InputStream stream = BaseTest.class.getResourceAsStream(theClasspath);
|
||||
if (stream==null) {
|
||||
throw new IllegalArgumentException("Unable to find resource: " + theClasspath);
|
||||
}
|
||||
return IOUtils.toString(stream, Charsets.UTF_8);
|
||||
}
|
||||
|
||||
}
|
|
@ -28,7 +28,6 @@ import java.util.List;
|
|||
public class ValidatorWrapper {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(ValidatorWrapper.class);
|
||||
private final DocumentBuilderFactory myDocBuilderFactory;
|
||||
private IResourceValidator.BestPracticeWarningLevel myBestPracticeWarningLevel;
|
||||
private boolean myAnyExtensionsAllowed;
|
||||
private boolean myErrorForUnknownProfiles;
|
||||
|
@ -39,7 +38,7 @@ public class ValidatorWrapper {
|
|||
* Constructor
|
||||
*/
|
||||
public ValidatorWrapper() {
|
||||
myDocBuilderFactory = XmlUtil.newDocumentBuilderFactory();
|
||||
super();
|
||||
}
|
||||
|
||||
public ValidatorWrapper setBestPracticeWarningLevel(IResourceValidator.BestPracticeWarningLevel theBestPracticeWarningLevel) {
|
||||
|
@ -95,9 +94,7 @@ public class ValidatorWrapper {
|
|||
if (encoding == EncodingEnum.XML) {
|
||||
Document document;
|
||||
try {
|
||||
DocumentBuilder builder = myDocBuilderFactory.newDocumentBuilder();
|
||||
InputSource src = new InputSource(new StringReader(input));
|
||||
document = builder.parse(src);
|
||||
document = XmlUtil.parseDocument(input);
|
||||
} catch (Exception e2) {
|
||||
ourLog.error("Failure to parse XML input", e2);
|
||||
ValidationMessage m = new ValidationMessage();
|
||||
|
|
|
@ -17,6 +17,8 @@ import java.util.Optional;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class CachingValidationSupport implements IValidationSupport {
|
||||
|
||||
|
@ -73,8 +75,9 @@ public class CachingValidationSupport implements IValidationSupport {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
||||
return myWrap.validateCode(theContext, theCodeSystem, theCode, theDisplay);
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
|
||||
String key = "validateCode " + theCodeSystem + " " + theCode + " " + defaultIfBlank(theValueSetUrl, "NO_VS");
|
||||
return loadFromCache(key, t -> myWrap.validateCode(theContext, theCodeSystem, theCode, theDisplay, theValueSetUrl));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -3,7 +3,6 @@ package org.hl7.fhir.dstu3.hapi.validation;
|
|||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.util.XmlUtil;
|
||||
import ca.uhn.fhir.validation.IValidationContext;
|
||||
import ca.uhn.fhir.validation.IValidatorModule;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
|
@ -38,7 +37,6 @@ import org.w3c.dom.NodeList;
|
|||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
|
@ -51,7 +49,6 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
|
||||
private boolean myAnyExtensionsAllowed = true;
|
||||
private BestPracticeWarningLevel myBestPracticeWarningLevel;
|
||||
private DocumentBuilderFactory myDocBuilderFactory;
|
||||
private StructureDefinition myStructureDefintion;
|
||||
private IValidationSupport myValidationSupport;
|
||||
private boolean noTerminologyChecks = false;
|
||||
|
@ -75,7 +72,6 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
* @param theValidationSupport The validation support
|
||||
*/
|
||||
public FhirInstanceValidator(IValidationSupport theValidationSupport) {
|
||||
myDocBuilderFactory = XmlUtil.newDocumentBuilderFactory();
|
||||
myValidationSupport = theValidationSupport;
|
||||
}
|
||||
|
||||
|
|
|
@ -181,7 +181,7 @@ public class PrePopulatedValidationSupport implements IValidationSupport {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -91,7 +91,7 @@ public class SnapshotGeneratingValidationSupport implements IValidationSupport {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -150,13 +150,13 @@ public class ValidationSupportChain implements IValidationSupport {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(FhirContext theCtx, String theCodeSystem, String theCode, String theDisplay) {
|
||||
public CodeValidationResult validateCode(FhirContext theCtx, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
|
||||
|
||||
ourLog.debug("Validating code {} in chain with {} items", theCode, myChain.size());
|
||||
|
||||
for (IValidationSupport next : myChain) {
|
||||
if (next.isCodeSystemSupported(theCtx, theCodeSystem)) {
|
||||
CodeValidationResult result = next.validateCode(theCtx, theCodeSystem, theCode, theDisplay);
|
||||
if (theCodeSystem != null && next.isCodeSystemSupported(theCtx, theCodeSystem)) {
|
||||
CodeValidationResult result = next.validateCode(theCtx, theCodeSystem, theCode, theDisplay, theValueSetUrl);
|
||||
if (result != null) {
|
||||
ourLog.debug("Chain item {} returned outcome {}", next, result.isOk());
|
||||
return result;
|
||||
|
@ -165,7 +165,7 @@ public class ValidationSupportChain implements IValidationSupport {
|
|||
ourLog.debug("Chain item {} does not support code system {}", next, theCodeSystem);
|
||||
}
|
||||
}
|
||||
return myChain.get(0).validateCode(theCtx, theCodeSystem, theCode, theDisplay);
|
||||
return myChain.get(0).validateCode(theCtx, theCodeSystem, theCode, theDisplay, theValueSetUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -41,13 +41,9 @@ import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
|
|||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.StringReader;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
|
@ -63,7 +59,6 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
|
||||
private boolean myAnyExtensionsAllowed = true;
|
||||
private BestPracticeWarningLevel myBestPracticeWarningLevel;
|
||||
private DocumentBuilderFactory myDocBuilderFactory;
|
||||
private StructureDefinition myStructureDefintion;
|
||||
private IValidationSupport myValidationSupport;
|
||||
private boolean noTerminologyChecks = false;
|
||||
|
@ -85,7 +80,6 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
* @param theValidationSupport The validation support
|
||||
*/
|
||||
public FhirInstanceValidator(IValidationSupport theValidationSupport) {
|
||||
myDocBuilderFactory = XmlUtil.newDocumentBuilderFactory();
|
||||
myValidationSupport = theValidationSupport;
|
||||
}
|
||||
|
||||
|
@ -280,9 +274,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
if (theEncoding == EncodingEnum.XML) {
|
||||
Document document;
|
||||
try {
|
||||
DocumentBuilder builder = myDocBuilderFactory.newDocumentBuilder();
|
||||
InputSource src = new InputSource(new StringReader(theInput));
|
||||
document = builder.parse(src);
|
||||
document = XmlUtil.parseDocument(theInput);
|
||||
} catch (Exception e2) {
|
||||
ourLog.error("Failure to parse XML input", e2);
|
||||
ValidationMessage m = new ValidationMessage();
|
||||
|
|
|
@ -91,7 +91,7 @@ public class ValidationSupportChain implements IValidationSupport {
|
|||
@Override
|
||||
public CodeValidationResult validateCode(FhirContext theCtx, String theCodeSystem, String theCode, String theDisplay) {
|
||||
for (IValidationSupport next : myChain) {
|
||||
if (next.isCodeSystemSupported(theCtx, theCodeSystem)) {
|
||||
if (theCodeSystem != null && next.isCodeSystemSupported(theCtx, theCodeSystem)) {
|
||||
return next.validateCode(theCtx, theCodeSystem, theCode, theDisplay);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@ import java.util.Optional;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class CachingValidationSupport implements IValidationSupport {
|
||||
|
||||
|
@ -81,9 +83,9 @@ public class CachingValidationSupport implements IValidationSupport {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
||||
String key = "validateCode " + theCodeSystem + " " + theCode;
|
||||
return loadFromCache(key, t -> myWrap.validateCode(theContext, theCodeSystem, theCode, theDisplay));
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
|
||||
String key = "validateCode " + theCodeSystem + " " + theCode + " " + defaultIfBlank(theValueSetUrl, "NO_VS");
|
||||
return loadFromCache(key, t -> myWrap.validateCode(theContext, theCodeSystem, theCode, theDisplay, theValueSetUrl));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -188,7 +188,7 @@ public class PrePopulatedValidationSupport implements IValidationSupport {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -92,7 +92,7 @@ public class SnapshotGeneratingValidationSupport implements IValidationSupport {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -141,13 +141,13 @@ public class ValidationSupportChain implements IValidationSupport {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(FhirContext theCtx, String theCodeSystem, String theCode, String theDisplay) {
|
||||
public CodeValidationResult validateCode(FhirContext theCtx, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
|
||||
|
||||
ourLog.debug("Validating code {} in chain with {} items", theCode, myChain.size());
|
||||
|
||||
for (IValidationSupport next : myChain) {
|
||||
if (next.isCodeSystemSupported(theCtx, theCodeSystem)) {
|
||||
CodeValidationResult result = next.validateCode(theCtx, theCodeSystem, theCode, theDisplay);
|
||||
if (theCodeSystem != null && next.isCodeSystemSupported(theCtx, theCodeSystem)) {
|
||||
CodeValidationResult result = next.validateCode(theCtx, theCodeSystem, theCode, theDisplay, theValueSetUrl);
|
||||
if (result != null) {
|
||||
ourLog.debug("Chain item {} returned outcome {}", next, result.isOk());
|
||||
return result;
|
||||
|
@ -156,7 +156,7 @@ public class ValidationSupportChain implements IValidationSupport {
|
|||
ourLog.debug("Chain item {} does not support code system {}", next, theCodeSystem);
|
||||
}
|
||||
}
|
||||
return myChain.get(0).validateCode(theCtx, theCodeSystem, theCode, theDisplay);
|
||||
return myChain.get(0).validateCode(theCtx, theCodeSystem, theCode, theDisplay, theValueSetUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,6 +16,8 @@ import java.util.Optional;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class CachingValidationSupport implements IValidationSupport {
|
||||
|
||||
|
@ -81,9 +83,9 @@ public class CachingValidationSupport implements IValidationSupport {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
||||
String key = "validateCode " + theCodeSystem + " " + theCode;
|
||||
return loadFromCache(key, t -> myWrap.validateCode(theContext, theCodeSystem, theCode, theDisplay));
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
|
||||
String key = "validateCode " + theCodeSystem + " " + theCode + " " + defaultIfBlank(theValueSetUrl, "NO_VS");
|
||||
return loadFromCache(key, t -> myWrap.validateCode(theContext, theCodeSystem, theCode, theDisplay, theValueSetUrl));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -188,7 +188,7 @@ public class PrePopulatedValidationSupport implements IValidationSupport {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -92,7 +92,7 @@ public class SnapshotGeneratingValidationSupport implements IValidationSupport {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -139,13 +139,13 @@ public class ValidationSupportChain implements IValidationSupport {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(FhirContext theCtx, String theCodeSystem, String theCode, String theDisplay) {
|
||||
public CodeValidationResult validateCode(FhirContext theCtx, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
|
||||
|
||||
ourLog.debug("Validating code {} in chain with {} items", theCode, myChain.size());
|
||||
|
||||
for (IValidationSupport next : myChain) {
|
||||
if (next.isCodeSystemSupported(theCtx, theCodeSystem)) {
|
||||
CodeValidationResult result = next.validateCode(theCtx, theCodeSystem, theCode, theDisplay);
|
||||
if (theCodeSystem != null && next.isCodeSystemSupported(theCtx, theCodeSystem)) {
|
||||
CodeValidationResult result = next.validateCode(theCtx, theCodeSystem, theCode, theDisplay, theValueSetUrl);
|
||||
if (result != null) {
|
||||
ourLog.debug("Chain item {} returned outcome {}", next, result.isOk());
|
||||
return result;
|
||||
|
@ -154,7 +154,7 @@ public class ValidationSupportChain implements IValidationSupport {
|
|||
ourLog.debug("Chain item {} does not support code system {}", next, theCodeSystem);
|
||||
}
|
||||
}
|
||||
return myChain.get(0).validateCode(theCtx, theCodeSystem, theCode, theDisplay);
|
||||
return myChain.get(0).validateCode(theCtx, theCodeSystem, theCode, theDisplay, theValueSetUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -53,6 +53,7 @@ import org.mockito.stubbing.Answer;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
|
@ -158,12 +159,14 @@ public class FhirInstanceValidatorDstu3Test {
|
|||
return retVal;
|
||||
}
|
||||
});
|
||||
when(myMockSupport.validateCode(nullable(FhirContext.class), nullable(String.class), nullable(String.class), nullable(String.class))).thenAnswer(new Answer<CodeValidationResult>() {
|
||||
when(myMockSupport.validateCode(nullable(FhirContext.class), nullable(String.class), nullable(String.class), nullable(String.class), nullable(String.class))).thenAnswer(new Answer<CodeValidationResult>() {
|
||||
@Override
|
||||
public CodeValidationResult answer(InvocationOnMock theInvocation) {
|
||||
FhirContext ctx = (FhirContext) theInvocation.getArguments()[0];
|
||||
String system = (String) theInvocation.getArguments()[1];
|
||||
String code = (String) theInvocation.getArguments()[2];
|
||||
FhirContext ctx = theInvocation.getArgument(0, FhirContext.class);
|
||||
String system = theInvocation.getArgument(1, String.class);
|
||||
String code = theInvocation.getArgument(2, String.class);
|
||||
String display = theInvocation.getArgument(3, String.class);
|
||||
String valueSetUrl = theInvocation.getArgument(4, String.class);
|
||||
CodeValidationResult retVal;
|
||||
if (myValidConcepts.contains(system + "___" + code)) {
|
||||
retVal = new CodeValidationResult(new ConceptDefinitionComponent(new CodeType(code)));
|
||||
|
@ -172,9 +175,9 @@ public class FhirInstanceValidatorDstu3Test {
|
|||
Optional<ConceptDefinitionComponent> found = cs.getConcept().stream().filter(t -> t.getCode().equals(code)).findFirst();
|
||||
retVal = found.map(t->new CodeValidationResult(t)).orElse(null);
|
||||
} else {
|
||||
retVal = myDefaultValidationSupport.validateCode(ctx, system, code, (String) theInvocation.getArguments()[2]);
|
||||
retVal = myDefaultValidationSupport.validateCode(ctx, system, code, display, valueSetUrl);
|
||||
}
|
||||
ourLog.debug("validateCode({}, {}, {}) : {}", new Object[] {system, code, theInvocation.getArguments()[2], retVal});
|
||||
ourLog.debug("validateCode({}, {}, {}, {}) : {}", system, code, display, valueSetUrl, retVal);
|
||||
return retVal;
|
||||
}
|
||||
});
|
||||
|
@ -241,7 +244,7 @@ public class FhirInstanceValidatorDstu3Test {
|
|||
int index = 0;
|
||||
for (SingleValidationMessage next : theOutput.getMessages()) {
|
||||
ourLog.info("Result {}: {} - {}:{} {} - {}",
|
||||
new Object[] {index, next.getSeverity(), defaultString(next.getLocationLine()), defaultString(next.getLocationCol()), next.getLocationString(), next.getMessage()});
|
||||
index, next.getSeverity(), defaultString(next.getLocationLine()), defaultString(next.getLocationCol()), next.getLocationString(), next.getMessage());
|
||||
index++;
|
||||
|
||||
retVal.add(next);
|
||||
|
@ -255,7 +258,7 @@ public class FhirInstanceValidatorDstu3Test {
|
|||
|
||||
int index = 0;
|
||||
for (SingleValidationMessage next : theOutput.getMessages()) {
|
||||
ourLog.info("Result {}: {} - {} - {}", new Object[] {index, next.getSeverity(), next.getLocationString(), next.getMessage()});
|
||||
ourLog.info("Result {}: {} - {} - {}", index, next.getSeverity(), next.getLocationString(), next.getMessage());
|
||||
index++;
|
||||
|
||||
if (next.getSeverity() != ResultSeverityEnum.INFORMATION) {
|
||||
|
@ -536,7 +539,7 @@ public class FhirInstanceValidatorDstu3Test {
|
|||
String name = "profiles-resources";
|
||||
ourLog.info("Uploading " + name);
|
||||
String vsContents;
|
||||
vsContents = IOUtils.toString(FhirInstanceValidatorDstu3Test.class.getResourceAsStream("/org/hl7/fhir/dstu3/model/profile/" + name + ".xml"), "UTF-8");
|
||||
vsContents = IOUtils.toString(FhirInstanceValidatorDstu3Test.class.getResourceAsStream("/org/hl7/fhir/dstu3/model/profile/" + name + ".xml"), StandardCharsets.UTF_8);
|
||||
|
||||
TreeSet<String> ids = new TreeSet<String>();
|
||||
|
||||
|
@ -566,7 +569,7 @@ public class FhirInstanceValidatorDstu3Test {
|
|||
|
||||
@Test
|
||||
public void testValidateBundleWithNoType() throws Exception {
|
||||
String vsContents = IOUtils.toString(FhirInstanceValidatorDstu3Test.class.getResourceAsStream("/dstu3/bundle-with-no-type.json"), "UTF-8");
|
||||
String vsContents = IOUtils.toString(FhirInstanceValidatorDstu3Test.class.getResourceAsStream("/dstu3/bundle-with-no-type.json"), StandardCharsets.UTF_8);
|
||||
|
||||
ValidationResult output = myVal.validateWithResult(vsContents);
|
||||
logResultsAndReturnNonInformationalOnes(output);
|
||||
|
@ -579,7 +582,7 @@ public class FhirInstanceValidatorDstu3Test {
|
|||
String name = "profiles-resources";
|
||||
ourLog.info("Uploading " + name);
|
||||
String inputString;
|
||||
inputString = IOUtils.toString(FhirInstanceValidatorDstu3Test.class.getResourceAsStream("/brian_reinhold_bundle.json"), "UTF-8");
|
||||
inputString = IOUtils.toString(FhirInstanceValidatorDstu3Test.class.getResourceAsStream("/brian_reinhold_bundle.json"), StandardCharsets.UTF_8);
|
||||
Bundle bundle = ourCtx.newJsonParser().parseResource(Bundle.class, inputString);
|
||||
|
||||
FHIRPathEngine fp = new FHIRPathEngine(new HapiWorkerContext(ourCtx, myDefaultValidationSupport));
|
||||
|
@ -631,7 +634,7 @@ public class FhirInstanceValidatorDstu3Test {
|
|||
String name = "profiles-resources";
|
||||
ourLog.info("Uploading " + name);
|
||||
String vsContents;
|
||||
vsContents = IOUtils.toString(FhirInstanceValidatorDstu3Test.class.getResourceAsStream("/crucible-condition.xml"), "UTF-8");
|
||||
vsContents = IOUtils.toString(FhirInstanceValidatorDstu3Test.class.getResourceAsStream("/crucible-condition.xml"), StandardCharsets.UTF_8);
|
||||
|
||||
ValidationResult output = myVal.validateWithResult(vsContents);
|
||||
List<SingleValidationMessage> errors = logResultsAndReturnNonInformationalOnes(output);
|
||||
|
@ -639,7 +642,7 @@ public class FhirInstanceValidatorDstu3Test {
|
|||
|
||||
@Test
|
||||
public void testValidateDocument() throws Exception {
|
||||
String vsContents = IOUtils.toString(FhirInstanceValidatorDstu3Test.class.getResourceAsStream("/sample-document.xml"), "UTF-8");
|
||||
String vsContents = IOUtils.toString(FhirInstanceValidatorDstu3Test.class.getResourceAsStream("/sample-document.xml"), StandardCharsets.UTF_8);
|
||||
|
||||
ValidationResult output = myVal.validateWithResult(vsContents);
|
||||
logResultsAndReturnNonInformationalOnes(output);
|
||||
|
|
|
@ -69,7 +69,7 @@ public class QuestionnaireResponseValidatorDstu3Test {
|
|||
myVal.setValidateAgainstStandardSchema(false);
|
||||
myVal.setValidateAgainstStandardSchematron(false);
|
||||
|
||||
ValidationSupportChain validationSupport = new ValidationSupportChain(myValSupport, myDefaultValidationSupport);
|
||||
ValidationSupportChain validationSupport = new ValidationSupportChain(myDefaultValidationSupport, myValSupport);
|
||||
myInstanceVal = new FhirInstanceValidator(validationSupport);
|
||||
|
||||
myVal.registerValidatorModule(myInstanceVal);
|
||||
|
@ -196,9 +196,9 @@ public class QuestionnaireResponseValidatorDstu3Test {
|
|||
|
||||
when(myValSupport.isCodeSystemSupported(any(), eq("http://codesystems.com/system"))).thenReturn(true);
|
||||
when(myValSupport.isCodeSystemSupported(any(), eq("http://codesystems.com/system2"))).thenReturn(true);
|
||||
when(myValSupport.validateCode(any(), eq("http://codesystems.com/system"), eq("code0"), any()))
|
||||
when(myValSupport.validateCode(any(), eq("http://codesystems.com/system"), eq("code0"), any(), any()))
|
||||
.thenReturn(new CodeValidationResult(new ConceptDefinitionComponent().setCode("code0")));
|
||||
when(myValSupport.validateCode(any(), eq("http://codesystems.com/system"), eq("code1"), any()))
|
||||
when(myValSupport.validateCode(any(), eq("http://codesystems.com/system"), eq("code1"), any(), any()))
|
||||
.thenReturn(new CodeValidationResult(ValidationMessage.IssueSeverity.ERROR, "Unknown code"));
|
||||
|
||||
CodeSystem codeSystem = new CodeSystem();
|
||||
|
@ -250,7 +250,7 @@ public class QuestionnaireResponseValidatorDstu3Test {
|
|||
errors = myVal.validateWithResult(qa);
|
||||
errors = stripBindingHasNoSourceMessage(errors);
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("Validation failed for 'http://codesystems.com/system2#code3'"));
|
||||
assertThat(errors.toString(), containsString("Unknown code: http://codesystems.com/system2 / code3"));
|
||||
assertThat(errors.toString(), containsString("QuestionnaireResponse.item[0].answer[0]"));
|
||||
|
||||
}
|
||||
|
@ -771,7 +771,7 @@ public class QuestionnaireResponseValidatorDstu3Test {
|
|||
options.getCompose().addInclude().setSystem(codeSystemUrl).addConcept().setCode(codeValue);
|
||||
when(myValSupport.fetchResource(any(FhirContext.class), eq(ValueSet.class), eq(valueSetRef)))
|
||||
.thenReturn(options);
|
||||
when(myValSupport.validateCode(any(FhirContext.class), eq(codeSystemUrl), eq(codeValue), any(String.class)))
|
||||
when(myValSupport.validateCode(any(FhirContext.class), eq(codeSystemUrl), eq(codeValue), any(String.class), any()))
|
||||
.thenReturn(new CodeValidationResult(new ConceptDefinitionComponent(new CodeType(codeValue))));
|
||||
|
||||
IParser xmlParser = ourCtx.newXmlParser().setPrettyPrint(true);
|
||||
|
@ -827,7 +827,7 @@ public class QuestionnaireResponseValidatorDstu3Test {
|
|||
options.getCompose().addInclude().setSystem(codeSystemUrl).addConcept().setCode(codeValue);
|
||||
when(myValSupport.fetchResource(any(FhirContext.class), eq(ValueSet.class), eq(valueSetRef)))
|
||||
.thenReturn(options);
|
||||
when(myValSupport.validateCode(any(FhirContext.class), eq(codeSystemUrl), eq(codeValue), any(String.class)))
|
||||
when(myValSupport.validateCode(any(FhirContext.class), eq(codeSystemUrl), eq(codeValue), any(String.class), any()))
|
||||
.thenReturn(new CodeValidationResult(new ConceptDefinitionComponent(new CodeType(codeValue))));
|
||||
|
||||
IParser xmlParser = ourCtx.newXmlParser().setPrettyPrint(true);
|
||||
|
@ -987,9 +987,9 @@ public class QuestionnaireResponseValidatorDstu3Test {
|
|||
|
||||
when(myValSupport.isCodeSystemSupported(any(), eq("http://codesystems.com/system"))).thenReturn(true);
|
||||
when(myValSupport.isCodeSystemSupported(any(), eq("http://codesystems.com/system2"))).thenReturn(true);
|
||||
when(myValSupport.validateCode(any(), eq("http://codesystems.com/system"), eq("code0"), any()))
|
||||
when(myValSupport.validateCode(any(), eq("http://codesystems.com/system"), eq("code0"), any(), any()))
|
||||
.thenReturn(new CodeValidationResult(new ConceptDefinitionComponent().setCode("code0")));
|
||||
when(myValSupport.validateCode(any(), eq("http://codesystems.com/system"), eq("code1"), any()))
|
||||
when(myValSupport.validateCode(any(), eq("http://codesystems.com/system"), eq("code1"), any(), any()))
|
||||
.thenReturn(new CodeValidationResult(ValidationMessage.IssueSeverity.ERROR, "Unknown code"));
|
||||
|
||||
CodeSystem codeSystem = new CodeSystem();
|
||||
|
@ -1009,7 +1009,7 @@ public class QuestionnaireResponseValidatorDstu3Test {
|
|||
options.getCompose().addInclude().setSystem("http://codesystems.com/system2").addConcept().setCode("code2");
|
||||
when(myValSupport.fetchResource(any(FhirContext.class), eq(ValueSet.class), eq("http://somevalueset"))).thenReturn(options);
|
||||
|
||||
when(myValSupport.validateCode(any(FhirContext.class), eq("http://codesystems.com/system"), eq("code0"), any(String.class))).thenReturn(new CodeValidationResult(new ConceptDefinitionComponent(new CodeType("code0"))));
|
||||
when(myValSupport.validateCode(any(FhirContext.class), eq("http://codesystems.com/system"), eq("code0"), any(), any())).thenReturn(new CodeValidationResult(new ConceptDefinitionComponent(new CodeType("code0"))));
|
||||
|
||||
QuestionnaireResponse qa;
|
||||
ValidationResult errors;
|
||||
|
|
|
@ -40,7 +40,7 @@ public class QuestionnaireValidatorDstu3Test {
|
|||
myVal.setValidateAgainstStandardSchema(false);
|
||||
myVal.setValidateAgainstStandardSchematron(false);
|
||||
|
||||
ValidationSupportChain validationSupport = new ValidationSupportChain(myValSupport, myDefaultValidationSupport);
|
||||
ValidationSupportChain validationSupport = new ValidationSupportChain(myDefaultValidationSupport, myValSupport);
|
||||
myInstanceVal = new FhirInstanceValidator(validationSupport);
|
||||
|
||||
myVal.registerValidatorModule(myInstanceVal);
|
||||
|
|
|
@ -102,7 +102,7 @@ public class FhirInstanceValidatorR4Test {
|
|||
myVal.setValidateAgainstStandardSchematron(false);
|
||||
|
||||
myMockSupport = mock(IValidationSupport.class);
|
||||
CachingValidationSupport validationSupport = new CachingValidationSupport(new ValidationSupportChain(myMockSupport, myDefaultValidationSupport));
|
||||
CachingValidationSupport validationSupport = new CachingValidationSupport(new ValidationSupportChain(myDefaultValidationSupport, myMockSupport));
|
||||
myInstanceVal = new FhirInstanceValidator(validationSupport);
|
||||
|
||||
myVal.registerValidatorModule(myInstanceVal);
|
||||
|
@ -149,19 +149,21 @@ public class FhirInstanceValidatorR4Test {
|
|||
return retVal;
|
||||
}
|
||||
});
|
||||
when(myMockSupport.validateCode(nullable(FhirContext.class), nullable(String.class), nullable(String.class), nullable(String.class))).thenAnswer(new Answer<CodeValidationResult>() {
|
||||
when(myMockSupport.validateCode(nullable(FhirContext.class), nullable(String.class), nullable(String.class), nullable(String.class), nullable(String.class))).thenAnswer(new Answer<CodeValidationResult>() {
|
||||
@Override
|
||||
public CodeValidationResult answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
FhirContext ctx = (FhirContext) theInvocation.getArguments()[0];
|
||||
String system = (String) theInvocation.getArguments()[1];
|
||||
String code = (String) theInvocation.getArguments()[2];
|
||||
public CodeValidationResult answer(InvocationOnMock theInvocation) {
|
||||
FhirContext ctx = theInvocation.getArgument(0, FhirContext.class);
|
||||
String system = theInvocation.getArgument(1, String.class);
|
||||
String code = theInvocation.getArgument(2, String.class);
|
||||
String display = theInvocation.getArgument(3, String.class);
|
||||
String valueSetUrl = theInvocation.getArgument(4, String.class);
|
||||
CodeValidationResult retVal;
|
||||
if (myValidConcepts.contains(system + "___" + code)) {
|
||||
retVal = new CodeValidationResult(new ConceptDefinitionComponent(new CodeType(code)));
|
||||
} else {
|
||||
retVal = myDefaultValidationSupport.validateCode(ctx, system, code, (String) theInvocation.getArguments()[2]);
|
||||
retVal = myDefaultValidationSupport.validateCode(ctx, system, code, display, valueSetUrl);
|
||||
}
|
||||
ourLog.debug("validateCode({}, {}, {}) : {}", new Object[]{system, code, (String) theInvocation.getArguments()[2], retVal});
|
||||
ourLog.debug("validateCode({}, {}, {}, {}) : {}", system, code, display, valueSetUrl, retVal);
|
||||
return retVal;
|
||||
}
|
||||
});
|
||||
|
@ -580,7 +582,7 @@ public class FhirInstanceValidatorR4Test {
|
|||
public void testValidateProfileWithExtension() throws IOException, FHIRException {
|
||||
PrePopulatedValidationSupport valSupport = new PrePopulatedValidationSupport();
|
||||
DefaultProfileValidationSupport defaultSupport = new DefaultProfileValidationSupport();
|
||||
CachingValidationSupport support = new CachingValidationSupport(new ValidationSupportChain(valSupport, defaultSupport));
|
||||
CachingValidationSupport support = new CachingValidationSupport(new ValidationSupportChain(defaultSupport, valSupport));
|
||||
|
||||
// Prepopulate SDs
|
||||
valSupport.addStructureDefinition(loadStructureDefinition(defaultSupport, "/dstu3/myconsent-profile.xml"));
|
||||
|
|
|
@ -62,7 +62,7 @@ public class QuestionnaireResponseValidatorR4Test {
|
|||
myVal.setValidateAgainstStandardSchema(false);
|
||||
myVal.setValidateAgainstStandardSchematron(false);
|
||||
|
||||
ValidationSupportChain validationSupport = new ValidationSupportChain(myValSupport, myDefaultValidationSupport);
|
||||
ValidationSupportChain validationSupport = new ValidationSupportChain(myDefaultValidationSupport, myValSupport);
|
||||
myInstanceVal = new FhirInstanceValidator(validationSupport);
|
||||
|
||||
myVal.registerValidatorModule(myInstanceVal);
|
||||
|
@ -191,9 +191,9 @@ public class QuestionnaireResponseValidatorR4Test {
|
|||
|
||||
when(myValSupport.isCodeSystemSupported(any(), eq("http://codesystems.com/system"))).thenReturn(true);
|
||||
when(myValSupport.isCodeSystemSupported(any(), eq("http://codesystems.com/system2"))).thenReturn(true);
|
||||
when(myValSupport.validateCode(any(), eq("http://codesystems.com/system"), eq("code0"), any()))
|
||||
when(myValSupport.validateCode(any(), eq("http://codesystems.com/system"), eq("code0"), any(), any()))
|
||||
.thenReturn(new IValidationSupport.CodeValidationResult(new CodeSystem.ConceptDefinitionComponent().setCode("code0")));
|
||||
when(myValSupport.validateCode(any(), eq("http://codesystems.com/system"), eq("code1"), any()))
|
||||
when(myValSupport.validateCode(any(), eq("http://codesystems.com/system"), eq("code1"), any(),any()))
|
||||
.thenReturn(new IValidationSupport.CodeValidationResult(ValidationMessage.IssueSeverity.ERROR, "Unknown code"));
|
||||
|
||||
CodeSystem codeSystem = new CodeSystem();
|
||||
|
@ -248,7 +248,7 @@ public class QuestionnaireResponseValidatorR4Test {
|
|||
errors = myVal.validateWithResult(qa);
|
||||
errors = stripBindingHasNoSourceMessage(errors);
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("Validation failed for 'http://codesystems.com/system2#code3'"));
|
||||
assertThat(errors.toString(), containsString("Unknown code: http://codesystems.com/system2 / code3"));
|
||||
assertThat(errors.toString(), containsString("QuestionnaireResponse.item[0].answer[0]"));
|
||||
|
||||
}
|
||||
|
@ -345,7 +345,7 @@ public class QuestionnaireResponseValidatorR4Test {
|
|||
options.getCompose().addInclude().setSystem(codeSystemUrl).addConcept().setCode(codeValue);
|
||||
when(myValSupport.fetchResource(any(FhirContext.class), eq(ValueSet.class), eq(valueSetRef)))
|
||||
.thenReturn(options);
|
||||
when(myValSupport.validateCode(any(FhirContext.class), eq(codeSystemUrl), eq(codeValue), any(String.class)))
|
||||
when(myValSupport.validateCode(any(FhirContext.class), eq(codeSystemUrl), eq(codeValue), any(String.class), any()))
|
||||
.thenReturn(new CodeValidationResult(new ConceptDefinitionComponent(new CodeType(codeValue))));
|
||||
|
||||
IParser xmlParser = ourCtx.newXmlParser().setPrettyPrint(true);
|
||||
|
@ -402,7 +402,7 @@ public class QuestionnaireResponseValidatorR4Test {
|
|||
options.getCompose().addInclude().setSystem(codeSystemUrl).addConcept().setCode(codeValue);
|
||||
when(myValSupport.fetchResource(any(FhirContext.class), eq(ValueSet.class), eq(valueSetRef)))
|
||||
.thenReturn(options);
|
||||
when(myValSupport.validateCode(any(FhirContext.class), eq(codeSystemUrl), eq(codeValue), any(String.class)))
|
||||
when(myValSupport.validateCode(any(FhirContext.class), eq(codeSystemUrl), eq(codeValue), any(String.class), any()))
|
||||
.thenReturn(new CodeValidationResult(new ConceptDefinitionComponent(new CodeType(codeValue))));
|
||||
|
||||
IParser xmlParser = ourCtx.newXmlParser().setPrettyPrint(true);
|
||||
|
@ -522,7 +522,7 @@ public class QuestionnaireResponseValidatorR4Test {
|
|||
options.getCompose().addInclude().setSystem("http://codesystems.com/system2").addConcept().setCode("code2");
|
||||
when(myValSupport.fetchResource(any(FhirContext.class), eq(ValueSet.class), eq("http://somevalueset"))).thenReturn(options);
|
||||
|
||||
when(myValSupport.validateCode(any(FhirContext.class), eq("http://codesystems.com/system"), eq("code0"), any(String.class))).thenReturn(new CodeValidationResult(new ConceptDefinitionComponent(new CodeType("code0"))));
|
||||
when(myValSupport.validateCode(any(FhirContext.class), eq("http://codesystems.com/system"), eq("code0"), any(String.class), any())).thenReturn(new CodeValidationResult(new ConceptDefinitionComponent(new CodeType("code0"))));
|
||||
|
||||
QuestionnaireResponse qa;
|
||||
ValidationResult errors;
|
||||
|
|
|
@ -42,7 +42,7 @@ public class QuestionnaireValidatorR4Test {
|
|||
myVal.setValidateAgainstStandardSchema(false);
|
||||
myVal.setValidateAgainstStandardSchematron(false);
|
||||
|
||||
ValidationSupportChain validationSupport = new ValidationSupportChain(myValSupport, myDefaultValidationSupport);
|
||||
ValidationSupportChain validationSupport = new ValidationSupportChain(myDefaultValidationSupport, myValSupport);
|
||||
myInstanceVal = new FhirInstanceValidator(validationSupport);
|
||||
|
||||
myVal.registerValidatorModule(myInstanceVal);
|
||||
|
|
|
@ -139,19 +139,21 @@ public class FhirInstanceValidatorR5Test {
|
|||
return retVal;
|
||||
}
|
||||
});
|
||||
when(myMockSupport.validateCode(nullable(FhirContext.class), nullable(String.class), nullable(String.class), nullable(String.class))).thenAnswer(new Answer<CodeValidationResult>() {
|
||||
when(myMockSupport.validateCode(nullable(FhirContext.class), nullable(String.class), nullable(String.class), nullable(String.class), nullable(String.class))).thenAnswer(new Answer<CodeValidationResult>() {
|
||||
@Override
|
||||
public CodeValidationResult answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
FhirContext ctx = (FhirContext) theInvocation.getArguments()[0];
|
||||
String system = (String) theInvocation.getArguments()[1];
|
||||
String code = (String) theInvocation.getArguments()[2];
|
||||
public CodeValidationResult answer(InvocationOnMock theInvocation) {
|
||||
FhirContext ctx = theInvocation.getArgument(0, FhirContext.class);
|
||||
String system = theInvocation.getArgument(1, String.class);
|
||||
String code = theInvocation.getArgument(2, String.class);
|
||||
String display = theInvocation.getArgument(3, String.class);
|
||||
String valueSetUrl = theInvocation.getArgument(4, String.class);
|
||||
CodeValidationResult retVal;
|
||||
if (myValidConcepts.contains(system + "___" + code)) {
|
||||
retVal = new CodeValidationResult(new ConceptDefinitionComponent(new CodeType(code)));
|
||||
} else {
|
||||
retVal = myDefaultValidationSupport.validateCode(ctx, system, code, (String) theInvocation.getArguments()[2]);
|
||||
retVal = myDefaultValidationSupport.validateCode(ctx, system, code, display, valueSetUrl);
|
||||
}
|
||||
ourLog.debug("validateCode({}, {}, {}) : {}", new Object[]{system, code, (String) theInvocation.getArguments()[2], retVal});
|
||||
ourLog.debug("validateCode({}, {}, {}, {}) : {}", system, code, display, valueSetUrl, retVal);
|
||||
return retVal;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -62,7 +62,7 @@ public class QuestionnaireResponseValidatorR5Test {
|
|||
myVal.setValidateAgainstStandardSchema(false);
|
||||
myVal.setValidateAgainstStandardSchematron(false);
|
||||
|
||||
ValidationSupportChain validationSupport = new ValidationSupportChain(myValSupport, myDefaultValidationSupport);
|
||||
ValidationSupportChain validationSupport = new ValidationSupportChain(myDefaultValidationSupport, myValSupport);
|
||||
myInstanceVal = new FhirInstanceValidator(validationSupport);
|
||||
|
||||
myVal.registerValidatorModule(myInstanceVal);
|
||||
|
@ -191,9 +191,9 @@ public class QuestionnaireResponseValidatorR5Test {
|
|||
|
||||
when(myValSupport.isCodeSystemSupported(any(), eq("http://codesystems.com/system"))).thenReturn(true);
|
||||
when(myValSupport.isCodeSystemSupported(any(), eq("http://codesystems.com/system2"))).thenReturn(true);
|
||||
when(myValSupport.validateCode(any(), eq("http://codesystems.com/system"), eq("code0"), any()))
|
||||
when(myValSupport.validateCode(any(), eq("http://codesystems.com/system"), eq("code0"), any(), any()))
|
||||
.thenReturn(new CodeValidationResult(new ConceptDefinitionComponent().setCode("code0")));
|
||||
when(myValSupport.validateCode(any(), eq("http://codesystems.com/system"), eq("code1"), any()))
|
||||
when(myValSupport.validateCode(any(), eq("http://codesystems.com/system"), eq("code1"), any(), any()))
|
||||
.thenReturn(new CodeValidationResult(ValidationMessage.IssueSeverity.ERROR, "Unknown code"));
|
||||
|
||||
CodeSystem codeSystem = new CodeSystem();
|
||||
|
@ -248,7 +248,7 @@ public class QuestionnaireResponseValidatorR5Test {
|
|||
errors = myVal.validateWithResult(qa);
|
||||
errors = stripBindingHasNoSourceMessage(errors);
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("Validation failed for 'http://codesystems.com/system2#code3'"));
|
||||
assertThat(errors.toString(), containsString("Unknown code: http://codesystems.com/system2 / code3"));
|
||||
assertThat(errors.toString(), containsString("QuestionnaireResponse.item[0].answer[0]"));
|
||||
|
||||
}
|
||||
|
@ -345,7 +345,7 @@ public class QuestionnaireResponseValidatorR5Test {
|
|||
options.getCompose().addInclude().setSystem(codeSystemUrl).addConcept().setCode(codeValue);
|
||||
when(myValSupport.fetchResource(any(FhirContext.class), eq(ValueSet.class), eq(valueSetRef)))
|
||||
.thenReturn(options);
|
||||
when(myValSupport.validateCode(any(FhirContext.class), eq(codeSystemUrl), eq(codeValue), any(String.class)))
|
||||
when(myValSupport.validateCode(any(FhirContext.class), eq(codeSystemUrl), eq(codeValue), any(String.class), any()))
|
||||
.thenReturn(new CodeValidationResult(new ConceptDefinitionComponent(new CodeType(codeValue))));
|
||||
|
||||
IParser xmlParser = ourCtx.newXmlParser().setPrettyPrint(true);
|
||||
|
@ -402,7 +402,7 @@ public class QuestionnaireResponseValidatorR5Test {
|
|||
options.getCompose().addInclude().setSystem(codeSystemUrl).addConcept().setCode(codeValue);
|
||||
when(myValSupport.fetchResource(any(FhirContext.class), eq(ValueSet.class), eq(valueSetRef)))
|
||||
.thenReturn(options);
|
||||
when(myValSupport.validateCode(any(FhirContext.class), eq(codeSystemUrl), eq(codeValue), any(String.class)))
|
||||
when(myValSupport.validateCode(any(FhirContext.class), eq(codeSystemUrl), eq(codeValue), any(String.class), any()))
|
||||
.thenReturn(new CodeValidationResult(new ConceptDefinitionComponent(new CodeType(codeValue))));
|
||||
|
||||
IParser xmlParser = ourCtx.newXmlParser().setPrettyPrint(true);
|
||||
|
@ -522,7 +522,7 @@ public class QuestionnaireResponseValidatorR5Test {
|
|||
options.getCompose().addInclude().setSystem("http://codesystems.com/system2").addConcept().setCode("code2");
|
||||
when(myValSupport.fetchResource(any(FhirContext.class), eq(ValueSet.class), eq("http://somevalueset"))).thenReturn(options);
|
||||
|
||||
when(myValSupport.validateCode(any(FhirContext.class), eq("http://codesystems.com/system"), eq("code0"), any(String.class))).thenReturn(new CodeValidationResult(new ConceptDefinitionComponent(new CodeType("code0"))));
|
||||
when(myValSupport.validateCode(any(FhirContext.class), eq("http://codesystems.com/system"), eq("code0"), any(String.class), any())).thenReturn(new CodeValidationResult(new ConceptDefinitionComponent(new CodeType("code0"))));
|
||||
|
||||
QuestionnaireResponse qa;
|
||||
ValidationResult errors;
|
||||
|
|
|
@ -51,12 +51,31 @@
|
|||
to Elastic in the future.
|
||||
]]>
|
||||
</action>
|
||||
<action type="fix">
|
||||
<![CDATA[
|
||||
<b>New Feature</b>:
|
||||
A new set of operations have been added to the JPA server that allow CodeSystem deltas to be
|
||||
uploaded. A CodeSystem Delta consists of a set of codes and relationships that are added or
|
||||
removed incrementally to the live CodeSystem without requiring a downtime or a complete
|
||||
upload of the contents. In addition, the HAPI FHIR CLI
|
||||
<code>upload-terminology</code> command has been modified to support this new functionality.
|
||||
]]>
|
||||
</action>
|
||||
<action type="add" issue="1489">
|
||||
<![CDATA[
|
||||
<b>Improvement</b>:
|
||||
<b>Performance Improvement</b>:
|
||||
A significant performance improvement was made to the parsers (particularly the Json Parser)
|
||||
when serializing resources. This work yields improvements of 20-50% in raw encode speed when
|
||||
encoding large resources. Thanks to David Maplesden for the pull request!
|
||||
]]>
|
||||
</action>
|
||||
<action type="add" issue="1489">
|
||||
<![CDATA[
|
||||
<b>Performance Improvement</b>:
|
||||
When running inside a JPA server, The DSTU3+ validator now performs code validations
|
||||
by directly testing ValueSet membership against a pre-calculated copy of the ValueSet,
|
||||
instead of first expanding the ValueSet and then examining the expanded contents.
|
||||
This can yield a significant improvement in validation speed in many cases.
|
||||
]]>
|
||||
</action>
|
||||
<action type="add" issue="1357">
|
||||
|
@ -315,9 +334,6 @@
|
|||
that was too short to hold the longest name from the final R4 definitions. This has been
|
||||
corrected to account for names up to 40 characters long.
|
||||
</action>
|
||||
<action type="fix">
|
||||
A new command has been added to the HAPI FHIR CLI that allows external (not-present) codesystem deltas to be manually uploaded
|
||||
</action>
|
||||
<action type="fix">
|
||||
The subscription triggering operation was not able to handle commas within search URLs being
|
||||
used to trigger resources for subscription checking. This has been corrected.
|
||||
|
@ -359,6 +375,14 @@
|
|||
as search requests with no search parameters, leading to confusing search results. These will now
|
||||
result in an HTTP 400 error with a meaningful error message.
|
||||
</action>
|
||||
<action type="change">
|
||||
The
|
||||
<![CDATA[<code>IValidationSupport#validateCode(...)</code>]]>
|
||||
method has been modified to add an additional parameter (String theValueSetUrl).
|
||||
Most users will be unaffected by this change as HAPI FHIR provides a number of
|
||||
built-in implementations of this interface, but any direct user implementations
|
||||
of this interface will need to add the new parameter.
|
||||
</action>
|
||||
<action type="add" issue="1541">
|
||||
The server CapabilityStatement (/metadata) endpoint now respects the Cache-Control header. Thanks
|
||||
to Jens Villadsen for the pull request!
|
||||
|
|
Loading…
Reference in New Issue