Skip to content

Commit 6287a6b

Browse files
Merge pull request #1077 from HL7/2025-04-gg-misc-fixes
2025 04 gg misc fixes
2 parents bad39f1 + 0e0c878 commit 6287a6b

12 files changed

Lines changed: 591 additions & 41 deletions

File tree

RELEASE_NOTES.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
## Validator Changes
2+
3+
* Loader: Don't pin ImplementationGuide.dependsOn references
4+
* Loader: don't raise errors for URL duplications where one resource is retired
5+
* Loader: upgrade tools IG to 0.5.0
6+
* Loader: Fix bug where examples in other packages not being found on windows
7+
* Template System: Add additional-paths to resources.json
8+
* Terminology Sub-system: Add support for filter by regex on property value in ValueSet definitions
9+
* Snapshot Generator: Fix discovered issues related to snapshot generation with slicing while investing how slicing works per extensive committee discussion
10+
* Validator: Cross-check canonical version when there's an applicable version element when validating `ImplemenationGuide.dependsOn` or `ValueSet.compose.include`
11+
* Validator: OperationDefinition: Cross check Operation Parameters with inputProfile and outputProfile
12+
* Validator: Add check for consistency across slices for types and must-support when validating profiles
13+
* Validator: more validation url resolution exemptions
14+
* Validator: Remove superfluous spaces validating ValueSet parameters in Questionnaire
15+
* Validator: Exempt `tel:` urls from being resolved in validator
16+
* Validator: More URL validation improvements including checking the OID registry
17+
* Validator: Fix source location tracking for FHIRPath expressions
18+
* Validator: Add support for enforcing R5 policy on relative references in Bundle entries
19+
* Validator: Add profile.compliesWith validation
20+
* Validator: Fix validation issues around ImplementationGuide.dependsOn canonical and package
21+
* Validator: Add support for sort in FHIRPath
22+
* Renderer: Obligation Rendering improvements
23+
* Renderer: Don't produce links in narrative when they are definitely broken
24+
* Renderer: Add obligation-summary fragment
25+
* Renderer: add oid based expansion fragment
26+
* Package Generator: Handle version conversion failure gracefully
27+
* QA: Improved rendering of sub-issues for compliesWith validation
28+
* General: Fix various NullPointerErrors
29+
* General: Bump apache poi and xml-beans versions

