Skip to content

Commit

Permalink
2023 08 gg extension paths (#1380)
Browse files Browse the repository at this point in the history
* fix problem with whitespace in terminology caching

* Support for x-version extensions when generating snapshots

* Fix problem with evaluating extension contexts

* Produce useful error message when whitespace is wrong in display name

* Resolve URL for x-version extensions

* render inactive property in expansions

* fix tests

* ping build

* Point to correct test-cases version

---------

Co-authored-by: Grahame Grieve <[email protected]>
Co-authored-by: dotasek <[email protected]>
  • Loading branch information
3 people authored Aug 3, 2023
1 parent 011167a commit 6d08834
Show file tree
Hide file tree
Showing 19 changed files with 693 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -846,7 +846,7 @@ public void generateSnapshot(StructureDefinition base, StructureDefinition deriv
for (UriType u : t.getProfile()) {
StructureDefinition sd = context.fetchResource(StructureDefinition.class, u.getValue(), derived);
if (sd == null) {
if (xver != null && xver.matchingUrl(u.getValue()) && xver.status(u.getValue()) == XVerExtensionStatus.Valid) {
if (makeXVer().matchingUrl(u.getValue()) && xver.status(u.getValue()) == XVerExtensionStatus.Valid) {
sd = xver.makeDefinition(u.getValue());
}
}
Expand Down Expand Up @@ -891,6 +891,13 @@ public void generateSnapshot(StructureDefinition base, StructureDefinition deriv
}
}

private XVerExtensionManager makeXVer() {
if (xver == null) {
xver = new XVerExtensionManager(context);
}
return xver;
}

private ElementDefinition getElementInCurrentContext(String path, List<ElementDefinition> list) {
for (int i = list.size() -1; i >= 0; i--) {
ElementDefinition t = list.get(i);
Expand Down Expand Up @@ -1759,7 +1766,7 @@ protected StructureDefinition getProfileForDataType(TypeRefComponent type, Strin
if (type.hasProfile()) {
sd = context.fetchResource(StructureDefinition.class, type.getProfile().get(0).getValue(), src);
if (sd == null) {
if (xver != null && xver.matchingUrl(type.getProfile().get(0).getValue()) && xver.status(type.getProfile().get(0).getValue()) == XVerExtensionStatus.Valid) {
if (makeXVer().matchingUrl(type.getProfile().get(0).getValue()) && xver.status(type.getProfile().get(0).getValue()) == XVerExtensionStatus.Valid) {
sd = xver.makeDefinition(type.getProfile().get(0).getValue());
generateSnapshot(context.fetchTypeDefinition("Extension"), sd, sd.getUrl(), webUrl, sd.getName());
}
Expand Down Expand Up @@ -2297,8 +2304,25 @@ protected void updateFromDefinition(ElementDefinition dest, ElementDefinition so
if (base.hasSliceName()) {
profile = base.getType().size() == 1 && base.getTypeFirstRep().hasProfile() ? context.fetchResource(StructureDefinition.class, base.getTypeFirstRep().getProfile().get(0).getValue(), srcSD) : null;
}
if (profile==null) {
profile = source.getType().size() == 1 && source.getTypeFirstRep().hasProfile() ? context.fetchResource(StructureDefinition.class, source.getTypeFirstRep().getProfile().get(0).getValue(), derivedSrc) : null;
if (profile == null && source.getTypeFirstRep().hasProfile()) {
String pu = source.getTypeFirstRep().getProfile().get(0).getValue();
profile = context.fetchResource(StructureDefinition.class, pu, derivedSrc);
if (profile == null) {
if (makeXVer().matchingUrl(pu)) {
switch (xver.status(pu)) {
case BadVersion:
throw new FHIRException("Reference to invalid version in extension url " + pu);
case Invalid:
throw new FHIRException("Reference to invalid extension " + pu);
case Unknown:
throw new FHIRException("Reference to unknown extension " + pu);
case Valid:
profile = xver.makeDefinition(pu);
generateSnapshot(context.fetchTypeDefinition("Extension"), profile, profile.getUrl(), context.getSpecUrl(), profile.getName());
}
}

}
if (profile != null && !"Extension".equals(profile.getType()) && profile.getKind() != StructureDefinitionKind.RESOURCE && profile.getKind() != StructureDefinitionKind.LOGICAL) {
// this is a problem - we're kind of hacking things here. The problem is that we sometimes want the details from the profile to override the
// inherited attributes, and sometimes not
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -695,7 +695,7 @@ private String loadJS(JsonElement e) {
}

protected String hashJson(String s) {
s = StringUtils.remove(s, ' ');
// s = StringUtils.remove(s, ' ');
s = StringUtils.remove(s, '\n');
s = StringUtils.remove(s, '\r');
return String.valueOf(s.hashCode());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12748,6 +12748,15 @@ public String typeSummary() {
}
return b.toString();
}

public List<String> typeList() {
List<String> res = new ArrayList<>();
for (TypeRefComponent tr : getType()) {
if (tr.hasCode())
res.add(tr.getWorkingCode());
}
return res;
}

public String typeSummaryVB() {
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder("|");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,16 +198,16 @@ private boolean generateExpansion(XhtmlNode x, ValueSet vs, boolean header, List
generateContentModeNotices(x, vs.getExpansion(), vs);
generateVersionNotice(x, vs.getExpansion(), vs);

CodeSystem allCS = null;
boolean doLevel = false;
for (ValueSetExpansionContainsComponent cc : vs.getExpansion().getContains()) {
if (cc.hasContains()) {
doLevel = true;
break;
}
}

boolean doInactive = checkDoInactive(vs.getExpansion().getContains());
boolean doDefinition = checkDoDefinition(vs.getExpansion().getContains());

XhtmlNode t = x.table( "codes");
XhtmlNode tr = t.tr();
if (doLevel)
Expand All @@ -221,6 +221,9 @@ private boolean generateExpansion(XhtmlNode x, ValueSet vs, boolean header, List
scanForDesignations(c, langs, designations);
}
scanForProperties(vs.getExpansion(), langs, properties);
if (doInactive) {
tr.td().b().tx("Inactive");
}
if (doDefinition) {
tr.td().b().tx("Definition");
doDesignations = false;
Expand Down Expand Up @@ -250,7 +253,7 @@ private boolean generateExpansion(XhtmlNode x, ValueSet vs, boolean header, List

addMapHeaders(tr, maps);
for (ValueSetExpansionContainsComponent c : vs.getExpansion().getContains()) {
addExpansionRowToTable(t, vs, c, 1, doLevel, true, doDefinition, maps, allCS, langs, designations, doDesignations, properties);
addExpansionRowToTable(t, vs, c, 1, doLevel, doDefinition, doInactive, maps, langs, designations, doDesignations, properties);
}

// now, build observed languages
Expand Down Expand Up @@ -673,6 +676,17 @@ private boolean checkDoDefinition(List<ValueSetExpansionContainsComponent> conta
return false;
}

private boolean checkDoInactive(List<ValueSetExpansionContainsComponent> contains) {
for (ValueSetExpansionContainsComponent c : contains) {
if (c.hasInactive()) {
return true;
}
if (checkDoInactive(c.getContains()))
return true;
}
return false;
}


private boolean allFromOneSystem(ValueSet vs) {
if (vs.getExpansion().getContains().isEmpty())
Expand Down Expand Up @@ -754,7 +768,7 @@ private void scanForLangs(ValueSetExpansionContainsComponent c, List<String> lan
}
}

private void addExpansionRowToTable(XhtmlNode t, ValueSet vs, ValueSetExpansionContainsComponent c, int i, boolean doLevel, boolean doSystem, boolean doDefinition, List<UsedConceptMap> maps, CodeSystem allCS, List<String> langs, Map<String, String> designations, boolean doDesignations, Map<String, String> properties) throws FHIRFormatError, DefinitionException, IOException {
private void addExpansionRowToTable(XhtmlNode t, ValueSet vs, ValueSetExpansionContainsComponent c, int i, boolean doLevel, boolean doDefinition, boolean doInactive, List<UsedConceptMap> maps, List<String> langs, Map<String, String> designations, boolean doDesignations, Map<String, String> properties) throws FHIRFormatError, DefinitionException, IOException {
XhtmlNode tr = t.tr();
if (ValueSetUtilities.isDeprecated(vs, c)) {
tr.setAttribute("style", "background-color: #ffeeee");
Expand All @@ -772,19 +786,21 @@ private void addExpansionRowToTable(XhtmlNode t, ValueSet vs, ValueSetExpansionC
String s = Utilities.padLeft("", '\u00A0', i*2);
td.attribute("style", "white-space:nowrap").addText(s);
addCodeToTable(c.getAbstract(), c.getSystem(), c.getCode(), c.getDisplay(), td);
if (doSystem) {
td = tr.td();
td.addText(c.getSystem());
}
td = tr.td();
td.addText(c.getSystem());
td = tr.td();
if (c.hasDisplayElement())
td.addText(c.getDisplay());

if (doInactive) {
td = tr.td();
if (c.getInactive()) {
td.tx("inactive");
}
}
if (doDefinition) {
CodeSystem cs = allCS;
if (cs == null)
cs = getContext().getWorker().fetchCodeSystem(c.getSystem());
td = tr.td();
CodeSystem cs = getContext().getWorker().fetchCodeSystem(c.getSystem());
if (cs != null) {
String defn = CodeSystemUtilities.getCodeDefinition(cs, c.getCode());
addMarkdown(td, defn, cs.getWebPath());
Expand Down Expand Up @@ -817,7 +833,7 @@ private void addExpansionRowToTable(XhtmlNode t, ValueSet vs, ValueSetExpansionC
addLangaugesToRow(c, langs, tr);
}
for (ValueSetExpansionContainsComponent cc : c.getContains()) {
addExpansionRowToTable(t, vs, cc, i+1, doLevel, doSystem, doDefinition, maps, allCS, langs, designations, doDesignations, properties);
addExpansionRowToTable(t, vs, cc, i+1, doLevel, doDefinition, doInactive, maps, langs, designations, doDesignations, properties);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,7 @@ private ValidationResult validateCode(String path, Coding code, CodeSystem cs, C
if (vcc != null) {
vcc.addCoding(vc);
}
boolean ws = false;
if (code.getDisplay() == null) {
return new ValidationResult(code.getSystem(), cs.getVersion(), cc, vc.getDisplay());
}
Expand All @@ -656,14 +657,20 @@ private ValidationResult validateCode(String path, Coding code, CodeSystem cs, C
b.append("'"+cc.getDisplay()+"'");
if (code.getDisplay().equalsIgnoreCase(cc.getDisplay())) {
return new ValidationResult(code.getSystem(), cs.getVersion(), cc, getPreferredDisplay(cc, cs));
} else if (Utilities.normalize(code.getDisplay()).equals(Utilities.normalize(cc.getDisplay()))) {
ws = true;
}
}

for (ConceptDefinitionDesignationComponent ds : cc.getDesignation()) {
if (isOkLanguage(ds.getLanguage())) {
b.append("'"+ds.getValue()+"'");
if (code.getDisplay().equalsIgnoreCase(ds.getValue())) {
return new ValidationResult(code.getSystem(),cs.getVersion(), cc, getPreferredDisplay(cc, cs));
}
if (Utilities.normalize(code.getDisplay()).equalsIgnoreCase(Utilities.normalize(ds.getValue()))) {
ws = true;
}
}
}
// also check to see if the value set has another display
Expand All @@ -690,7 +697,7 @@ private ValidationResult validateCode(String path, Coding code, CodeSystem cs, C
String msg = context.formatMessagePlural(options.getLanguages().size(), I18nConstants.NO_VALID_DISPLAY_FOUND, code.getSystem(), code.getCode(), code.getDisplay(), options.langSummary());
return new ValidationResult(IssueSeverity.WARNING, msg, code.getSystem(), cs.getVersion(), cc, getPreferredDisplay(cc, cs), makeIssue(IssueSeverity.WARNING, IssueType.INVALID, path+".display", msg));
} else {
String msg = context.formatMessagePlural(b.count(), I18nConstants.DISPLAY_NAME_FOR__SHOULD_BE_ONE_OF__INSTEAD_OF, code.getSystem(), code.getCode(), b.toString(), code.getDisplay(), options.langSummary());
String msg = context.formatMessagePlural(b.count(), ws ? I18nConstants.DISPLAY_NAME_WS_FOR__SHOULD_BE_ONE_OF__INSTEAD_OF : I18nConstants.DISPLAY_NAME_FOR__SHOULD_BE_ONE_OF__INSTEAD_OF, code.getSystem(), code.getCode(), b.toString(), code.getDisplay(), options.langSummary());
return new ValidationResult(dispWarningStatus(), msg, code.getSystem(), cs.getVersion(), cc, getPreferredDisplay(cc, cs), makeIssue(dispWarning(), IssueType.INVALID, path+".display", msg));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,4 +256,34 @@ private boolean isVersionPattern(String v) {
return v.length() == 3 && Character.isDigit(v.charAt(0)) && v.charAt(1) == '.' && Character.isDigit(v.charAt(2));
}

public String getReference(String url) {
String version = getVersion(url);
String base = VersionUtilities.getSpecUrl(version);
if (base == null) {
return null;
} else {
String path = url.substring(url.indexOf("-")+1);
if (!path.contains(".")) {
return null;
}
String type = path.substring(0, path.indexOf("."));
if (Utilities.existsInList(type, "Annotation", "Attachment", "Identifier", "CodeableConcept", "Coding", "Quantity", "Duration", "Range", "Period", "Ratio", "RatioRange", "SampledData", "Signature", "HumanName", "Address", "ContactPoint", "Timing")) {
return Utilities.pathURL(base, "datatypes-definitions.html#"+path+"|"+VersionUtilities.getNameForVersion(version)+" "+path);
}
if (Utilities.existsInList(type, "Element", "BackboneElement", "BackboneType", "PrimitiveType", "DataType", "Base")) {
return Utilities.pathURL(base, "types-definitions.html#"+path+"|"+VersionUtilities.getNameForVersion(version)+" "+path);
}
if (Utilities.existsInList(type, "UsageContext", "RelatedArtifact", "DataRequirement", "ParameterDefinition", "TriggerDefinition", "Expression", "ContactDetail", "ExtendedContactDetail", "VirtualServiceDetail", "Availability", "MonetaryComponent", "Contributor")) {
return Utilities.pathURL(base, "metadatatypes-definitions.html#"+path+"|"+VersionUtilities.getNameForVersion(version)+" "+path);
}
if (Utilities.existsInList(type, "Reference", "CodeableReference")) {
return Utilities.pathURL(base, "references-definitions.html#"+path+"|"+VersionUtilities.getNameForVersion(version)+" "+path);
}
if (Utilities.existsInList(type, "Meta")) {
return Utilities.pathURL(base, "resource-definitions.html#"+path+"|"+VersionUtilities.getNameForVersion(version)+" "+path);
}
return Utilities.pathURL(base, type.toLowerCase()+"-definitions.html#"+path+"|"+VersionUtilities.getNameForVersion(version)+" "+path);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ public void testCachePersistence() throws IOException, URISyntaxException {
assertCanonicalResourceEquals(terminologyCapabilities, terminologyCacheB.getTerminologyCapabilities());
assertCanonicalResourceEquals(capabilityStatement, terminologyCacheB.getCapabilityStatement());

assertValidationResultEquals(codingResultA, terminologyCacheB.getValidation( terminologyCacheA.generateValidationToken(CacheTestUtils.validationOptions, coding, valueSet, new Parameters())));
assertValidationResultEquals(codingResultA, terminologyCacheB.getValidation(terminologyCacheA.generateValidationToken(CacheTestUtils.validationOptions, coding, valueSet, new Parameters())));
assertValidationResultEquals(codeableConceptResultA, terminologyCacheB.getValidation(terminologyCacheA.generateValidationToken(CacheTestUtils.validationOptions, concept, valueSet, new Parameters())));
assertExpansionOutcomeEquals(expansionOutcomeA,terminologyCacheB.getExpansion(terminologyCacheA.generateExpandToken(valueSet, true)));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,42 @@ e: {
"error" : "Cannot invoke \"org.hl7.fhir.r5.terminologies.client.ITerminologyClient.expandValueset(org.hl7.fhir.r5.model.ValueSet, org.hl7.fhir.r5.model.Parameters, java.util.Map)\" because the return value of \"org.hl7.fhir.r5.terminologies.client.TerminologyClientContext.getClient()\" is null"
}
-------------------------------------------------------------------------------------
{"hierarchical" : false, "valueSet" :{
"resourceType" : "ValueSet",
"compose" : {
"inactive" : true,
"include" : [{
"system" : "urn:ietf:bcp:47"
}]
}
}}####
e: {
"error" : "Cannot invoke \"org.hl7.fhir.r5.terminologies.client.ITerminologyClient.expandValueset(org.hl7.fhir.r5.model.ValueSet, org.hl7.fhir.r5.model.Parameters, java.util.Map)\" because the return value of \"org.hl7.fhir.r5.terminologies.client.TerminologyClientContext.getClient()\" is null"
}
-------------------------------------------------------------------------------------
{"hierarchical" : false, "valueSet" :{
"resourceType" : "ValueSet",
"compose" : {
"inactive" : true,
"include" : [{
"system" : "urn:ietf:bcp:47"
}]
}
}}####
e: {
"error" : "Cannot invoke \"org.hl7.fhir.r5.terminologies.client.ITerminologyClient.expandValueset(org.hl7.fhir.r5.model.ValueSet, org.hl7.fhir.r5.model.Parameters, java.util.Map)\" because the return value of \"org.hl7.fhir.r5.terminologies.client.TerminologyClientContext.getClient()\" is null"
}
-------------------------------------------------------------------------------------
{"hierarchical" : false, "valueSet" :{
"resourceType" : "ValueSet",
"compose" : {
"inactive" : true,
"include" : [{
"system" : "urn:ietf:bcp:47"
}]
}
}}####
e: {
"error" : "Cannot invoke \"org.hl7.fhir.r5.terminologies.client.ITerminologyClient.expandValueset(org.hl7.fhir.r5.model.ValueSet, org.hl7.fhir.r5.model.Parameters, java.util.Map)\" because the return value of \"org.hl7.fhir.r5.terminologies.client.TerminologyClientContext.getClient()\" is null"
}
-------------------------------------------------------------------------------------
Loading

0 comments on commit 6d08834

Please sign in to comment.