Add UCUM support (#1824)
* Add UCUM support * Add changelog * Some cleanup * Test fix * Add flywayDB callback * Add hooks to schema migrator
This commit is contained in:
parent
f94f2fde65
commit
3d5a8bb3f8
|
@ -0,0 +1,106 @@
|
|||
package ca.uhn.fhir.util;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import com.google.common.base.Charsets;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.io.input.BOMInputStream;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.function.Function;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
/**
|
||||
* Use this API with caution, it may change!
|
||||
*/
|
||||
public class ClasspathUtil {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(ClasspathUtil.class);
|
||||
|
||||
public static String loadResource(String theClasspath) {
|
||||
Function<InputStream, InputStream> streamTransform = t -> t;
|
||||
return loadResource(theClasspath, streamTransform);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a classpath resource, throw an {@link InternalErrorException} if not found
|
||||
*/
|
||||
@Nonnull
|
||||
public static InputStream loadResourceAsStream(String theClasspath) {
|
||||
InputStream retVal = ClasspathUtil.class.getResourceAsStream(theClasspath);
|
||||
if (retVal == null) {
|
||||
throw new InternalErrorException("Unable to find classpath resource: " + theClasspath);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a classpath resource, throw an {@link InternalErrorException} if not found
|
||||
*/
|
||||
@Nonnull
|
||||
public static String loadResource(String theClasspath, Function<InputStream, InputStream> theStreamTransform) {
|
||||
InputStream stream = ClasspathUtil.class.getResourceAsStream(theClasspath);
|
||||
try {
|
||||
if (stream == null) {
|
||||
throw new IOException("Unable to find classpath resource: " + theClasspath);
|
||||
}
|
||||
try {
|
||||
InputStream newStream = theStreamTransform.apply(stream);
|
||||
return IOUtils.toString(newStream, Charsets.UTF_8);
|
||||
} finally {
|
||||
stream.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static String loadCompressedResource(String theClasspath) {
|
||||
Function<InputStream, InputStream> streamTransform = t -> {
|
||||
try {
|
||||
return new GZIPInputStream(t);
|
||||
} catch (IOException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
};
|
||||
return loadResource(theClasspath, streamTransform);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static <T extends IBaseResource> T loadResource(FhirContext theCtx, Class<T> theType, String theClasspath) {
|
||||
String raw = loadResource(theClasspath);
|
||||
return EncodingEnum.detectEncodingNoDefault(raw).newParser(theCtx).parseResource(theType, raw);
|
||||
}
|
||||
|
||||
public static void close(InputStream theInput) {
|
||||
try {
|
||||
if (theInput != null) {
|
||||
theInput.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
ourLog.debug("Closing InputStream threw exception", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static Function<InputStream, InputStream> withBom() {
|
||||
return t -> new BOMInputStream(t);
|
||||
}
|
||||
|
||||
public static byte[] loadResourceAsByteArray(String theClasspath) {
|
||||
InputStream stream = loadResourceAsStream(theClasspath);
|
||||
try {
|
||||
return IOUtils.toByteArray(stream);
|
||||
} catch (IOException e) {
|
||||
throw new InternalErrorException(e);
|
||||
} finally {
|
||||
close(stream);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -23,9 +23,7 @@ package ca.uhn.fhir.validation;
|
|||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.io.input.BOMInputStream;
|
||||
import ca.uhn.fhir.util.ClasspathUtil;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.w3c.dom.ls.LSInput;
|
||||
import org.w3c.dom.ls.LSResourceResolver;
|
||||
|
@ -41,10 +39,7 @@ import javax.xml.validation.SchemaFactory;
|
|||
import javax.xml.validation.Validator;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.StringReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
@ -152,20 +147,9 @@ public class SchemaBaseValidator implements IValidatorModule {
|
|||
Source loadXml(String theSchemaName) {
|
||||
String pathToBase = myCtx.getVersion().getPathToSchemaDefinitions() + '/' + theSchemaName;
|
||||
ourLog.debug("Going to load resource: {}", pathToBase);
|
||||
try (InputStream baseIs = FhirValidator.class.getResourceAsStream(pathToBase)) {
|
||||
if (baseIs == null) {
|
||||
throw new InternalErrorException("Schema not found. " + RESOURCES_JAR_NOTE);
|
||||
}
|
||||
try (BOMInputStream bomInputStream = new BOMInputStream(baseIs, false)) {
|
||||
try (InputStreamReader baseReader = new InputStreamReader(bomInputStream, StandardCharsets.UTF_8)) {
|
||||
// Buffer so that we can close the input stream
|
||||
String contents = IOUtils.toString(baseReader);
|
||||
return new StreamSource(new StringReader(contents), null);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
|
||||
String contents = ClasspathUtil.loadResource(pathToBase, ClasspathUtil.withBom());
|
||||
return new StreamSource(new StringReader(contents), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -188,16 +172,8 @@ public class SchemaBaseValidator implements IValidatorModule {
|
|||
|
||||
ourLog.debug("Loading referenced schema file: " + pathToBase);
|
||||
|
||||
try (InputStream baseIs = FhirValidator.class.getResourceAsStream(pathToBase)) {
|
||||
if (baseIs == null) {
|
||||
throw new InternalErrorException("Schema file not found: " + pathToBase);
|
||||
}
|
||||
|
||||
byte[] bytes = IOUtils.toByteArray(baseIs);
|
||||
input.setByteStream(new ByteArrayInputStream(bytes));
|
||||
} catch (IOException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
byte[] bytes = ClasspathUtil.loadResourceAsByteArray(pathToBase);
|
||||
input.setByteStream(new ByteArrayInputStream(bytes));
|
||||
return input;
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
package ca.uhn.fhir.util;
|
||||
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
public class ClasspathUtilTest {
|
||||
|
||||
@Test
|
||||
public void testLoadResourceNotFound() {
|
||||
try {
|
||||
ClasspathUtil.loadResource("/FOOOOOO");
|
||||
} catch (InternalErrorException e) {
|
||||
assertEquals("Unable to find classpath resource: /FOOOOOO", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadResourceAsStreamNotFound() {
|
||||
try {
|
||||
ClasspathUtil.loadResourceAsStream("/FOOOOOO");
|
||||
} catch (InternalErrorException e) {
|
||||
assertEquals("Unable to find classpath resource: /FOOOOOO", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Should not throw any exception
|
||||
*/
|
||||
@Test
|
||||
public void testClose_Null() {
|
||||
ClasspathUtil.close(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Should not throw any exception
|
||||
*/
|
||||
@Test
|
||||
public void testClose_Ok() {
|
||||
ClasspathUtil.close(new ByteArrayInputStream(new byte[]{0,1,2}));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Should not throw any exception
|
||||
*/
|
||||
@Test
|
||||
public void testClose_ThrowException() throws IOException {
|
||||
InputStream is = mock(InputStream.class);
|
||||
doThrow(new IOException("FOO")).when(is).close();
|
||||
ClasspathUtil.close(is);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
type: add
|
||||
issue: 1824
|
||||
title: Native support for UCUM has been added to the validation stack, meaning that UCUM codes can be validated
|
||||
at runtime without the need for any external validation.
|
|
@ -9,6 +9,7 @@
|
|||
<li>Hibernate Validator (JPA): 5.4.2.Final -> 6.1.3.Final</li>
|
||||
<li>Guava (JPA): 28.0 -> 28.2</li>
|
||||
<li>Spring Boot (Boot): 2.2.0.RELEASE -> 2.2.6.RELEASE</li>
|
||||
<li>FlywayDB (JPA) 6.1.0 -> 6.4.1</li>
|
||||
</ul>"
|
||||
- item:
|
||||
issue: "1583"
|
||||
|
|
|
@ -98,6 +98,17 @@ The following table lists vocabulary that is validated by this module:
|
|||
added in the future, please get in touch if you would like to help.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Unified Codes for Units of Measure (UCUM)</td>
|
||||
<td>
|
||||
ValueSet: <code><a href="http://hl7.org/fhir/ValueSet/ucum-units">(...)/ValueSet/ucum-units</a></code>
|
||||
<br/>
|
||||
CodeSystem: <code>http://unitsofmeasure.org</code>
|
||||
</td>
|
||||
<td>
|
||||
Codes are validated using the UcumEssenceService provided by the <a href="https://github.com/FHIR/Ucum-java">UCUM Java</a> library.
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
|
|
@ -21,9 +21,13 @@ package ca.uhn.fhir.jpa.migrate;
|
|||
*/
|
||||
|
||||
import ca.uhn.fhir.jpa.migrate.taskdef.BaseTask;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.flywaydb.core.api.callback.Callback;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.sql.DataSource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
|
@ -34,6 +38,17 @@ public abstract class BaseMigrator implements IMigrator {
|
|||
private DriverTypeEnum myDriverType;
|
||||
private DataSource myDataSource;
|
||||
private List<BaseTask.ExecutedStatement> myExecutedStatements = new ArrayList<>();
|
||||
private List<Callback> myCallbacks = Collections.emptyList();
|
||||
|
||||
@Nonnull
|
||||
public List<Callback> getCallbacks() {
|
||||
return myCallbacks;
|
||||
}
|
||||
|
||||
public void setCallbacks(@Nonnull List<Callback> theCallbacks) {
|
||||
Validate.notNull(theCallbacks);
|
||||
myCallbacks = theCallbacks;
|
||||
}
|
||||
|
||||
public DataSource getDataSource() {
|
||||
return myDataSource;
|
||||
|
|
|
@ -25,6 +25,7 @@ import ca.uhn.fhir.jpa.migrate.taskdef.InitializeSchemaTask;
|
|||
import com.google.common.annotations.VisibleForTesting;
|
||||
import org.flywaydb.core.Flyway;
|
||||
import org.flywaydb.core.api.MigrationInfoService;
|
||||
import org.flywaydb.core.api.callback.Callback;
|
||||
import org.flywaydb.core.api.migration.JavaMigration;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -79,6 +80,7 @@ public class FlywayMigrator extends BaseMigrator {
|
|||
.baselineOnMigrate(true)
|
||||
.outOfOrder(isOutOfOrderPermitted())
|
||||
.javaMigrations(myTasks.toArray(new JavaMigration[0]))
|
||||
.callbacks(getCallbacks().toArray(new Callback[0]))
|
||||
.load();
|
||||
for (FlywayMigration task : myTasks) {
|
||||
task.setConnectionProperties(theConnectionProperties);
|
||||
|
|
|
@ -132,4 +132,6 @@ public class Migrator {
|
|||
public void setNoColumnShrink(boolean theNoColumnShrink) {
|
||||
myNoColumnShrink = theNoColumnShrink;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -24,20 +24,23 @@ import ca.uhn.fhir.context.ConfigurationException;
|
|||
import ca.uhn.fhir.jpa.migrate.taskdef.BaseTask;
|
||||
import org.flywaydb.core.api.MigrationInfo;
|
||||
import org.flywaydb.core.api.MigrationInfoService;
|
||||
import org.flywaydb.core.api.callback.Callback;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
|
||||
public class SchemaMigrator {
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(SchemaMigrator.class);
|
||||
public static final String HAPI_FHIR_MIGRATION_TABLENAME = "FLY_HFJ_MIGRATION";
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(SchemaMigrator.class);
|
||||
private final DataSource myDataSource;
|
||||
private final boolean mySkipValidation;
|
||||
private final String myMigrationTableName;
|
||||
|
@ -45,6 +48,7 @@ public class SchemaMigrator {
|
|||
private boolean myDontUseFlyway;
|
||||
private boolean myOutOfOrderPermitted;
|
||||
private DriverTypeEnum myDriverType;
|
||||
private List<Callback> myCallbacks = Collections.emptyList();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -61,6 +65,11 @@ public class SchemaMigrator {
|
|||
}
|
||||
}
|
||||
|
||||
public void setCallbacks(List<Callback> theCallbacks) {
|
||||
Assert.notNull(theCallbacks);
|
||||
myCallbacks = theCallbacks;
|
||||
}
|
||||
|
||||
public void setDontUseFlyway(boolean theDontUseFlyway) {
|
||||
myDontUseFlyway = theDontUseFlyway;
|
||||
}
|
||||
|
@ -110,6 +119,7 @@ public class SchemaMigrator {
|
|||
migrator.setOutOfOrderPermitted(myOutOfOrderPermitted);
|
||||
}
|
||||
migrator.addTasks(myMigrationTasks);
|
||||
migrator.setCallbacks(myCallbacks);
|
||||
return migrator;
|
||||
}
|
||||
|
||||
|
|
|
@ -34,13 +34,14 @@ import java.util.Set;
|
|||
|
||||
public class InitializeSchemaTask extends BaseTask {
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(InitializeSchemaTask.class);
|
||||
public static final String DESCRIPTION_PREFIX = "Initialize schema for ";
|
||||
|
||||
private final ISchemaInitializationProvider mySchemaInitializationProvider;
|
||||
|
||||
public InitializeSchemaTask(String theProductVersion, String theSchemaVersion, ISchemaInitializationProvider theSchemaInitializationProvider) {
|
||||
super(theProductVersion, theSchemaVersion);
|
||||
mySchemaInitializationProvider = theSchemaInitializationProvider;
|
||||
setDescription("Initialize schema for " + mySchemaInitializationProvider.getSchemaDescription());
|
||||
setDescription(DESCRIPTION_PREFIX + mySchemaInitializationProvider.getSchemaDescription());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -21,18 +21,12 @@ package ca.uhn.fhir.test;
|
|||
*/
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import com.google.common.base.Charsets;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import ca.uhn.fhir.util.ClasspathUtil;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.function.Function;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
public class BaseTest {
|
||||
|
||||
|
@ -41,35 +35,14 @@ public class BaseTest {
|
|||
}
|
||||
|
||||
protected String loadResource(String theClasspath) throws IOException {
|
||||
Function<InputStream, InputStream> streamTransform = t->t;
|
||||
return loadResource(theClasspath, streamTransform);
|
||||
}
|
||||
|
||||
private String loadResource(String theClasspath, Function<InputStream, InputStream> theStreamTransform) throws IOException {
|
||||
try (InputStream stream = BaseTest.class.getResourceAsStream(theClasspath)) {
|
||||
if (stream == null) {
|
||||
throw new IllegalArgumentException("Unable to find resource: " + theClasspath);
|
||||
}
|
||||
|
||||
InputStream newStream = theStreamTransform.apply(stream);
|
||||
|
||||
return IOUtils.toString(newStream, Charsets.UTF_8);
|
||||
}
|
||||
return ClasspathUtil.loadResource(theClasspath);
|
||||
}
|
||||
|
||||
protected String loadCompressedResource(String theClasspath) throws IOException {
|
||||
Function<InputStream, InputStream> streamTransform = t-> {
|
||||
try {
|
||||
return new GZIPInputStream(t);
|
||||
} catch (IOException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
};
|
||||
return loadResource(theClasspath, streamTransform);
|
||||
return ClasspathUtil.loadCompressedResource(theClasspath);
|
||||
}
|
||||
|
||||
protected <T extends IBaseResource> T loadResource(FhirContext theCtx, Class<T> theType, String theClasspath) throws IOException {
|
||||
String raw = loadResource(theClasspath);
|
||||
return EncodingEnum.detectEncodingNoDefault(raw).newParser(theCtx).parseResource(theType, raw);
|
||||
return ClasspathUtil.loadResource(theCtx, theType, theClasspath);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,22 @@
|
|||
package org.hl7.fhir.common.hapi.validation.support;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.context.support.ConceptValidationOptions;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.util.ClasspathUtil;
|
||||
import ca.uhn.fhir.util.FileUtil;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.fhir.ucum.UcumEssenceService;
|
||||
import org.fhir.ucum.UcumException;
|
||||
import org.hl7.fhir.dstu2.model.ValueSet;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
@ -26,12 +35,13 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport {
|
|||
public static final String MIMETYPES_VALUESET_URL = "http://hl7.org/fhir/ValueSet/mimetypes";
|
||||
public static final String CURRENCIES_CODESYSTEM_URL = "urn:iso:std:iso:4217";
|
||||
public static final String CURRENCIES_VALUESET_URL = "http://hl7.org/fhir/ValueSet/currencies";
|
||||
public static final String UCUM_CODESYSTEM_URL = "http://unitsofmeasure.org";
|
||||
private static final String USPS_CODESYSTEM_URL = "https://www.usps.com/";
|
||||
private static final String USPS_VALUESET_URL = "http://hl7.org/fhir/us/core/ValueSet/us-core-usps-state";
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(CommonCodeSystemsTerminologyService.class);
|
||||
public static final String UCUM_VALUESET_URL = "http://hl7.org/fhir/ValueSet/ucum-units";
|
||||
private static Map<String, String> USPS_CODES = Collections.unmodifiableMap(buildUspsCodes());
|
||||
private static Map<String, String> ISO_4217_CODES = Collections.unmodifiableMap(buildIso4217Codes());
|
||||
|
||||
|
||||
private final FhirContext myFhirContext;
|
||||
|
||||
/**
|
||||
|
@ -71,8 +81,22 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport {
|
|||
return new CodeValidationResult()
|
||||
.setCode(theCode)
|
||||
.setDisplay(theDisplay);
|
||||
}
|
||||
|
||||
case UCUM_VALUESET_URL: {
|
||||
String system = theCodeSystem;
|
||||
if (system == null && theOptions.isInferSystem()) {
|
||||
system = UCUM_CODESYSTEM_URL;
|
||||
}
|
||||
LookupCodeResult lookupResult = lookupCode(theRootValidationSupport, system, theCode);
|
||||
if (lookupResult != null) {
|
||||
if (lookupResult.isFound()) {
|
||||
return new CodeValidationResult()
|
||||
.setCode(lookupResult.getSearchedForCode())
|
||||
.setDisplay(lookupResult.getCodeDisplay());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (handlerMap != null) {
|
||||
String display = handlerMap.get(theCode);
|
||||
|
@ -92,6 +116,49 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LookupCodeResult lookupCode(IValidationSupport theRootValidationSupport, String theSystem, String theCode) {
|
||||
|
||||
if (UCUM_CODESYSTEM_URL.equals(theSystem) && theRootValidationSupport.getFhirContext().getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.DSTU3)) {
|
||||
|
||||
InputStream input = ClasspathUtil.loadResourceAsStream("/ucum-essence.xml");
|
||||
try {
|
||||
UcumEssenceService svc = new UcumEssenceService(input);
|
||||
String outcome = svc.analyse(theCode);
|
||||
if (outcome != null) {
|
||||
|
||||
LookupCodeResult retVal = new LookupCodeResult();
|
||||
retVal.setSearchedForCode(theCode);
|
||||
retVal.setSearchedForSystem(theSystem);
|
||||
retVal.setFound(true);
|
||||
retVal.setCodeDisplay(outcome);
|
||||
return retVal;
|
||||
|
||||
}
|
||||
} catch (UcumException e) {
|
||||
ourLog.debug("Failed parse UCUM code: {}", theCode, e);
|
||||
return null;
|
||||
} finally {
|
||||
ClasspathUtil.close(input);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCodeSystemSupported(IValidationSupport theRootValidationSupport, String theSystem) {
|
||||
|
||||
switch (theSystem) {
|
||||
case UCUM_CODESYSTEM_URL:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public String getValueSetUrl(@Nonnull IBaseResource theValueSet) {
|
||||
String url;
|
||||
switch (getFhirContext().getVersion().getVersion()) {
|
||||
|
|
|
@ -22,6 +22,7 @@ import org.hl7.fhir.utilities.validation.ValidationMessage;
|
|||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
@ -53,7 +54,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
|||
@Override
|
||||
public ValueSetExpansionOutcome expandValueSet(IValidationSupport theRootValidationSupport, ValueSetExpansionOptions theExpansionOptions, IBaseResource theValueSetToExpand) {
|
||||
|
||||
org.hl7.fhir.r5.model.ValueSet expansionR5 = expandValueSetToCanonical(theRootValidationSupport, theValueSetToExpand);
|
||||
org.hl7.fhir.r5.model.ValueSet expansionR5 = expandValueSetToCanonical(theRootValidationSupport, theValueSetToExpand, null, null);
|
||||
if (expansionR5 == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -85,20 +86,20 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
|||
return new ValueSetExpansionOutcome(expansion, null);
|
||||
}
|
||||
|
||||
private org.hl7.fhir.r5.model.ValueSet expandValueSetToCanonical(IValidationSupport theRootValidationSupport, IBaseResource theValueSetToExpand) {
|
||||
private org.hl7.fhir.r5.model.ValueSet expandValueSetToCanonical(IValidationSupport theRootValidationSupport, IBaseResource theValueSetToExpand, @Nullable String theWantSystem, @Nullable String theWantCode) {
|
||||
org.hl7.fhir.r5.model.ValueSet expansionR5;
|
||||
switch (myCtx.getVersion().getVersion()) {
|
||||
case DSTU2:
|
||||
case DSTU2_HL7ORG: {
|
||||
expansionR5 = expandValueSetDstu2Hl7Org(theRootValidationSupport, (ValueSet) theValueSetToExpand);
|
||||
expansionR5 = expandValueSetDstu2Hl7Org(theRootValidationSupport, (ValueSet) theValueSetToExpand, theWantSystem, theWantCode);
|
||||
break;
|
||||
}
|
||||
case DSTU3: {
|
||||
expansionR5 = expandValueSetDstu3(theRootValidationSupport, (org.hl7.fhir.dstu3.model.ValueSet) theValueSetToExpand);
|
||||
expansionR5 = expandValueSetDstu3(theRootValidationSupport, (org.hl7.fhir.dstu3.model.ValueSet) theValueSetToExpand, theWantSystem, theWantCode);
|
||||
break;
|
||||
}
|
||||
case R4: {
|
||||
expansionR5 = expandValueSetR4(theRootValidationSupport, (org.hl7.fhir.r4.model.ValueSet) theValueSetToExpand);
|
||||
expansionR5 = expandValueSetR4(theRootValidationSupport, (org.hl7.fhir.r4.model.ValueSet) theValueSetToExpand, theWantSystem, theWantCode);
|
||||
break;
|
||||
}
|
||||
case R5: {
|
||||
|
@ -118,7 +119,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
|||
|
||||
@Override
|
||||
public CodeValidationResult validateCodeInValueSet(IValidationSupport theRootValidationSupport, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) {
|
||||
org.hl7.fhir.r5.model.ValueSet expansion = expandValueSetToCanonical(theRootValidationSupport, theValueSet);
|
||||
org.hl7.fhir.r5.model.ValueSet expansion = expandValueSetToCanonical(theRootValidationSupport, theValueSet, theCodeSystem, theCode);
|
||||
if (expansion == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -287,7 +288,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
|||
}
|
||||
|
||||
@Nullable
|
||||
private org.hl7.fhir.r5.model.ValueSet expandValueSetDstu2Hl7Org(IValidationSupport theRootValidationSupport, ValueSet theInput) {
|
||||
private org.hl7.fhir.r5.model.ValueSet expandValueSetDstu2Hl7Org(IValidationSupport theRootValidationSupport, ValueSet theInput, @Nullable String theWantSystem, @Nullable String theWantCode) {
|
||||
Function<String, CodeSystem> codeSystemLoader = t -> {
|
||||
org.hl7.fhir.dstu2.model.ValueSet codeSystem = (org.hl7.fhir.dstu2.model.ValueSet) theRootValidationSupport.fetchCodeSystem(t);
|
||||
CodeSystem retVal = new CodeSystem();
|
||||
|
@ -300,7 +301,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
|||
};
|
||||
|
||||
org.hl7.fhir.r5.model.ValueSet input = ValueSet10_50.convertValueSet(theInput);
|
||||
org.hl7.fhir.r5.model.ValueSet output = expandValueSetR5(input, codeSystemLoader, valueSetLoader);
|
||||
org.hl7.fhir.r5.model.ValueSet output = expandValueSetR5(theRootValidationSupport, input, codeSystemLoader, valueSetLoader, theWantSystem, theWantCode);
|
||||
return (output);
|
||||
}
|
||||
|
||||
|
@ -342,7 +343,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
|||
}
|
||||
|
||||
@Nullable
|
||||
private org.hl7.fhir.r5.model.ValueSet expandValueSetDstu3(IValidationSupport theRootValidationSupport, org.hl7.fhir.dstu3.model.ValueSet theInput) {
|
||||
private org.hl7.fhir.r5.model.ValueSet expandValueSetDstu3(IValidationSupport theRootValidationSupport, org.hl7.fhir.dstu3.model.ValueSet theInput, @Nullable String theWantSystem, @Nullable String theWantCode) {
|
||||
Function<String, org.hl7.fhir.r5.model.CodeSystem> codeSystemLoader = t -> {
|
||||
org.hl7.fhir.dstu3.model.CodeSystem codeSystem = (org.hl7.fhir.dstu3.model.CodeSystem) theRootValidationSupport.fetchCodeSystem(t);
|
||||
return CodeSystem30_50.convertCodeSystem(codeSystem);
|
||||
|
@ -353,12 +354,12 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
|||
};
|
||||
|
||||
org.hl7.fhir.r5.model.ValueSet input = ValueSet30_50.convertValueSet(theInput);
|
||||
org.hl7.fhir.r5.model.ValueSet output = expandValueSetR5(input, codeSystemLoader, valueSetLoader);
|
||||
org.hl7.fhir.r5.model.ValueSet output = expandValueSetR5(theRootValidationSupport, input, codeSystemLoader, valueSetLoader, theWantSystem, theWantCode);
|
||||
return (output);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private org.hl7.fhir.r5.model.ValueSet expandValueSetR4(IValidationSupport theRootValidationSupport, org.hl7.fhir.r4.model.ValueSet theInput) {
|
||||
private org.hl7.fhir.r5.model.ValueSet expandValueSetR4(IValidationSupport theRootValidationSupport, org.hl7.fhir.r4.model.ValueSet theInput, @Nullable String theWantSystem, @Nullable String theWantCode) {
|
||||
Function<String, org.hl7.fhir.r5.model.CodeSystem> codeSystemLoader = t -> {
|
||||
org.hl7.fhir.r4.model.CodeSystem codeSystem = (org.hl7.fhir.r4.model.CodeSystem) theRootValidationSupport.fetchCodeSystem(t);
|
||||
return CodeSystem40_50.convertCodeSystem(codeSystem);
|
||||
|
@ -369,7 +370,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
|||
};
|
||||
|
||||
org.hl7.fhir.r5.model.ValueSet input = ValueSet40_50.convertValueSet(theInput);
|
||||
org.hl7.fhir.r5.model.ValueSet output = expandValueSetR5(input, codeSystemLoader, valueSetLoader);
|
||||
org.hl7.fhir.r5.model.ValueSet output = expandValueSetR5(theRootValidationSupport, input, codeSystemLoader, valueSetLoader, theWantSystem, theWantCode);
|
||||
return (output);
|
||||
}
|
||||
|
||||
|
@ -378,16 +379,16 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
|||
Function<String, org.hl7.fhir.r5.model.CodeSystem> codeSystemLoader = t -> (org.hl7.fhir.r5.model.CodeSystem) theRootValidationSupport.fetchCodeSystem(t);
|
||||
Function<String, org.hl7.fhir.r5.model.ValueSet> valueSetLoader = t -> (org.hl7.fhir.r5.model.ValueSet) theRootValidationSupport.fetchValueSet(t);
|
||||
|
||||
return expandValueSetR5(theInput, codeSystemLoader, valueSetLoader);
|
||||
return expandValueSetR5(theRootValidationSupport, theInput, codeSystemLoader, valueSetLoader, null, null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private org.hl7.fhir.r5.model.ValueSet expandValueSetR5(org.hl7.fhir.r5.model.ValueSet theInput, Function<String, CodeSystem> theCodeSystemLoader, Function<String, org.hl7.fhir.r5.model.ValueSet> theValueSetLoader) {
|
||||
private org.hl7.fhir.r5.model.ValueSet expandValueSetR5(IValidationSupport theRootValidationSupport, org.hl7.fhir.r5.model.ValueSet theInput, Function<String, CodeSystem> theCodeSystemLoader, Function<String, org.hl7.fhir.r5.model.ValueSet> theValueSetLoader, @Nullable String theWantSystem, @Nullable String theWantCode) {
|
||||
Set<VersionIndependentConcept> concepts = new HashSet<>();
|
||||
|
||||
try {
|
||||
expandValueSetR5IncludeOrExclude(concepts, theCodeSystemLoader, theValueSetLoader, theInput.getCompose().getInclude(), true);
|
||||
expandValueSetR5IncludeOrExclude(concepts, theCodeSystemLoader, theValueSetLoader, theInput.getCompose().getExclude(), false);
|
||||
expandValueSetR5IncludeOrExclude(theRootValidationSupport, concepts, theCodeSystemLoader, theValueSetLoader, theInput.getCompose().getInclude(), true, theWantSystem, theWantCode);
|
||||
expandValueSetR5IncludeOrExclude(theRootValidationSupport, concepts, theCodeSystemLoader, theValueSetLoader, theInput.getCompose().getExclude(), false, theWantSystem, theWantCode);
|
||||
} catch (ExpansionCouldNotBeCompletedInternallyException e) {
|
||||
return null;
|
||||
}
|
||||
|
@ -403,34 +404,70 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
|||
return retVal;
|
||||
}
|
||||
|
||||
private void expandValueSetR5IncludeOrExclude(Set<VersionIndependentConcept> theConcepts, Function<String, CodeSystem> theCodeSystemLoader, Function<String, org.hl7.fhir.r5.model.ValueSet> theValueSetLoader, List<org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent> theComposeList, boolean theComposeListIsInclude) throws ExpansionCouldNotBeCompletedInternallyException {
|
||||
private void expandValueSetR5IncludeOrExclude(IValidationSupport theRootValidationSupport, Set<VersionIndependentConcept> theConcepts, Function<String, CodeSystem> theCodeSystemLoader, Function<String, org.hl7.fhir.r5.model.ValueSet> theValueSetLoader, List<org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent> theComposeList, boolean theComposeListIsInclude, @Nullable String theWantSystem, @Nullable String theWantCode) throws ExpansionCouldNotBeCompletedInternallyException {
|
||||
for (org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent nextInclude : theComposeList) {
|
||||
|
||||
List<VersionIndependentConcept> nextCodeList = new ArrayList<>();
|
||||
String system = nextInclude.getSystem();
|
||||
if (isNotBlank(system)) {
|
||||
|
||||
if (theWantSystem != null && !theWantSystem.equals(system)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CodeSystem codeSystem = theCodeSystemLoader.apply(system);
|
||||
if (codeSystem == null) {
|
||||
throw new ExpansionCouldNotBeCompletedInternallyException();
|
||||
}
|
||||
if (codeSystem.getContent() == CodeSystem.CodeSystemContentMode.NOTPRESENT) {
|
||||
throw new ExpansionCouldNotBeCompletedInternallyException();
|
||||
}
|
||||
|
||||
Set<String> wantCodes;
|
||||
if (nextInclude.getConcept().isEmpty()) {
|
||||
wantCodes = null;
|
||||
} else {
|
||||
wantCodes = nextInclude.getConcept().stream().map(t -> t.getCode()).collect(Collectors.toSet());
|
||||
wantCodes = nextInclude
|
||||
.getConcept()
|
||||
.stream().map(t -> t.getCode()).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
boolean ableToHandleCode = false;
|
||||
if (codeSystem == null) {
|
||||
|
||||
if (theWantCode != null) {
|
||||
LookupCodeResult lookup = theRootValidationSupport.lookupCode(theRootValidationSupport, system, theWantCode);
|
||||
if (lookup != null && lookup.isFound()) {
|
||||
CodeSystem.ConceptDefinitionComponent conceptDefinition = new CodeSystem.ConceptDefinitionComponent()
|
||||
.addConcept()
|
||||
.setCode(theWantCode)
|
||||
.setDisplay(lookup.getCodeDisplay());
|
||||
List<CodeSystem.ConceptDefinitionComponent> codesList = Collections.singletonList(conceptDefinition);
|
||||
addCodes(system, codesList, nextCodeList, wantCodes);
|
||||
ableToHandleCode = true;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
ableToHandleCode = true;
|
||||
|
||||
}
|
||||
|
||||
if (!ableToHandleCode) {
|
||||
throw new ExpansionCouldNotBeCompletedInternallyException();
|
||||
}
|
||||
|
||||
if (codeSystem != null) {
|
||||
|
||||
if (codeSystem.getContent() == CodeSystem.CodeSystemContentMode.NOTPRESENT) {
|
||||
throw new ExpansionCouldNotBeCompletedInternallyException();
|
||||
}
|
||||
|
||||
addCodes(system, codeSystem.getConcept(), nextCodeList, wantCodes);
|
||||
|
||||
}
|
||||
|
||||
addCodes(system, codeSystem.getConcept(), nextCodeList, wantCodes);
|
||||
}
|
||||
|
||||
for (CanonicalType nextValueSetInclude : nextInclude.getValueSet()) {
|
||||
org.hl7.fhir.r5.model.ValueSet vs = theValueSetLoader.apply(nextValueSetInclude.getValueAsString());
|
||||
if (vs != null) {
|
||||
org.hl7.fhir.r5.model.ValueSet subExpansion = expandValueSetR5(vs, theCodeSystemLoader, theValueSetLoader);
|
||||
org.hl7.fhir.r5.model.ValueSet subExpansion = expandValueSetR5(theRootValidationSupport, vs, theCodeSystemLoader, theValueSetLoader, theWantSystem, theWantCode);
|
||||
if (subExpansion == null) {
|
||||
throw new ExpansionCouldNotBeCompletedInternallyException();
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ public class SchemaBaseValidatorTest {
|
|||
validator.loadXml("foo.xsd");
|
||||
fail();
|
||||
} catch (InternalErrorException e) {
|
||||
assertThat(e.getMessage(), containsString("Schema not found"));
|
||||
assertThat(e.getMessage(), containsString("Unable to find classpath resource"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
package org.hl7.fhir.common.hapi.validation.support;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.support.ConceptValidationOptions;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import org.hl7.fhir.r4.model.ValueSet;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
public class CommonCodeSystemsTerminologyServiceTest {
|
||||
|
||||
private CommonCodeSystemsTerminologyService mySvc;
|
||||
private FhirContext myCtx;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
myCtx = FhirContext.forR4();
|
||||
mySvc = new CommonCodeSystemsTerminologyService(myCtx);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUcum_LookupCode_Good() {
|
||||
IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(myCtx.getValidationSupport(), "http://unitsofmeasure.org", "Cel");
|
||||
assertEquals(true, outcome.isFound());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUcum_LookupCode_Bad() {
|
||||
IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(myCtx.getValidationSupport(), "http://unitsofmeasure.org", "AAAAA");
|
||||
assertNull( outcome);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUcum_LookupCode_UnknownSystem() {
|
||||
IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(myCtx.getValidationSupport(), "http://foo", "AAAAA");
|
||||
assertNull( outcome);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUcum_ValidateCode_Good() {
|
||||
ValueSet vs = new ValueSet();
|
||||
vs.setUrl("http://hl7.org/fhir/ValueSet/ucum-units");
|
||||
IValidationSupport.CodeValidationResult outcome = mySvc.validateCodeInValueSet(myCtx.getValidationSupport(), new ConceptValidationOptions(), "http://unitsofmeasure.org", "mg", null, vs);
|
||||
assertEquals(true, outcome.isOk());
|
||||
assertEquals("(milligram)", outcome.getDisplay());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUcum_ValidateCode_Good_SystemInferred() {
|
||||
ValueSet vs = new ValueSet();
|
||||
vs.setUrl("http://hl7.org/fhir/ValueSet/ucum-units");
|
||||
IValidationSupport.CodeValidationResult outcome = mySvc.validateCodeInValueSet(myCtx.getValidationSupport(), new ConceptValidationOptions().setInferSystem(true), null, "mg", null, vs);
|
||||
assertEquals(true, outcome.isOk());
|
||||
assertEquals("(milligram)", outcome.getDisplay());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUcum_ValidateCode_Bad() {
|
||||
ValueSet vs = new ValueSet();
|
||||
vs.setUrl("http://hl7.org/fhir/ValueSet/ucum-units");
|
||||
IValidationSupport.CodeValidationResult outcome = mySvc.validateCodeInValueSet(myCtx.getValidationSupport(), new ConceptValidationOptions(), "http://unitsofmeasure.org", "aaaaa", null, vs);
|
||||
assertNull(outcome);
|
||||
}
|
||||
|
||||
}
|
|
@ -17,19 +17,43 @@ import org.apache.commons.io.IOUtils;
|
|||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.common.hapi.validation.support.CachingValidationSupport;
|
||||
import org.hl7.fhir.common.hapi.validation.support.CommonCodeSystemsTerminologyService;
|
||||
import org.hl7.fhir.common.hapi.validation.support.PrePopulatedValidationSupport;
|
||||
import org.hl7.fhir.common.hapi.validation.support.InMemoryTerminologyServerValidationSupport;
|
||||
import org.hl7.fhir.common.hapi.validation.support.PrePopulatedValidationSupport;
|
||||
import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain;
|
||||
import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4.conformance.ProfileUtilities;
|
||||
import org.hl7.fhir.r4.context.IWorkerContext;
|
||||
import org.hl7.fhir.r4.hapi.ctx.HapiWorkerContext;
|
||||
import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator;
|
||||
import org.hl7.fhir.r4.model.*;
|
||||
import org.hl7.fhir.r4.model.AllergyIntolerance;
|
||||
import org.hl7.fhir.r4.model.Base;
|
||||
import org.hl7.fhir.r4.model.Base64BinaryType;
|
||||
import org.hl7.fhir.r4.model.BooleanType;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
|
||||
import org.hl7.fhir.r4.model.CodeSystem;
|
||||
import org.hl7.fhir.r4.model.CodeType;
|
||||
import org.hl7.fhir.r4.model.Consent;
|
||||
import org.hl7.fhir.r4.model.ContactPoint;
|
||||
import org.hl7.fhir.r4.model.DateTimeType;
|
||||
import org.hl7.fhir.r4.model.Extension;
|
||||
import org.hl7.fhir.r4.model.Media;
|
||||
import org.hl7.fhir.r4.model.Narrative;
|
||||
import org.hl7.fhir.r4.model.Observation;
|
||||
import org.hl7.fhir.r4.model.Observation.ObservationStatus;
|
||||
import org.hl7.fhir.r4.model.OperationOutcome;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.hl7.fhir.r4.model.Period;
|
||||
import org.hl7.fhir.r4.model.Practitioner;
|
||||
import org.hl7.fhir.r4.model.Procedure;
|
||||
import org.hl7.fhir.r4.model.QuestionnaireResponse;
|
||||
import org.hl7.fhir.r4.model.Reference;
|
||||
import org.hl7.fhir.r4.model.RelatedPerson;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
import org.hl7.fhir.r4.model.StructureDefinition;
|
||||
import org.hl7.fhir.r4.model.StructureDefinition.StructureDefinitionKind;
|
||||
import org.hl7.fhir.r4.model.ValueSet;
|
||||
import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent;
|
||||
import org.hl7.fhir.r4.terminologies.ValueSetExpander;
|
||||
import org.hl7.fhir.r4.utils.FHIRPathEngine;
|
||||
|
@ -195,7 +219,16 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
|
|||
when(mockSupport.fetchCodeSystem(nullable(String.class))).thenAnswer(new Answer<CodeSystem>() {
|
||||
@Override
|
||||
public CodeSystem answer(InvocationOnMock theInvocation) {
|
||||
CodeSystem retVal = (CodeSystem) myDefaultValidationSupport.fetchCodeSystem((String) theInvocation.getArguments()[0]);
|
||||
String system = theInvocation.getArgument(0, String.class);
|
||||
if ("http://loinc.org".equals(system)) {
|
||||
CodeSystem retVal = new CodeSystem();
|
||||
retVal.setUrl("http://loinc.org");
|
||||
retVal.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT);
|
||||
ourLog.debug("fetchCodeSystem({}) : {}", new Object[]{theInvocation.getArguments()[0], retVal});
|
||||
return retVal;
|
||||
}
|
||||
|
||||
CodeSystem retVal = (CodeSystem) myDefaultValidationSupport.fetchCodeSystem(system);
|
||||
ourLog.debug("fetchCodeSystem({}) : {}", new Object[]{theInvocation.getArguments()[0], retVal});
|
||||
return retVal;
|
||||
}
|
||||
|
@ -216,6 +249,23 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
|
|||
return retVal;
|
||||
}
|
||||
});
|
||||
when(mockSupport.lookupCode(any(), any(), any())).thenAnswer(t -> {
|
||||
String system = t.getArgument(1, String.class);
|
||||
String code = t.getArgument(2, String.class);
|
||||
if (myValidConcepts.contains(system + "___" + code)) {
|
||||
return new IValidationSupport.LookupCodeResult().setFound(true);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
when(mockSupport.validateCodeInValueSet(any(), any(), any(), any(), any(), any())).thenAnswer(t -> {
|
||||
String system = t.getArgument(2, String.class);
|
||||
String code = t.getArgument(3, String.class);
|
||||
if (myValidConcepts.contains(system + "___" + code)) {
|
||||
return new IValidationSupport.CodeValidationResult().setCode(code).setDisplay(code);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
@ -1239,6 +1289,25 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
|
|||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testValidateWithUcum() throws IOException {
|
||||
addValidConcept("http://loinc.org", "8310-5");
|
||||
|
||||
Observation input = loadResource(ourCtx, Observation.class, "/r4/observation-with-body-temp-ucum.json");
|
||||
ValidationResult output = myVal.validateWithResult(input);
|
||||
List<SingleValidationMessage> all = logResultsAndReturnNonInformationalOnes(output);
|
||||
assertThat(all, empty());
|
||||
|
||||
// Change the unit to something not supported
|
||||
input.getValueQuantity().setCode("Heck");
|
||||
output = myVal.validateWithResult(input);
|
||||
all = logResultsAndReturnNonInformationalOnes(output);
|
||||
assertEquals(1, all.size());
|
||||
assertThat(all.get(0).getMessage(), containsString("The value provided (\"Heck\") is not in the value set http://hl7.org/fhir/ValueSet/ucum-bodytemp"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiplePerformer() {
|
||||
Observation o = new Observation();
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"resourceType": "Observation",
|
||||
"id": "bodytemp",
|
||||
"meta": {
|
||||
"profile": [
|
||||
"http://hl7.org/fhir/StructureDefinition/bodytemp"
|
||||
]
|
||||
},
|
||||
"status": "final",
|
||||
"category": [
|
||||
{
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://terminology.hl7.org/CodeSystem/observation-category",
|
||||
"code": "vital-signs"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"code": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://loinc.org",
|
||||
"code": "8310-5"
|
||||
}
|
||||
]
|
||||
},
|
||||
"subject": {
|
||||
"reference": "Patient/1"
|
||||
},
|
||||
"effectiveDateTime": "2020-04-30T12:00:00+01:00",
|
||||
"valueQuantity": {
|
||||
"value": 37.5,
|
||||
"unit": "Cel",
|
||||
"system": "http://unitsofmeasure.org",
|
||||
"code": "Cel"
|
||||
}
|
||||
}
|
2
pom.xml
2
pom.xml
|
@ -664,7 +664,7 @@
|
|||
<!-- 9.4.17 seems to have issues -->
|
||||
<jetty_version>9.4.24.v20191120</jetty_version>
|
||||
<jsr305_version>3.0.2</jsr305_version>
|
||||
<flyway_version>6.1.0</flyway_version>
|
||||
<flyway_version>6.4.1</flyway_version>
|
||||
<!--<hibernate_version>5.2.10.Final</hibernate_version>-->
|
||||
<hibernate_version>5.4.14.Final</hibernate_version>
|
||||
<!-- Update lucene version when you update hibernate-search version -->
|
||||
|
|
Loading…
Reference in New Issue