Add language validation (#2644)

* Add language validation

* Add tests

* Test fix
This commit is contained in:
James Agnew 2021-05-10 20:09:45 -04:00 committed by GitHub
parent eec675457b
commit 26d030f93e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 76669 additions and 36 deletions

View File

@ -0,0 +1,5 @@
---
type: add
issue: 2644
title: "Support for validating BCP-47 (language) codes against the FHIR Languages and All-Languages ValueSets
has been improved."

View File

@ -89,13 +89,22 @@ The following table lists vocabulary that is validated by this module:
<tr> <tr>
<td>Languages (BCP-47)</td> <td>Languages (BCP-47)</td>
<td> <td>
ValueSet: <a href="http://hl7.org/fhir/ValueSet/mimetypes">(...)/ValueSet/mimetypes</a> ValueSet: <a href="http://hl7.org/fhir/ValueSet/languages">(...)/ValueSet/languages</a>
<br/>
ValueSet: <a href="http://hl7.org/fhir/ValueSet/all-languages">(...)/ValueSet/all-languages</a>
<br/> <br/>
CodeSystem: <code>urn:ietf:bcp:47</code> CodeSystem: <code>urn:ietf:bcp:47</code>
</td> </td>
<td> <td>
Codes are not validated, but are instead assumed to be correct. Improved validation should be Codes are validated against the respective ValueSet. Support for two different ValueSets
added in the future, please get in touch if you would like to help. is provided: The <a href="http://hl7.org/fhir/ValueSet/languages">languages</a>
ValueSet provides a collection of commonly used language codes. Only codes explicitly
referenced in this ValueSet are considered valid.
The <a href="http://hl7.org/fhir/ValueSet/languages">all-languages</a> ValueSet
accepts any valid BCP-47 code. Codes are validated using data supplied by
the
<a href="https://github.com/mattcg/language-subtag-registry">Language Subtype Registry</a>
project.
</td> </td>
</tr> </tr>
<tr> <tr>

View File

@ -73,6 +73,7 @@ public class JpaFhirRetrieveProvider extends SearchParamFhirRetrieveProvider {
// TODO: Once HAPI breaks this out from the server dependencies // TODO: Once HAPI breaks this out from the server dependencies
// we can include it on its own. // we can include it on its own.
ca.uhn.fhir.jpa.searchparam.SearchParameterMap hapiMap = new ca.uhn.fhir.jpa.searchparam.SearchParameterMap(); ca.uhn.fhir.jpa.searchparam.SearchParameterMap hapiMap = new ca.uhn.fhir.jpa.searchparam.SearchParameterMap();
hapiMap.setLoadSynchronous(true);
try { try {
Method[] methods = hapiMap.getClass().getDeclaredMethods(); Method[] methods = hapiMap.getClass().getDeclaredMethods();

View File

@ -1,13 +1,20 @@
package org.hl7.fhir.common.hapi.validation.support; package org.hl7.fhir.common.hapi.validation.support;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.ConceptValidationOptions; import ca.uhn.fhir.context.support.ConceptValidationOptions;
import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.context.support.ValidationSupportContext;
import ca.uhn.fhir.util.ClasspathUtil; import ca.uhn.fhir.util.ClasspathUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
import org.fhir.ucum.UcumEssenceService; import org.fhir.ucum.UcumEssenceService;
import org.fhir.ucum.UcumException; import org.fhir.ucum.UcumException;
import org.hl7.fhir.common.hapi.validation.validator.VersionSpecificWorkerContextWrapper;
import org.hl7.fhir.convertors.VersionConvertor_30_40; import org.hl7.fhir.convertors.VersionConvertor_30_40;
import org.hl7.fhir.convertors.VersionConvertor_40_50; import org.hl7.fhir.convertors.VersionConvertor_40_50;
import org.hl7.fhir.dstu2.model.ValueSet; import org.hl7.fhir.dstu2.model.ValueSet;
@ -22,10 +29,12 @@ import java.io.InputStream;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.hl7.fhir.common.hapi.validation.support.SnapshotGeneratingValidationSupport.newVersionTypeConverter;
/** /**
* This {@link IValidationSupport validation support module} can be used to validate codes against common * This {@link IValidationSupport validation support module} can be used to validate codes against common
@ -38,6 +47,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
*/ */
public class CommonCodeSystemsTerminologyService implements IValidationSupport { public class CommonCodeSystemsTerminologyService implements IValidationSupport {
public static final String LANGUAGES_VALUESET_URL = "http://hl7.org/fhir/ValueSet/languages"; public static final String LANGUAGES_VALUESET_URL = "http://hl7.org/fhir/ValueSet/languages";
public static final String LANGUAGES_CODESYSTEM_URL = "urn:ietf:bcp:47";
public static final String MIMETYPES_VALUESET_URL = "http://hl7.org/fhir/ValueSet/mimetypes"; public static final String MIMETYPES_VALUESET_URL = "http://hl7.org/fhir/ValueSet/mimetypes";
public static final String MIMETYPES_CODESYSTEM_URL = "urn:ietf:bcp:13"; public static final String MIMETYPES_CODESYSTEM_URL = "urn:ietf:bcp:13";
public static final String CURRENCIES_CODESYSTEM_URL = "urn:iso:std:iso:4217"; public static final String CURRENCIES_CODESYSTEM_URL = "urn:iso:std:iso:4217";
@ -45,6 +55,7 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport {
public static final String COUNTRIES_CODESYSTEM_URL = "urn:iso:std:iso:3166"; public static final String COUNTRIES_CODESYSTEM_URL = "urn:iso:std:iso:3166";
public static final String UCUM_CODESYSTEM_URL = "http://unitsofmeasure.org"; public static final String UCUM_CODESYSTEM_URL = "http://unitsofmeasure.org";
public static final String UCUM_VALUESET_URL = "http://hl7.org/fhir/ValueSet/ucum-units"; public static final String UCUM_VALUESET_URL = "http://hl7.org/fhir/ValueSet/ucum-units";
public static final String ALL_LANGUAGES_VALUESET_URL = "http://hl7.org/fhir/ValueSet/all-languages";
private static final String USPS_CODESYSTEM_URL = "https://www.usps.com/"; 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 String USPS_VALUESET_URL = "http://hl7.org/fhir/us/core/ValueSet/us-core-usps-state";
private static final Logger ourLog = LoggerFactory.getLogger(CommonCodeSystemsTerminologyService.class); private static final Logger ourLog = LoggerFactory.getLogger(CommonCodeSystemsTerminologyService.class);
@ -52,6 +63,10 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport {
private static Map<String, String> ISO_4217_CODES = Collections.unmodifiableMap(buildIso4217Codes()); private static Map<String, String> ISO_4217_CODES = Collections.unmodifiableMap(buildIso4217Codes());
private static Map<String, String> ISO_3166_CODES = Collections.unmodifiableMap(buildIso3166Codes()); private static Map<String, String> ISO_3166_CODES = Collections.unmodifiableMap(buildIso3166Codes());
private final FhirContext myFhirContext; private final FhirContext myFhirContext;
private final VersionSpecificWorkerContextWrapper.IVersionTypeConverter myVersionConverter;
private volatile org.hl7.fhir.r5.model.ValueSet myLanguagesVs;
private volatile Map<String, String> myLanguagesLanugageMap;
private volatile Map<String, String> myLanguagesRegionMap;
/** /**
* Constructor * Constructor
@ -60,6 +75,7 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport {
Validate.notNull(theFhirContext); Validate.notNull(theFhirContext);
myFhirContext = theFhirContext; myFhirContext = theFhirContext;
myVersionConverter = newVersionTypeConverter(myFhirContext.getVersion().getVersion());
} }
@Override @Override
@ -89,6 +105,52 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport {
break; break;
case LANGUAGES_VALUESET_URL: case LANGUAGES_VALUESET_URL:
if (!LANGUAGES_CODESYSTEM_URL.equals(theCodeSystem) && !(theCodeSystem == null && theOptions.isInferSystem())) {
return new CodeValidationResult()
.setSeverity(IssueSeverity.ERROR)
.setMessage("Inappropriate CodeSystem URL \"" + theCodeSystem + "\" for ValueSet: " + theValueSetUrl);
}
IBaseResource languagesVs = myLanguagesVs;
if (languagesVs == null) {
languagesVs = theValidationSupportContext.getRootValidationSupport().fetchValueSet("http://hl7.org/fhir/ValueSet/languages");
myLanguagesVs = (org.hl7.fhir.r5.model.ValueSet) myVersionConverter.toCanonical(languagesVs);
}
Optional<org.hl7.fhir.r5.model.ValueSet.ConceptReferenceComponent> match = myLanguagesVs
.getCompose()
.getInclude()
.stream()
.flatMap(t -> t.getConcept().stream())
.filter(t -> theCode.equals(t.getCode()))
.findFirst();
if (match.isPresent()) {
return new CodeValidationResult()
.setCode(theCode)
.setDisplay(match.get().getDisplay());
} else {
return new CodeValidationResult()
.setSeverity(IssueSeverity.ERROR)
.setMessage("Code \"" + theCode + "\" is not in valueset: " + theValueSetUrl);
}
case ALL_LANGUAGES_VALUESET_URL:
if (!LANGUAGES_CODESYSTEM_URL.equals(theCodeSystem) && !(theCodeSystem == null && theOptions.isInferSystem())) {
return new CodeValidationResult()
.setSeverity(IssueSeverity.ERROR)
.setMessage("Inappropriate CodeSystem URL \"" + theCodeSystem + "\" for ValueSet: " + theValueSetUrl);
}
LookupCodeResult outcome = lookupLanguageCode(theCode);
if (outcome.isFound()) {
return new CodeValidationResult()
.setCode(theCode)
.setDisplay(outcome.getCodeDisplay());
} else {
return new CodeValidationResult()
.setSeverity(IssueSeverity.ERROR)
.setMessage("Code \"" + theCode + "\" is not in valueset: " + theValueSetUrl);
}
case MIMETYPES_VALUESET_URL: case MIMETYPES_VALUESET_URL:
// This is a pretty naive implementation - Should be enhanced in future // This is a pretty naive implementation - Should be enhanced in future
return new CodeValidationResult() return new CodeValidationResult()
@ -141,6 +203,7 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport {
.setDisplay(lookupResult.getCodeDisplay()); .setDisplay(lookupResult.getCodeDisplay());
} }
} }
return validationResult; return validationResult;
} }
@ -150,6 +213,8 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport {
Map<String, String> map; Map<String, String> map;
switch (theSystem) { switch (theSystem) {
case LANGUAGES_CODESYSTEM_URL:
return lookupLanguageCode(theCode);
case UCUM_CODESYSTEM_URL: case UCUM_CODESYSTEM_URL:
return lookupUcumCode(theCode); return lookupUcumCode(theCode);
case MIMETYPES_CODESYSTEM_URL: case MIMETYPES_CODESYSTEM_URL:
@ -186,6 +251,75 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport {
} }
private LookupCodeResult lookupLanguageCode(String theCode) {
Map<String, String> languagesMap = myLanguagesLanugageMap;
Map<String, String> regionsMap = myLanguagesRegionMap;
if (languagesMap == null || regionsMap == null) {
ourLog.info("Loading BCP47 Language Registry");
String input = ClasspathUtil.loadResource("org/hl7/fhir/common/hapi/validation/support/registry.json");
ArrayNode map;
try {
map = (ArrayNode) new ObjectMapper().readTree(input);
} catch (JsonProcessingException e) {
throw new ConfigurationException(e);
}
languagesMap = new HashMap<>();
regionsMap = new HashMap<>();
for (int i = 0; i < map.size(); i++) {
ObjectNode next = (ObjectNode) map.get(i);
String type = next.get("Type").asText();
if ("language".equals(type)) {
String language = next.get("Subtag").asText();
ArrayNode descriptions = (ArrayNode) next.get("Description");
String description = null;
if (descriptions.size() > 0) {
description = descriptions.get(0).asText();
}
languagesMap.put(language, description);
}
if ("region".equals(type)) {
String region = next.get("Subtag").asText();
ArrayNode descriptions = (ArrayNode) next.get("Description");
String description = null;
if (descriptions.size() > 0) {
description = descriptions.get(0).asText();
}
regionsMap.put(region, description);
}
}
ourLog.info("Have {} languages and {} regions", languagesMap.size(), regionsMap.size());
myLanguagesLanugageMap = languagesMap;
myLanguagesRegionMap = regionsMap;
}
int idx = StringUtils.indexOfAny(theCode, '-', '_');
String language = null;
String region = null;
if (idx > 0) {
language = languagesMap.get(theCode.substring(0, idx));
region = regionsMap.get(theCode.substring(idx + 1));
}
LookupCodeResult retVal = new LookupCodeResult();
retVal.setSearchedForCode(theCode);
retVal.setSearchedForSystem(LANGUAGES_CODESYSTEM_URL);
if (language != null && region != null) {
String display = language + " " + region;
retVal.setFound(true);
retVal.setCodeDisplay(display);
}
return retVal;
}
@Nonnull @Nonnull
private LookupCodeResult lookupMimetypeCode(String theCode) { private LookupCodeResult lookupMimetypeCode(String theCode) {
// This is a pretty naive implementation - Should be enhanced in future // This is a pretty naive implementation - Should be enhanced in future
@ -270,6 +404,7 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport {
case UCUM_CODESYSTEM_URL: case UCUM_CODESYSTEM_URL:
case MIMETYPES_CODESYSTEM_URL: case MIMETYPES_CODESYSTEM_URL:
case USPS_CODESYSTEM_URL: case USPS_CODESYSTEM_URL:
case LANGUAGES_CODESYSTEM_URL:
return true; return true;
} }
@ -282,6 +417,7 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport {
switch (theValueSetUrl) { switch (theValueSetUrl) {
case CURRENCIES_VALUESET_URL: case CURRENCIES_VALUESET_URL:
case LANGUAGES_VALUESET_URL: case LANGUAGES_VALUESET_URL:
case ALL_LANGUAGES_VALUESET_URL:
case MIMETYPES_VALUESET_URL: case MIMETYPES_VALUESET_URL:
case UCUM_VALUESET_URL: case UCUM_VALUESET_URL:
case USPS_VALUESET_URL: case USPS_VALUESET_URL:

View File

@ -1,6 +1,7 @@
package org.hl7.fhir.common.hapi.validation.support; package org.hl7.fhir.common.hapi.validation.support;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.context.support.ValidationSupportContext;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
@ -18,6 +19,7 @@ import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isBlank;
@ -49,26 +51,11 @@ public class SnapshotGeneratingValidationSupport implements IValidationSupport {
String inputUrl = null; String inputUrl = null;
try { try {
assert theInput.getStructureFhirVersionEnum() == myCtx.getVersion().getVersion(); FhirVersionEnum version = theInput.getStructureFhirVersionEnum();
assert version == myCtx.getVersion().getVersion();
VersionSpecificWorkerContextWrapper.IVersionTypeConverter converter;
switch (theInput.getStructureFhirVersionEnum()) {
case DSTU3:
converter = new VersionTypeConverterDstu3();
break;
case R4:
converter = new VersionTypeConverterR4();
break;
case R5:
converter = VersionSpecificWorkerContextWrapper.IDENTITY_VERSION_TYPE_CONVERTER;
break;
case DSTU2:
case DSTU2_HL7ORG:
case DSTU2_1:
default:
throw new IllegalStateException("Can not generate snapshot for version: " + theInput.getStructureFhirVersionEnum());
}
VersionSpecificWorkerContextWrapper.IVersionTypeConverter converter = newVersionTypeConverter(version);
Validate.notNull(converter, "Can not generate snapshot for version: %s", version);
org.hl7.fhir.r5.model.StructureDefinition inputCanonical = (org.hl7.fhir.r5.model.StructureDefinition) converter.toCanonical(theInput); org.hl7.fhir.r5.model.StructureDefinition inputCanonical = (org.hl7.fhir.r5.model.StructureDefinition) converter.toCanonical(theInput);
@ -103,7 +90,7 @@ public class SnapshotGeneratingValidationSupport implements IValidationSupport {
ProfileUtilities profileUtilities = new ProfileUtilities(context, messages, profileKnowledgeProvider); ProfileUtilities profileUtilities = new ProfileUtilities(context, messages, profileKnowledgeProvider);
profileUtilities.generateSnapshot(baseCanonical, inputCanonical, theUrl, theWebUrl, theProfileName); profileUtilities.generateSnapshot(baseCanonical, inputCanonical, theUrl, theWebUrl, theProfileName);
switch (theInput.getStructureFhirVersionEnum()) { switch (version) {
case DSTU3: case DSTU3:
org.hl7.fhir.dstu3.model.StructureDefinition generatedDstu3 = (org.hl7.fhir.dstu3.model.StructureDefinition) converter.fromCanonical(inputCanonical); org.hl7.fhir.dstu3.model.StructureDefinition generatedDstu3 = (org.hl7.fhir.dstu3.model.StructureDefinition) converter.fromCanonical(inputCanonical);
((org.hl7.fhir.dstu3.model.StructureDefinition) theInput).getSnapshot().getElement().clear(); ((org.hl7.fhir.dstu3.model.StructureDefinition) theInput).getSnapshot().getElement().clear();
@ -123,7 +110,7 @@ public class SnapshotGeneratingValidationSupport implements IValidationSupport {
case DSTU2_HL7ORG: case DSTU2_HL7ORG:
case DSTU2_1: case DSTU2_1:
default: default:
throw new IllegalStateException("Can not generate snapshot for version: " + theInput.getStructureFhirVersionEnum()); throw new IllegalStateException("Can not generate snapshot for version: " + version);
} }
return theInput; return theInput;
@ -144,5 +131,27 @@ public class SnapshotGeneratingValidationSupport implements IValidationSupport {
return myCtx; return myCtx;
} }
@Nullable
public static VersionSpecificWorkerContextWrapper.IVersionTypeConverter newVersionTypeConverter(FhirVersionEnum version) {
VersionSpecificWorkerContextWrapper.IVersionTypeConverter converter;
switch (version) {
case DSTU3:
converter = new VersionTypeConverterDstu3();
break;
case R4:
converter = new VersionTypeConverterR4();
break;
case R5:
converter = VersionSpecificWorkerContextWrapper.IDENTITY_VERSION_TYPE_CONVERTER;
break;
case DSTU2:
case DSTU2_HL7ORG:
case DSTU2_1:
default:
return null;
}
return converter;
}
} }

View File

@ -0,0 +1,4 @@
This package contains the BCP47 Language Registry from
https://github.com/mattcg/language-subtag-registry
Used under the terms of the Open Data Commons Attribution License (ODC-BY).

View File

@ -5,8 +5,6 @@ import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.support.ConceptValidationOptions; import ca.uhn.fhir.context.support.ConceptValidationOptions;
import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.context.support.ValidationSupportContext;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.CodeSystem; import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.ValueSet; import org.hl7.fhir.r4.model.ValueSet;
@ -14,11 +12,11 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assertions.fail;
import javax.annotation.Nonnull;
public class CommonCodeSystemsTerminologyServiceTest { public class CommonCodeSystemsTerminologyServiceTest {
private CommonCodeSystemsTerminologyService mySvc; private CommonCodeSystemsTerminologyService mySvc;
@ -32,25 +30,28 @@ public class CommonCodeSystemsTerminologyServiceTest {
@Test @Test
public void testUcum_LookupCode_Good() { public void testUcum_LookupCode_Good() {
IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(new ValidationSupportContext(myCtx.getValidationSupport()), "http://unitsofmeasure.org", "Cel"); IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(newSupport(), "http://unitsofmeasure.org", "Cel");
assert outcome != null;
assertEquals(true, outcome.isFound()); assertEquals(true, outcome.isFound());
} }
@Test @Test
public void testUcum_LookupCode_Good2() { public void testUcum_LookupCode_Good2() {
IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(new ValidationSupportContext(myCtx.getValidationSupport()), "http://unitsofmeasure.org", "kg/m2"); IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(newSupport(), "http://unitsofmeasure.org", "kg/m2");
assert outcome != null;
assertEquals(true, outcome.isFound()); assertEquals(true, outcome.isFound());
} }
@Test @Test
public void testUcum_LookupCode_Bad() { public void testUcum_LookupCode_Bad() {
IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(new ValidationSupportContext(myCtx.getValidationSupport()), "http://unitsofmeasure.org", "AAAAA"); IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(newSupport(), "http://unitsofmeasure.org", "AAAAA");
assert outcome != null;
assertEquals(false, outcome.isFound()); assertEquals(false, outcome.isFound());
} }
@Test @Test
public void testUcum_LookupCode_UnknownSystem() { public void testUcum_LookupCode_UnknownSystem() {
IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(new ValidationSupportContext(myCtx.getValidationSupport()), "http://foo", "AAAAA"); IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(newSupport(), "http://foo", "AAAAA");
assertNull(outcome); assertNull(outcome);
} }
@ -58,7 +59,8 @@ public class CommonCodeSystemsTerminologyServiceTest {
public void testUcum_ValidateCode_Good() { public void testUcum_ValidateCode_Good() {
ValueSet vs = new ValueSet(); ValueSet vs = new ValueSet();
vs.setUrl("http://hl7.org/fhir/ValueSet/ucum-units"); vs.setUrl("http://hl7.org/fhir/ValueSet/ucum-units");
IValidationSupport.CodeValidationResult outcome = mySvc.validateCodeInValueSet(new ValidationSupportContext(myCtx.getValidationSupport()), new ConceptValidationOptions(), "http://unitsofmeasure.org", "mg", null, vs); IValidationSupport.CodeValidationResult outcome = mySvc.validateCodeInValueSet(newSupport(), newOptions(), "http://unitsofmeasure.org", "mg", null, vs);
assert outcome != null;
assertEquals(true, outcome.isOk()); assertEquals(true, outcome.isOk());
assertEquals("(milligram)", outcome.getDisplay()); assertEquals("(milligram)", outcome.getDisplay());
} }
@ -67,7 +69,8 @@ public class CommonCodeSystemsTerminologyServiceTest {
public void testUcum_ValidateCode_Good_SystemInferred() { public void testUcum_ValidateCode_Good_SystemInferred() {
ValueSet vs = new ValueSet(); ValueSet vs = new ValueSet();
vs.setUrl("http://hl7.org/fhir/ValueSet/ucum-units"); vs.setUrl("http://hl7.org/fhir/ValueSet/ucum-units");
IValidationSupport.CodeValidationResult outcome = mySvc.validateCodeInValueSet(new ValidationSupportContext(myCtx.getValidationSupport()), new ConceptValidationOptions().setInferSystem(true), null, "mg", null, vs); IValidationSupport.CodeValidationResult outcome = mySvc.validateCodeInValueSet(newSupport(), newOptions().setInferSystem(true), null, "mg", null, vs);
assert outcome != null;
assertEquals(true, outcome.isOk()); assertEquals(true, outcome.isOk());
assertEquals("(milligram)", outcome.getDisplay()); assertEquals("(milligram)", outcome.getDisplay());
} }
@ -76,13 +79,76 @@ public class CommonCodeSystemsTerminologyServiceTest {
public void testUcum_ValidateCode_Bad() { public void testUcum_ValidateCode_Bad() {
ValueSet vs = new ValueSet(); ValueSet vs = new ValueSet();
vs.setUrl("http://hl7.org/fhir/ValueSet/ucum-units"); vs.setUrl("http://hl7.org/fhir/ValueSet/ucum-units");
IValidationSupport.CodeValidationResult outcome = mySvc.validateCodeInValueSet(new ValidationSupportContext(myCtx.getValidationSupport()), new ConceptValidationOptions(), "http://unitsofmeasure.org", "aaaaa", null, vs); IValidationSupport.CodeValidationResult outcome = mySvc.validateCodeInValueSet(newSupport(), newOptions(), "http://unitsofmeasure.org", "aaaaa", null, vs);
assertNull(outcome); assertNull(outcome);
} }
@Test
public void testLanguagesLanguagesCs_GoodCode() {
IValidationSupport.CodeValidationResult outcome = mySvc.validateLookupCode(newSupport(), "en-CA", "urn:ietf:bcp:47");
assert outcome != null;
assertTrue(outcome.isOk());
assertEquals("English Canada", outcome.getDisplay());
}
@Test
public void testLanguagesLanguagesCs_BadCode() {
IValidationSupport.CodeValidationResult outcome = mySvc.validateLookupCode(newSupport(), "en-FOO", "urn:ietf:bcp:47");
assertNull(outcome);
}
@Test
public void testLanguages_CommonLanguagesVs_GoodCode() {
IValidationSupport.CodeValidationResult outcome = mySvc.validateCode(newSupport(), newOptions(), "urn:ietf:bcp:47", "en-US", null, "http://hl7.org/fhir/ValueSet/languages");
assert outcome != null;
assertTrue(outcome.isOk());
assertEquals("English (United States)", outcome.getDisplay());
}
@Test
public void testLanguages_CommonLanguagesVs_BadCode() {
IValidationSupport.CodeValidationResult outcome = mySvc.validateCode(newSupport(), newOptions(), "urn:ietf:bcp:47", "FOO", null, "http://hl7.org/fhir/ValueSet/languages");
assert outcome != null;
assertFalse(outcome.isOk());
assertEquals("Code \"FOO\" is not in valueset: http://hl7.org/fhir/ValueSet/languages", outcome.getMessage());
}
@Test
public void testLanguages_CommonLanguagesVs_BadSystem() {
IValidationSupport.CodeValidationResult outcome = mySvc.validateCode(newSupport(), newOptions(), "FOO", "en-US", null, "http://hl7.org/fhir/ValueSet/languages");
assert outcome != null;
assertFalse(outcome.isOk());
assertEquals("Inappropriate CodeSystem URL \"FOO\" for ValueSet: http://hl7.org/fhir/ValueSet/languages", outcome.getMessage());
}
@Test
public void testLanguages_AllLanguagesVs_GoodCode() {
IValidationSupport.CodeValidationResult outcome = mySvc.validateCode(newSupport(), newOptions(), "urn:ietf:bcp:47", "en-US", null, "http://hl7.org/fhir/ValueSet/all-languages");
assert outcome != null;
assertTrue(outcome.isOk());
assertEquals("English United States", outcome.getDisplay());
}
@Test
public void testLanguages_AllLanguagesVs_BadCode() {
IValidationSupport.CodeValidationResult outcome = mySvc.validateCode(newSupport(), newOptions(), "urn:ietf:bcp:47", "FOO", null, "http://hl7.org/fhir/ValueSet/all-languages");
assert outcome != null;
assertFalse(outcome.isOk());
assertEquals("Code \"FOO\" is not in valueset: http://hl7.org/fhir/ValueSet/all-languages", outcome.getMessage());
}
@Test
public void testLanguages_AllLanguagesVs_BadSystem() {
IValidationSupport.CodeValidationResult outcome = mySvc.validateCode(newSupport(), newOptions(), "FOO", "en-US", null, "http://hl7.org/fhir/ValueSet/all-languages");
assert outcome != null;
assertFalse(outcome.isOk());
assertEquals("Inappropriate CodeSystem URL \"FOO\" for ValueSet: http://hl7.org/fhir/ValueSet/all-languages", outcome.getMessage());
}
@Test @Test
public void testFetchCodeSystemBuiltIn_Iso3166_R4() { public void testFetchCodeSystemBuiltIn_Iso3166_R4() {
CodeSystem cs = (CodeSystem) mySvc.fetchCodeSystem(CommonCodeSystemsTerminologyService.COUNTRIES_CODESYSTEM_URL); CodeSystem cs = (CodeSystem) mySvc.fetchCodeSystem(CommonCodeSystemsTerminologyService.COUNTRIES_CODESYSTEM_URL);
assert cs != null;
assertEquals(498, cs.getConcept().size()); assertEquals(498, cs.getConcept().size());
} }
@ -90,6 +156,7 @@ public class CommonCodeSystemsTerminologyServiceTest {
public void testFetchCodeSystemBuiltIn_Iso3166_DSTU3() { public void testFetchCodeSystemBuiltIn_Iso3166_DSTU3() {
CommonCodeSystemsTerminologyService svc = new CommonCodeSystemsTerminologyService(FhirContext.forCached(FhirVersionEnum.DSTU3)); CommonCodeSystemsTerminologyService svc = new CommonCodeSystemsTerminologyService(FhirContext.forCached(FhirVersionEnum.DSTU3));
org.hl7.fhir.dstu3.model.CodeSystem cs = (org.hl7.fhir.dstu3.model.CodeSystem) svc.fetchCodeSystem(CommonCodeSystemsTerminologyService.COUNTRIES_CODESYSTEM_URL); org.hl7.fhir.dstu3.model.CodeSystem cs = (org.hl7.fhir.dstu3.model.CodeSystem) svc.fetchCodeSystem(CommonCodeSystemsTerminologyService.COUNTRIES_CODESYSTEM_URL);
assert cs != null;
assertEquals(498, cs.getConcept().size()); assertEquals(498, cs.getConcept().size());
} }
@ -97,6 +164,7 @@ public class CommonCodeSystemsTerminologyServiceTest {
public void testFetchCodeSystemBuiltIn_Iso3166_R5() { public void testFetchCodeSystemBuiltIn_Iso3166_R5() {
CommonCodeSystemsTerminologyService svc = new CommonCodeSystemsTerminologyService(FhirContext.forCached(FhirVersionEnum.R5)); CommonCodeSystemsTerminologyService svc = new CommonCodeSystemsTerminologyService(FhirContext.forCached(FhirVersionEnum.R5));
org.hl7.fhir.r5.model.CodeSystem cs = (org.hl7.fhir.r5.model.CodeSystem) svc.fetchCodeSystem(CommonCodeSystemsTerminologyService.COUNTRIES_CODESYSTEM_URL); org.hl7.fhir.r5.model.CodeSystem cs = (org.hl7.fhir.r5.model.CodeSystem) svc.fetchCodeSystem(CommonCodeSystemsTerminologyService.COUNTRIES_CODESYSTEM_URL);
assert cs != null;
assertEquals(498, cs.getConcept().size()); assertEquals(498, cs.getConcept().size());
} }
@ -110,6 +178,7 @@ public class CommonCodeSystemsTerminologyServiceTest {
@Test @Test
public void testFetchCodeSystemBuiltIn_Iso_R4() { public void testFetchCodeSystemBuiltIn_Iso_R4() {
CodeSystem cs = (CodeSystem) mySvc.fetchCodeSystem(CommonCodeSystemsTerminologyService.CURRENCIES_CODESYSTEM_URL); CodeSystem cs = (CodeSystem) mySvc.fetchCodeSystem(CommonCodeSystemsTerminologyService.CURRENCIES_CODESYSTEM_URL);
assert cs != null;
assertEquals(182, cs.getConcept().size()); assertEquals(182, cs.getConcept().size());
} }
@ -130,5 +199,13 @@ public class CommonCodeSystemsTerminologyServiceTest {
} }
} }
private ValidationSupportContext newSupport() {
return new ValidationSupportContext(myCtx.getValidationSupport());
}
private ConceptValidationOptions newOptions() {
return new ConceptValidationOptions();
}
} }