org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/FetchedFile.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ public enum FetchedBundleType {
7272
private int processMode;
7373
private Set<String> outputNames = new HashSet<String>();
7474
private String statedPath;
75+
private List<String> additionalPaths;
7576
private String logical;
7677
private List<ProcessingReport> processes = new ArrayList<>();
7778
private boolean loaded;
@@ -295,5 +296,17 @@ public boolean isLoaded() {
295296
public void setLoaded(boolean loaded) {
296297
this.loaded = loaded;
297298
}
299+
public boolean hasAdditionalPaths() {
300+
return additionalPaths != null && !additionalPaths.isEmpty();
301+
}
302+
public List<String> getAdditionalPaths() {
303+
return additionalPaths;
304+
}
305+
public void addAdditionalPath(String additionalPath) {
306+
if (additionalPaths == null) {
307+
additionalPaths = new ArrayList<String>();
308+
}
309+
this.additionalPaths.add(additionalPath);
310+
}
298311

299312
}

org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/Publisher.java

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,7 @@
379379
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
380380
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
381381
import org.hl7.fhir.utilities.validation.ValidationMessage.Source;
382+
import org.hl7.fhir.utilities.validation.ValidationOptions.R5BundleRelativeReferencePolicy;
382383
import org.hl7.fhir.utilities.validation.ValidationOptions;
383384
import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator;
384385
import org.hl7.fhir.utilities.xhtml.NodeType;
@@ -460,7 +461,7 @@ public class Publisher implements ILoggingService, IReferenceResolver, IValidati
460461

461462
public enum PinningPolicy {NO_CHANGE, FIX, WHEN_MULTIPLE_CHOICES }
462463

463-
private static final String TOOLING_IG_CURRENT_RELEASE = "0.4.1";
464+
private static final String TOOLING_IG_CURRENT_RELEASE = "0.5.0";
464465

465466
public class FragmentUseRecord {
466467

@@ -2906,6 +2907,7 @@ private void initializeFromIg(IniFile ini) throws Exception {
29062907
tempLangDir = Utilities.path(rootDir, "translations");
29072908
outputDir = Utilities.path(rootDir, "output");
29082909
List<String> relatedIGParams = new ArrayList<>();
2910+
R5BundleRelativeReferencePolicy r5BundleRelativeReferencePolicy = R5BundleRelativeReferencePolicy.DEFAULT;
29092911

29102912
Map<String, String> expParamMap = new HashMap<>();
29112913
boolean allowExtensibleWarnings = false;
@@ -3302,6 +3304,8 @@ else if (p.getValue().equals("no-experimental-content"))
33023304
throw new FHIRException("Unknown value for 'pin-canonicals' of '"+p.getValue()+"'");
33033305
}
33043306
break;
3307+
case "r5-bundle-relative-reference-policy" :
3308+
r5BundleRelativeReferencePolicy = R5BundleRelativeReferencePolicy.fromCode(p.getValue());
33053309
default:
33063310
if (!template.isParameter(pc)) {
33073311
unknownParams.add(pc+"="+p.getValue());
@@ -3577,6 +3581,8 @@ else if (vsCache == null) {
35773581
validator.setFetcher(validationFetcher);
35783582
validator.setPolicyAdvisor(validationFetcher);
35793583
validator.setTracker(this);
3584+
validator.getSettings().setR5BundleRelativeReferencePolicy(r5BundleRelativeReferencePolicy);
3585+
35803586
for (String s : context.getBinaryKeysAsSet()) {
35813587
if (needFile(s)) {
35823588
if (makeQA)
@@ -7400,6 +7406,9 @@ public boolean visit(String path, DataType node) {
74007406
if (!tgt.hasVersion()) {
74017407
return false;
74027408
}
7409+
if (Utilities.startsWithInList(path, "ImplementationGuide.dependsOn")) {
7410+
return false;
7411+
}
74037412
if (pinningPolicy == PinningPolicy.FIX) {
74047413
if (!snapshotMode) {
74057414
pinCount++;
@@ -7436,11 +7445,13 @@ private String stringify(String string, Map<String, String> lst) {
74367445
private boolean checkCanonicalsForVersions(FetchedFile f, CanonicalResource bc, boolean snapshotMode) {
74377446
if (pinningPolicy == PinningPolicy.NO_CHANGE) {
74387447
return false;
7448+
// } else if ("ImplementationGuide".equals(bc.fhirType())) {
7449+
// return false;
74397450
} else {
74407451
DataTypeVisitor dv = new DataTypeVisitor();
74417452
dv.visit(bc, new CanonicalVisitor<CanonicalType>(f, snapshotMode));
74427453
return dv.isAnyTrue();
7443-
}
7454+
}
74447455
}
74457456

74467457
private String getOid(String type, String id) {
@@ -7866,6 +7877,7 @@ private void generateSnapshot(FetchedFile f, FetchedResource r, StructureDefinit
78667877
r.setElement(convertToElement(r, sd));
78677878
}
78687879
r.getElement().setUserData(UserDataNames.SNAPSHOT_ERRORS, messages);
7880+
r.getElement().setUserData(UserDataNames.SNAPSHOT_DETAILS, sd.getSnapshot());
78697881
f.getErrors().addAll(messages);
78707882
r.setSnapshotted(true);
78717883
logDebugMessage(LogCategory.CONTEXT, "Context.See "+sd.getUrl());
@@ -8203,14 +8215,18 @@ private void checkURLsUnique() {
82038215
try {
82048216
for (FetchedResource r : f.getResources()) {
82058217
if (r.getResource() != null && r.getResource() instanceof CanonicalResource) {
8206-
String url = ((CanonicalResource) r.getResource()).getUrl();
8218+
CanonicalResource cr = (CanonicalResource) r.getResource();
8219+
String url = cr.getUrl();
82078220
if (url != null) {
82088221
if (urls.containsKey(url)) {
82098222
FetchedResource rs = urls.get(url);
8210-
FetchedFile fs = findFileForResource(rs);
8211-
boolean local = url.startsWith(igpkp.getCanonical());
8212-
f.getErrors().add(new ValidationMessage(Source.Publisher, IssueType.BUSINESSRULE, "Resource", "The URL '"+url+"' has already been used by "+rs.getId()+" in "+fs.getName(), local ? IssueSeverity.ERROR : IssueSeverity.WARNING));
8213-
fs.getErrors().add(new ValidationMessage(Source.Publisher, IssueType.BUSINESSRULE, "Resource", "The URL '"+url+"' is also used by "+r.getId()+" in "+f.getName(), local ? IssueSeverity.ERROR : IssueSeverity.WARNING));
8223+
CanonicalResource crs = (CanonicalResource) rs.getResource();
8224+
if (!(crs.getStatus() == PublicationStatus.RETIRED || cr.getStatus() == PublicationStatus.RETIRED)) {
8225+
FetchedFile fs = findFileForResource(rs);
8226+
boolean local = url.startsWith(igpkp.getCanonical());
8227+
f.getErrors().add(new ValidationMessage(Source.Publisher, IssueType.BUSINESSRULE, "Resource", "The URL '"+url+"' has already been used by "+rs.getId()+" in "+fs.getName(), local ? IssueSeverity.ERROR : IssueSeverity.WARNING));
8228+
fs.getErrors().add(new ValidationMessage(Source.Publisher, IssueType.BUSINESSRULE, "Resource", "The URL '"+url+"' is also used by "+r.getId()+" in "+f.getName(), local ? IssueSeverity.ERROR : IssueSeverity.WARNING));
8229+
}
82148230
} else {
82158231
urls.put(url, r);
82168232
}
@@ -9718,6 +9734,8 @@ private void generateSummaryOutputs(DBBuilder db) throws Exception {
97189734
fragment("codesystem-ref-all-list", cvr.renderCSList(versionToAnnotate, cslist, cvr.needVersionReferences(vslist, publishedIg.getVersion()), true), otherFilesRun, start, "codesystem-ref-all-list", "Cross");
97199735
saveCSList("codesystem-ref-all-list", cslist, db, 3);
97209736

9737+
fragment("obligation-summary", cvr.renderObligationSummary(), otherFilesRun, System.currentTimeMillis(), "obligation-summary", "Cross");
9738+
97219739
for (String v : generateVersions) {
97229740
for (String n : context.getResourceNames()) {
97239741
fragment("version-"+v+"-summary-"+n, generateVersionSummary(v, n), otherFilesRun, start, "version-"+v+"-summary-"+n, "Cross");
@@ -9889,7 +9907,6 @@ private void generateSummaryOutputs(DBBuilder db) throws Exception {
98899907
addTranslationsToJson(item, "copyright", q.getCopyrightElement(), false);
98909908
item.add("description", ProfileUtilities.processRelativeUrls(q.getDescription(), "", igpkp.specPath(), context.getResourceNames(), specMaps.get(0).listTargets(), pageTargets(), false));
98919909
addTranslationsToJson(item, "description", q.getDescriptionElement(), true);
9892-
98939910
i++;
98949911
}
98959912
}
@@ -9911,6 +9928,15 @@ private void generateSummaryOutputs(DBBuilder db) throws Exception {
99119928
item.add("index", i);
99129929
item.add("source", f.getStatedPath());
99139930
item.add("sourceTail", tailPI(f.getStatedPath()));
9931+
if (f.hasAdditionalPaths()) {
9932+
JsonArray adp = item.forceArray("additional-paths");
9933+
for (String s : f.getAdditionalPaths()) {
9934+
JsonObject p = new JsonObject();
9935+
adp.add(p);
9936+
p.add("source", s);
9937+
p.add("sourceTail", tailPI(s));
9938+
}
9939+
}
99149940
path = null;
99159941
if (r.getPath() != null) {
99169942
path = r.getPath();
@@ -13185,7 +13211,16 @@ private byte[] saveNativeResourceOutputs(FetchedFile f, FetchedResource r) throw
1318513211
String ver = VersionUtilities.versionFromCode(v);
1318613212
Resource res = r.hasOtherVersions() && r.getOtherVersions().containsKey(ver+"-"+r.fhirType()) ? r.getOtherVersions().get(ver+"-"+r.fhirType()).getResource() : r.getResource();
1318713213
if (res != null) {
13188-
vnpms.get(v).addFile(isExample(f,r ) ? Category.EXAMPLE : Category.RESOURCE, element.fhirTypeRoot()+"-"+r.getId()+".json", convVersion(res.copy(), ver));
13214+
byte[] resVer = null;
13215+
try {
13216+
resVer = convVersion(res.copy(), ver);
13217+
} catch (Exception e) {
13218+
System.out.println("Unable to convert "+res.fhirType()+"/"+res.getId()+" to "+ver+": "+e.getMessage());
13219+
resVer = null;
13220+
}
13221+
if (resVer != null) {
13222+
vnpms.get(v).addFile(isExample(f,r ) ? Category.EXAMPLE : Category.RESOURCE, element.fhirTypeRoot()+"-"+r.getId()+".json", resVer);
13223+
}
1318913224
}
1319013225
}
1319113226
if (r.getResource() != null && r.getResource().hasUserData(UserDataNames.archetypeSource)) {
@@ -13882,7 +13917,7 @@ private void generateOutputsValueSet(FetchedFile f, FetchedResource r, ValueSet
1388213917
String html = "<p style=\"color: maroon\">Expansions are not generated for retired value sets</p>";
1388313918

1388413919
fragment("ValueSet-"+prefixForContainer+vs.getId()+"-expansion", html, f.getOutputNames(), r, vars, null, start, "expansion", "ValueSet");
13885-
} else {
13920+
} else {
1388613921
ValueSetExpansionOutcome exp = context.expandVS(vs, true, true, true);
1388713922

1388813923
db.recordExpansion(vs, exp);
@@ -13894,6 +13929,10 @@ private void generateOutputsValueSet(FetchedFile f, FetchedResource r, ValueSet
1389413929
RendererFactory.factory(exp.getValueset(), elrc).renderResource(ResourceWrapper.forResource(elrc, exp.getValueset()));
1389513930
String html = new XhtmlComposer(XhtmlComposer.XML).compose(exp.getValueset().getText().getDiv());
1389613931
fragment("ValueSet-"+prefixForContainer+vs.getId()+"-expansion"+langSfx, html, f.getOutputNames(), r, vars, null, start, "expansion", "ValueSet");
13932+
elrc = elrc.withOids(true);
13933+
XhtmlNode node = RendererFactory.factory(exp.getValueset(), elrc).buildNarrative(ResourceWrapper.forResource(elrc, exp.getValueset()));
13934+
html = new XhtmlComposer(XhtmlComposer.XML).compose(node);
13935+
fragment("ValueSet-"+prefixForContainer+vs.getId()+"-expansion-oids"+langSfx, html, f.getOutputNames(), r, vars, null, start, "expansion", "ValueSet");
1389713936
if (ValueSetUtilities.isIncompleteExpansion(exp.getValueset())) {
1389813937
f.getErrors().add(new ValidationMessage(Source.TerminologyEngine, IssueType.INFORMATIONAL, "ValueSet.where(id = '"+vs.getId()+"')", "The value set expansion is too large, and only a subset has been displayed", IssueSeverity.INFORMATION).setTxLink(exp.getTxLink()));
1389913938
}
@@ -14157,6 +14196,11 @@ private void generateOutputsStructureDefinition(FetchedFile f, FetchedResource r
1415714196
fragment("StructureDefinition-"+prefixForContainer+sd.getId()+"-snapshot-obligations"+langSfx, sdr.snapshot(igpkp.getDefinitionsName(r), otherFilesRun, tabbedSnapshots, StructureDefinitionRendererMode.OBLIGATIONS, false), f.getOutputNames(), r, vars, null, start, "snapshot-obligations", "StructureDefinition");
1415814197
fragment("StructureDefinition-"+prefixForContainer+sd.getId()+"-snapshot-obligations-all"+langSfx, sdr.snapshot(igpkp.getDefinitionsName(r), otherFilesRun, tabbedSnapshots, StructureDefinitionRendererMode.OBLIGATIONS, true), f.getOutputNames(), r, vars, null, start, "snapshot-obligations", "StructureDefinition");
1415914198
}
14199+
if (igpkp.wantGen(r, "obligations")) {
14200+
long start = System.currentTimeMillis();
14201+
fragment("StructureDefinition-"+prefixForContainer+sd.getId()+"-obligations"+langSfx, sdr.obligations(igpkp.getDefinitionsName(r), otherFilesRun, tabbedSnapshots, StructureDefinitionRendererMode.OBLIGATIONS, false), f.getOutputNames(), r, vars, null, start, "diff-obligations", "StructureDefinition");
14202+
fragment("StructureDefinition-"+prefixForContainer+sd.getId()+"-obligations-all"+langSfx, sdr.obligations(igpkp.getDefinitionsName(r), otherFilesRun, tabbedSnapshots, StructureDefinitionRendererMode.OBLIGATIONS, true), f.getOutputNames(), r, vars, null, start, "diff-obligations", "StructureDefinition");
14203+
}
1416014204
if (igpkp.wantGen(r, "snapshot-by-key-obligations")) {
1416114205
long start = System.currentTimeMillis();
1416214206
fragment("StructureDefinition-"+prefixForContainer+sd.getId()+"-snapshot-by-key-obligations"+langSfx, sdr.byKey(igpkp.getDefinitionsName(r), otherFilesRun, tabbedSnapshots, StructureDefinitionRendererMode.OBLIGATIONS, false), f.getOutputNames(), r, vars, null, start, "snapshot-by-key-obligations", "StructureDefinition");
@@ -14676,6 +14720,7 @@ private String wrapLiquid(String content) {
1467614720
private String pageWrap(String content, String title) {
1467714721
return "<html>\r\n"+
1467814722
"<head>\r\n"+
14723+
" <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>\r\n"+
1467914724
" <title>"+title+"</title>\r\n"+
1468014725
" <link rel=\"stylesheet\" href=\"fhir.css\"/>\r\n"+
1468114726
"</head>\r\n"+

org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/ValidationServices.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -484,7 +484,7 @@ public Set<String> fetchCanonicalResourceVersions(IResourceValidator validator,
484484
if (r instanceof CanonicalResource) {
485485

486486
CanonicalResource cr = (CanonicalResource) r;
487-
if (cr.getUrl().contains("terminology.hl7.org") && cr.getSourcePackage().isCore()) {
487+
if (cr.getUrl().contains("terminology.hl7.org") && cr.getSourcePackage() != null && cr.getSourcePackage().isCore()) {
488488
continue;
489489
}
490490
if (cr instanceof CodeSystem) {

org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/loaders/AdjunctFileLoader.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,6 @@ public String getPath() {
6666
public Attachment getAttachment() {
6767
return attachment;
6868
}
69-
70-
7169
}
7270

7371

@@ -86,6 +84,7 @@ public boolean replaceAttachments2(FetchedFile f, FetchedResource r) {
8684
if (fn != null) {
8785
Attachment a;
8886
try {
87+
f.addAdditionalPath(fn);
8988
a = loadFile(fn);
9089
res = true;
9190
att.getAttachment().setId(null);

0 commit comments

Comments
 (0)