Skip to content

Commit 142de5f

Browse files
Merge pull request #1251 from HL7/2026-02-gg-canonical-checks
2026 02 gg canonical checks
2 parents 4b9b5f2 + afa1b83 commit 142de5f

4 files changed

Lines changed: 155 additions & 53 deletions

File tree

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

Lines changed: 119 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -771,7 +771,74 @@ protected boolean checkCanonicalsForVersions(FetchedFile f, CanonicalResource bc
771771
} else {
772772
DataTypeVisitor dv = new DataTypeVisitor();
773773
dv.visit(bc, new PublisherBase.CanonicalVisitor<CanonicalType>(f, snapshotMode));
774-
return dv.isAnyTrue();
774+
boolean changed = false;
775+
if (bc instanceof ValueSet) {
776+
changed = checkValueSetVersions(f, (ValueSet) bc);
777+
}
778+
return dv.isAnyTrue() || changed;
779+
}
780+
}
781+
782+
private boolean checkValueSetVersions(FetchedFile f, ValueSet vs) {
783+
boolean changed = false;
784+
int i = 0;
785+
for (ValueSet.ConceptSetComponent inc : vs.getCompose().getInclude()) {
786+
changed = checkValueSetVersions(f, inc, "ValueSet.compose.include["+i+"]") || changed;
787+
i++;
788+
}
789+
for (ValueSet.ConceptSetComponent inc : vs.getCompose().getExclude()) {
790+
changed = checkValueSetVersions(f, inc, "ValueSet.compose.exclude["+i+"]") || changed;
791+
}
792+
return changed;
793+
}
794+
795+
private boolean checkValueSetVersions(FetchedFile f, ValueSet.ConceptSetComponent inc, String path) {
796+
String url = inc.getSystem();
797+
String version = inc.getVersion();
798+
if (version != null) {
799+
return false;
800+
}
801+
CanonicalResource tgt = (CanonicalResource) PublisherBase.this.pf.context.fetchResourceRaw(Resource.class, url);
802+
if (tgt instanceof CodeSystem) {
803+
CodeSystem cs = (CodeSystem) tgt;
804+
if (cs.getContent() == Enumerations.CodeSystemContentMode.NOTPRESENT && cs.hasSourcePackage() && cs.getSourcePackage().isTHO()) {
805+
// we ignore these definitions - their version is completely wrong for a start
806+
return false;
807+
}
808+
}
809+
if (tgt == null) {
810+
return false;
811+
}
812+
if (!tgt.hasVersion()) {
813+
return false;
814+
}
815+
if (PublisherBase.this.pf.pinningPolicy == PublisherUtils.PinningPolicy.FIX) {
816+
PublisherBase.this.pf.pinCount++;
817+
f.getErrors().add(new ValidationMessage(ValidationMessage.Source.Publisher, ValidationMessage.IssueType.PROCESSING, path, "Pinned the version of "+url+" to "+tgt.getVersion(),
818+
ValidationMessage.IssueSeverity.INFORMATION).setMessageId(PublisherMessageIds.PIN_VERSION));
819+
if (PublisherBase.this.pf.pinDest != null) {
820+
pinInManifest(tgt.fhirType(), url, tgt.getVersion());
821+
return false;
822+
} else {
823+
inc.setVersion(tgt.getVersion());
824+
return true;
825+
}
826+
} else {
827+
Map<String, String> lst = PublisherBase.this.pf.validationFetcher.fetchCanonicalResourceVersionMap(null, null, url);
828+
if (lst.size() < 2) {
829+
return false;
830+
} else {
831+
PublisherBase.this.pf.pinCount++;
832+
f.getErrors().add(new ValidationMessage(ValidationMessage.Source.Publisher, ValidationMessage.IssueType.PROCESSING, path, "Pinned the version of "+url+" to "+tgt.getVersion()+" from choices of "+stringify(",", lst),
833+
ValidationMessage.IssueSeverity.INFORMATION).setMessageId(PublisherMessageIds.PIN_VERSION));
834+
if (PublisherBase.this.pf.pinDest != null) {
835+
pinInManifest(tgt.fhirType(), url, tgt.getVersion());
836+
return false;
837+
} else {
838+
inc.setVersion(tgt.getVersion());
839+
return true;
840+
}
841+
}
775842
}
776843
}
777844

@@ -1294,10 +1361,11 @@ public boolean visit(String path, DataType node) {
12941361
}
12951362
if (PublisherBase.this.pf.pinDest != null) {
12961363
pinInManifest(tgt.fhirType(), url, tgt.getVersion());
1364+
return false;
12971365
} else {
12981366
ct.setValue(url+"|"+tgt.getVersion());
1367+
return true;
12991368
}
1300-
return true;
13011369
} else {
13021370
Map<String, String> lst = PublisherBase.this.pf.validationFetcher.fetchCanonicalResourceVersionMap(null, null, url);
13031371
if (lst.size() < 2) {
@@ -1310,67 +1378,68 @@ public boolean visit(String path, DataType node) {
13101378
}
13111379
if (PublisherBase.this.pf.pinDest != null) {
13121380
pinInManifest(tgt.fhirType(), url, tgt.getVersion());
1381+
return false;
13131382
} else {
13141383
ct.setValue(url+"|"+tgt.getVersion());
1384+
return true;
13151385
}
1316-
return true;
13171386
}
13181387
}
13191388
}
13201389

