Fix bug in code. Add test. Add changelog

This commit is contained in:
Tadgh 2021-08-25 18:16:47 -04:00
parent 090a8b0821
commit fd6dcf6363
3 changed files with 96 additions and 52 deletions

View File

@ -0,0 +1,6 @@
---
type: fix
issue: 2920
jira: SMILE-2971
title: "Previously, validation against bcp47 (urn:ietf:bcp:47) as a language would fail validation if the region was absent. This has been fixed, and the validate
operation will now correctly validate simple languages, e.g. `nl` instead of requiring `nl-DE` or `nl-NL`"

View File

@ -257,69 +257,100 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport {
Map<String, String> languagesMap = myLanguagesLanugageMap; Map<String, String> languagesMap = myLanguagesLanugageMap;
Map<String, String> regionsMap = myLanguagesRegionMap; Map<String, String> regionsMap = myLanguagesRegionMap;
if (languagesMap == null || regionsMap == null) { if (languagesMap == null || regionsMap == null) {
initializeBcp47LanguageMap();
}
ourLog.info("Loading BCP47 Language Registry"); int langRegionSeparatorIndex = StringUtils.indexOfAny(theCode, '-', '_');
boolean hasRegionAndCodeSegments = langRegionSeparatorIndex > 0;
String language;
String region;
String input = ClasspathUtil.loadResource("org/hl7/fhir/common/hapi/validation/support/registry.json"); if (hasRegionAndCodeSegments) {
ArrayNode map; language = myLanguagesLanugageMap.get(theCode.substring(0, langRegionSeparatorIndex));
try { region = myLanguagesRegionMap.get(theCode.substring(langRegionSeparatorIndex + 1));
map = (ArrayNode) new ObjectMapper().readTree(input);
} catch (JsonProcessingException e) { if (language == null || region == null) {
throw new ConfigurationException(e); //In case the user provides both a language and a region, they must both be valid for the lookup to succeed.
ourLog.warn("Couldn't find a valid bcp47 language-region combination from code: {}", theCode);
return buildNotFoundLookupCodeResult(theCode);
} else {
return buildLookupResultForLanguageAndRegion(theCode, language, region);
} }
} else {
languagesMap = new HashMap<>(); //In case user has only provided a language, we build the lookup from only that.
regionsMap = new HashMap<>(); language = myLanguagesLanugageMap.get(theCode);
if (language == null) {
for (int i = 0; i < map.size(); i++) { ourLog.warn("Couldn't find a valid bcp47 language from code: {}", theCode);
ObjectNode next = (ObjectNode) map.get(i); return buildNotFoundLookupCodeResult(theCode);
String type = next.get("Type").asText(); } else {
if ("language".equals(type)) { return buildLookupResultForLanguage(theCode, language);
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);
}
} }
}
}
private LookupCodeResult buildLookupResultForLanguageAndRegion(@Nonnull String theOriginalCode, @Nonnull String theLanguage, @Nonnull String theRegion) {
LookupCodeResult lookupCodeResult = buildNotFoundLookupCodeResult(theOriginalCode);
lookupCodeResult.setCodeDisplay(theLanguage + " " + theRegion);
lookupCodeResult.setFound(true);
return lookupCodeResult;
}
private LookupCodeResult buildLookupResultForLanguage(@Nonnull String theOriginalCode, @Nonnull String theLanguage) {
LookupCodeResult lookupCodeResult = buildNotFoundLookupCodeResult(theOriginalCode);
lookupCodeResult.setCodeDisplay(theLanguage);
lookupCodeResult.setFound(true);
return lookupCodeResult;
}
ourLog.info("Have {} languages and {} regions", languagesMap.size(), regionsMap.size()); private LookupCodeResult buildNotFoundLookupCodeResult(@Nonnull String theOriginalCode) {
LookupCodeResult lookupCodeResult = new LookupCodeResult();
lookupCodeResult.setFound(false);
lookupCodeResult.setSearchedForSystem(LANGUAGES_CODESYSTEM_URL);
lookupCodeResult.setSearchedForCode(theOriginalCode);
return lookupCodeResult;
}
myLanguagesLanugageMap = languagesMap; private void initializeBcp47LanguageMap() {
myLanguagesRegionMap = regionsMap; Map<String, String> regionsMap;
Map<String, String> languagesMap;
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);
} }
int idx = StringUtils.indexOfAny(theCode, '-', '_'); languagesMap = new HashMap<>();
String language = null; regionsMap = new HashMap<>();
String region = null;
if (idx > 0) { for (int i = 0; i < map.size(); i++) {
language = languagesMap.get(theCode.substring(0, idx)); ObjectNode next = (ObjectNode) map.get(i);
region = regionsMap.get(theCode.substring(idx + 1)); 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);
}
} }
LookupCodeResult retVal = new LookupCodeResult(); ourLog.info("Have {} languages and {} regions", languagesMap.size(), regionsMap.size());
retVal.setSearchedForCode(theCode);
retVal.setSearchedForSystem(LANGUAGES_CODESYSTEM_URL);
if (language != null && region != null) { myLanguagesLanugageMap = languagesMap;
String display = language + " " + region; myLanguagesRegionMap = regionsMap;
retVal.setFound(true);
retVal.setCodeDisplay(display);
}
return retVal;
} }
@Nonnull @Nonnull

View File

@ -105,6 +105,13 @@ public class CommonCodeSystemsTerminologyServiceTest {
assertEquals("English (United States)", outcome.getDisplay()); assertEquals("English (United States)", outcome.getDisplay());
} }
@Test
public void testLanguages_CommonLanguagesVs_OnlyLanguage_NoRegion() {
IValidationSupport.LookupCodeResult nl = mySvc.lookupCode(newSupport(), "urn:ietf:bcp:47", "nl");
assertTrue(nl.isFound());
assertEquals("Dutch", nl.getCodeDisplay());
}
@Test @Test
public void testLanguages_CommonLanguagesVs_BadCode() { public void testLanguages_CommonLanguagesVs_BadCode() {
IValidationSupport.CodeValidationResult outcome = mySvc.validateCode(newSupport(), newOptions(), "urn:ietf:bcp:47", "FOO", null, "http://hl7.org/fhir/ValueSet/languages"); IValidationSupport.CodeValidationResult outcome = mySvc.validateCode(newSupport(), newOptions(), "urn:ietf:bcp:47", "FOO", null, "http://hl7.org/fhir/ValueSet/languages");