1321-
private void pinInManifest(String type, String url, String version) {
1322-
FetchedResource r = fetchByResource("Parameters", PublisherBase.this.pf.pinDest);
1323-
if (r == null) {
1324-
throw new Error("Unable to find nominated pin-manifest "+ PublisherBase.this.pf.pinDest);
1325-
}
1326-
Element p = r.getElement();
1327-
if (!p.hasUserData(UserDataNames.EXP_REVIEWED)) {
1328-
new ExpansionParameterUtilities(PublisherBase.this.pf.context).reviewVersions(p);
1329-
p.setUserData(UserDataNames.EXP_REVIEWED, true);
1330-
}
1331-
String pn = null;
1332-
switch (type) {
1333-
case "CodeSystem":
1334-
pn = "system-version";
1335-
break;
1336-
case "ValueSet":
1337-
pn = "default-valueset-version";
1338-
break;
1339-
default:
1340-
pn = "default-canonical-version";
1341-
}
1342-
String v = url+"|"+version;
1343-
boolean add = true;
1344-
for (Element t : p.getChildren("parameter")) {
1345-
String name = t.getNamedChildValue("name");
1346-
String value = t.getNamedChildValue("value");
1347-
if (name.equals(pn) && value.startsWith(url+"|")) {
1348-
if (!v.equals(value)) {
1349-
if (t.hasUserData(UserDataNames.auto_added_parameter)) {
1350-
throw new FHIRException("An error occurred building the version manifest: the IGPublisher wanted to add version "+version+" but had already added version "+value.substring(version.indexOf("|")+1));
1351-
} else {
1352-
throw new FHIRException("An error occurred building the version manifest: the IGPublisher wanted to add version "+version+" but found version "+value.substring(version.indexOf("|")+1)+" already specified");
1353-
}
1390+
}
1391+
1392+
private void pinInManifest(String type, String url, String version) {
1393+
FetchedResource r = fetchByResource("Parameters", PublisherBase.this.pf.pinDest);
1394+
if (r == null) {
1395+
throw new Error("Unable to find nominated pin-manifest "+ PublisherBase.this.pf.pinDest);
1396+
}
1397+
Element p = r.getElement();
1398+
if (!p.hasUserData(UserDataNames.EXP_REVIEWED)) {
1399+
new ExpansionParameterUtilities(PublisherBase.this.pf.context).reviewVersions(p);
1400+
p.setUserData(UserDataNames.EXP_REVIEWED, true);
1401+
}
1402+
String pn = null;
1403+
switch (type) {
1404+
case "CodeSystem":
1405+
pn = "system-version";
1406+
break;
1407+
case "ValueSet":
1408+
pn = "default-valueset-version";
1409+
break;
1410+
default:
1411+
pn = "default-canonical-version";
1412+
}
1413+
String v = url+"|"+version;
1414+
boolean add = true;
1415+
for (Element t : p.getChildren("parameter")) {
1416+
String name = t.getNamedChildValue("name");
1417+
String value = t.getNamedChildValue("value");
1418+
if (name.equals(pn) && value.startsWith(url+"|")) {
1419+
if (!v.equals(value)) {
1420+
if (t.hasUserData(UserDataNames.auto_added_parameter)) {
1421+
throw new FHIRException("An error occurred building the version manifest: the IGPublisher wanted to add version "+version+" but had already added version "+value.substring(version.indexOf("|")+1));
1422+
} else {
1423+
throw new FHIRException("An error occurred building the version manifest: the IGPublisher wanted to add version "+version+" but found version "+value.substring(version.indexOf("|")+1)+" already specified");
13541424
}
1355-
add = false;
13561425
}
1357-
}
1358-
if (add) {
1359-
Element pp = p.addElement("parameter");
1360-
pp.setChildValue("name", pn);
1361-
pp.setChildValue("valueUri", v);
1362-
pp.setUserData(UserDataNames.auto_added_parameter, true);
1426+
add = false;
13631427
}
13641428
}
1365-
1366-
private String stringify(String string, Map<String, String> lst) {
1367-
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
1368-
for (String s : Utilities.sorted(lst.keySet())) {
1369-
b.append(s+" ("+lst.get(s)+")");
1370-
}
1371-
return b.toString();
1429+
if (add) {
1430+
Element pp = p.addElement("parameter");
1431+
pp.setChildValue("name", pn);
1432+
pp.setChildValue("valueUri", v);
1433+
pp.setUserData(UserDataNames.auto_added_parameter, true);
13721434
}
13731435
}
13741436

1437+
private String stringify(String string, Map<String, String> lst) {
1438+
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
1439+
for (String s : Utilities.sorted(lst.keySet())) {
1440+
b.append(s+" ("+lst.get(s)+")");
1441+
}
1442+
return b.toString();
1443+
}
13751444

13761445
}

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -950,6 +950,9 @@ else if (pf.vsCache == null) {
950950
pf.context.getExpansionParameters().addParameter(n, expParamMap.get(n));
951951
}
952952

953+
if (!pf.sourceIg.hasUrl()) {
954+
throw new FHIRException("Unable to publish an IG without a URL");
955+
}
953956
settings.setNewMultiLangTemplateFormat(pf.template.config().asBoolean("multilanguage-format"));
954957
loadPubPack();
955958
pf.igpkp = new IGKnowledgeProvider(pf.context, checkAppendSlash(pf.specPath), determineCanonical(pf.sourceIg.getUrl(), "ImplementationGuide.url"), pf.template.config(), pf.errors, VersionUtilities.isR2Ver(pf.version), pf.template, pf.listedURLExemptions, pf.altCanonical, pf.fileList, pf.module);
@@ -1096,7 +1099,7 @@ else if (pf.vsCache == null) {
10961099
pf.validator.getExtensionDomains().addAll(extensionDomains);
10971100
pf.validator.setNoExperimentalContent(pf.noExperimentalContent);
10981101
pf.validator.getExtensionDomains().add(ExtensionDefinitions.EXT_PRIVATE_BASE);
1099-
pf.validationFetcher = new ValidationServices(pf.context, pf.igpkp, pf.sourceIg, pf.fileList, pf.npmList, pf.bundleReferencesResolve, pf.specMaps, pf.linkSpecMaps, pf.module);
1102+
pf.validationFetcher = new ValidationServices(pf.context, pf.igpkp, pf.sourceIg, pf.fileList, pf.npmList, pf.bundleReferencesResolve, pf.specMaps, pf.linkSpecMaps, pf.module, allLangs());
11001103
pf.validator.setFetcher(pf.validationFetcher);
11011104
pf.validator.setPolicyAdvisor(pf.validationFetcher);
11021105
pf.validator.getSettings().setR5BundleRelativeReferencePolicy(r5BundleRelativeReferencePolicy);

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

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ public class ValidationServices implements IValidatorResourceFetcher, IValidatio
9292
private List<NpmPackage> packages;
9393
private Set<String> otherUrls = new HashSet<>();
9494
private List<String> mappingUrls = new ArrayList<>();
95+
private List<String> allLangs = new ArrayList<>();
9596
private boolean bundleReferencesResolve;
9697
private List<SpecMapManager> specMaps;
9798
private List<PublisherUtils.LinkedSpecification> linkSpecMaps;
@@ -100,7 +101,7 @@ public class ValidationServices implements IValidatorResourceFetcher, IValidatio
100101

101102

102103
public ValidationServices(IWorkerContext context, IGKnowledgeProvider ipg, ImplementationGuide ig, List<FetchedFile> files, List<NpmPackage> packages,
103-
boolean bundleReferencesResolve, List<SpecMapManager> specMaps, List<PublisherUtils.LinkedSpecification> linkSpecMaps, IPublisherModule module) {
104+
boolean bundleReferencesResolve, List<SpecMapManager> specMaps, List<PublisherUtils.LinkedSpecification> linkSpecMaps, IPublisherModule module, List<String> allLangs) {
104105
super();
105106
this.context = context;
106107
this.ipg = ipg;
@@ -111,6 +112,7 @@ public ValidationServices(IWorkerContext context, IGKnowledgeProvider ipg, Imple
111112
this.specMaps = specMaps;
112113
this.linkSpecMaps = linkSpecMaps;
113114
this.module = module;
115+
this.allLangs = allLangs;
114116
initOtherUrls();
115117
}
116118

@@ -443,6 +445,34 @@ public boolean resolveURL(IResourceValidator validator, Object appContext, Strin
443445
return true;
444446
}
445447
}
448+
if ("url".equals(type) && !Utilities.isAbsoluteUrl(url)) {
449+
// well, it might be a reference to a page in the specification, so we should check that.
450+
if (matchesPage(url)) {
451+
return true;
452+
}
453+
}
454+
return false;
455+
}
456+
457+
private boolean matchesPage(String url) {
458+
if (url.contains("#")) {
459+
url = url.substring(0, url.indexOf("#"));
460+
}
461+
for (FetchedFile f : files) {
462+
for (FetchedResource r : f.getResources()) {
463+
String p = ipg.getLinkFor(r, true);
464+
if (p != null && p.equals(url)) {
465+
return true;
466+
}
467+
if (p != null) {
468+
for (String lang : allLangs) {
469+
if ((lang + "/" + p).equals(url)) {
470+
return true;
471+
}
472+
}
473+
}
474+
}
475+
}
446476
return false;
447477
}
448478

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<packaging>pom</packaging>
99

1010
<properties>
11-
<core_version>6.8.0</core_version>
11+
<core_version>6.8.1-SNAPSHOT</core_version>
1212
<maven_surefire_version>3.0.0-M5</maven_surefire_version>
1313
<apache_poi_version>5.4.1</apache_poi_version>
1414
<commons_io_version>2.17.0</commons_io_version>

0 commit comments

Comments
 (0)