Merge branch 'master' into do-20240909-package-lock-cleanup

This commit is contained in:
dotasek 2024-09-18 09:29:46 -04:00
commit 3d6650399c
26 changed files with 128 additions and 87 deletions

View File

@ -1,18 +1,7 @@
## Validator Changes
* Fix expression for con-3 properly (fix validation problem on some condition resources)
* Fix FHIRPath bug using wrong type on simple elements when checking FHIRPath types
* FHIRPath: Allow _ in constant names (per FHIRPath spec)
* Fix value set rendering creating wrong references
* Fix bug processing value set includes / excludes that are just value sets (no system value)
* Alter processing of unknown code systems per discussion at ,https://chat.fhir.org/#narrow/stream/179252-IG-creation/topic/Don't.20error.20when.20you.20can't.20find.20code.20system and implement unknown-codesystems-cause-errors
* Improve message for when elements are out of order in profile differentials
* no changes
## Other code changes
* fix problem where profile rendering had spurious 'slices for' nodes everywhere
* Update SQL-On-FHIR implementation for latest cases, and clone test cases to general test care repository
* Fix problem generating value set spreadsheets
* fix concurrent modification error processing language translations
* Check for null fetcher processing ConceptMaps (#1728)
* no changes

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -1,5 +1,5 @@
Locale,Coverage #,Coverage %
de,831,41%
de,831,40%
es,714,35%
ja,902,44%
nl,1989,98%

1 Locale Coverage # Coverage %
2 de 831 41% 40%
3 es 714 35%
4 ja 902 44%
5 nl 1989 98%

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId>
<version>6.3.26-SNAPSHOT</version>
<version>6.3.27-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId>
<version>6.3.26-SNAPSHOT</version>
<version>6.3.27-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId>
<version>6.3.26-SNAPSHOT</version>
<version>6.3.27-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId>
<version>6.3.26-SNAPSHOT</version>
<version>6.3.27-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId>
<version>6.3.26-SNAPSHOT</version>
<version>6.3.27-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId>
<version>6.3.26-SNAPSHOT</version>
<version>6.3.27-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId>
<version>6.3.26-SNAPSHOT</version>
<version>6.3.27-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -413,11 +413,10 @@ public class ProfileUtilities {
// note that ProfileUtilities are used re-entrantly internally, so nothing with process state can be here
private final IWorkerContext context;
private FHIRPathEngine fpe;
private List<ValidationMessage> messages;
private List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
private List<String> snapshotStack = new ArrayList<String>();
private ProfileKnowledgeProvider pkp;
// private boolean igmode;
private boolean exception;
private ValidationOptions terminologyServiceOptions = new ValidationOptions(FhirPublication.R5);
private boolean newSlicingProcessing;
private String defWebRoot;
@ -431,11 +430,16 @@ public class ProfileUtilities {
private MappingMergeModeOption mappingMergeMode = MappingMergeModeOption.APPEND;
private boolean forPublication;
private List<StructureDefinition> obligationProfiles = new ArrayList<>();
private boolean wantThrowExceptions;
public ProfileUtilities(IWorkerContext context, List<ValidationMessage> messages, ProfileKnowledgeProvider pkp, FHIRPathEngine fpe) {
super();
this.context = context;
this.messages = messages;
if (messages != null) {
this.messages = messages;
} else {
wantThrowExceptions = true;
}
this.pkp = pkp;
this.fpe = fpe;
@ -447,7 +451,11 @@ public class ProfileUtilities {
public ProfileUtilities(IWorkerContext context, List<ValidationMessage> messages, ProfileKnowledgeProvider pkp) {
super();
this.context = context;
this.messages = messages;
if (messages != null) {
this.messages = messages;
} else {
wantThrowExceptions = true;
}
this.pkp = pkp;
if (context != null) {
this.fpe = new FHIRPathEngine(context, this);
@ -789,7 +797,7 @@ public class ProfileUtilities {
ce++;
if (e.hasId()) {
String msg = "No match found for "+e.getId()+" in the generated snapshot: check that the path and definitions are legal in the differential (including order)";
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, "StructureDefinition.differential.element["+i+"]", msg, ValidationMessage.IssueSeverity.ERROR));
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, "StructureDefinition.differential.element["+i+"]", msg, ValidationMessage.IssueSeverity.ERROR));
}
}
i++;
@ -862,19 +870,19 @@ public class ProfileUtilities {
slice.getFocus().setMin(count);
} else {
String msg = "The slice definition for "+slice.getFocus().getId()+" has a minimum of "+slice.getFocus().getMin()+" but the slices add up to a minimum of "+count;
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE,
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE,
"StructureDefinition.snapshot.element["+slice.getIndex()+"]", msg, forPublication ? ValidationMessage.IssueSeverity.ERROR : ValidationMessage.IssueSeverity.INFORMATION).setIgnorableError(true));
}
}
count = slice.checkMax();
if (count > -1 && repeats) {
String msg = "The slice definition for "+slice.getFocus().getId()+" has a maximum of "+slice.getFocus().getMax()+" but the slices add up to a maximum of "+count+". Check that this is what is intended";
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE,
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE,
"StructureDefinition.snapshot.element["+slice.getIndex()+"]", msg, ValidationMessage.IssueSeverity.INFORMATION));
}
if (!slice.checkMinMax()) {
String msg = "The slice definition for "+slice.getFocus().getId()+" has a maximum of "+slice.getFocus().getMax()+" which is less than the minimum of "+slice.getFocus().getMin();
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE,
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE,
"StructureDefinition.snapshot.element["+slice.getIndex()+"]", msg, ValidationMessage.IssueSeverity.WARNING));
}
slices.remove(s);
@ -885,13 +893,13 @@ public class ProfileUtilities {
}
if (ed.hasSliceName() && !slices.containsKey(ed.getPath())) {
String msg = "The element "+ed.getId()+" launches straight into slicing without the slicing being set up properly first";
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE,
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE,
"StructureDefinition.snapshot.element["+i+"]", msg, ValidationMessage.IssueSeverity.ERROR).setIgnorableError(true));
}
if (ed.hasSliceName() && slices.containsKey(ed.getPath())) {
if (!slices.get(ed.getPath()).count(ed, ed.getSliceName())) {
String msg = "Duplicate slice name "+ed.getSliceName()+" on "+ed.getId()+" (["+i+"])";
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE,
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE,
"StructureDefinition.snapshot.element["+i+"]", msg, ValidationMessage.IssueSeverity.ERROR).setIgnorableError(true));
}
}
@ -910,10 +918,8 @@ public class ProfileUtilities {
}
}
if (sd == null) {
if (messages != null) {
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE,
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE,
"StructureDefinition.snapshot.element["+i+"]", "The type of profile "+u.getValue()+" cannot be checked as the profile is not known", IssueSeverity.WARNING));
}
} else {
String wt = t.getWorkingCode();
if (ed.getPath().equals("Bundle.entry.response.outcome")) {
@ -1012,13 +1018,15 @@ public class ProfileUtilities {
}
private void handleError(String url, String msg) {
if (exception)
throw new DefinitionException(msg);
else
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url, msg, ValidationMessage.IssueSeverity.ERROR));
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url, msg, ValidationMessage.IssueSeverity.ERROR));
}
private void addMessage(ValidationMessage msg) {
messages.add(msg);
if (msg.getLevel() == IssueSeverity.ERROR && wantThrowExceptions) {
throw new DefinitionException(msg.getMessage());
}
}
private void copyInheritedExtensions(StructureDefinition base, StructureDefinition derived, String webUrl) {
@ -2249,7 +2257,7 @@ public class ProfileUtilities {
* Not sure we have enough information here to do the check properly. Might be better done when we're sorting the profile?
if (i != start && result.isEmpty() && !path.startsWith(context.getElement().get(start).getPath()))
messages.add(new ValidationMessage(Source.ProfileValidator, IssueType.VALUE, "StructureDefinition.differential.element["+Integer.toString(start)+"]", "Error: unknown element '"+context.getElement().get(start).getPath()+"' (or it is out of order) in profile '"+url+"' (looking for '"+path+"')", IssueSeverity.WARNING));
addMessage(new ValidationMessage(Source.ProfileValidator, IssueType.VALUE, "StructureDefinition.differential.element["+Integer.toString(start)+"]", "Error: unknown element '"+context.getElement().get(start).getPath()+"' (or it is out of order) in profile '"+url+"' (looking for '"+path+"')", IssueSeverity.WARNING));
*/
result.add(context.getElement().get(i));
@ -2529,7 +2537,7 @@ public class ProfileUtilities {
if (derived.hasMinElement()) {
if (!Base.compareDeep(derived.getMinElement(), base.getMinElement(), false)) {
if (derived.getMin() < base.getMin() && !derived.hasSliceName()) // in a slice, minimum cardinality rules do not apply
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+source.getPath(), "Element "+base.getPath()+": derived min ("+Integer.toString(derived.getMin())+") cannot be less than the base min ("+Integer.toString(base.getMin())+") in "+srcSD.getVersionedUrl(), ValidationMessage.IssueSeverity.ERROR));
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+source.getPath(), "Element "+base.getPath()+": derived min ("+Integer.toString(derived.getMin())+") cannot be less than the base min ("+Integer.toString(base.getMin())+") in "+srcSD.getVersionedUrl(), ValidationMessage.IssueSeverity.ERROR));
base.setMinElement(derived.getMinElement().copy());
} else if (trimDifferential)
derived.setMinElement(null);
@ -2540,7 +2548,7 @@ public class ProfileUtilities {
if (derived.hasMaxElement()) {
if (!Base.compareDeep(derived.getMaxElement(), base.getMaxElement(), false)) {
if (isLargerMax(derived.getMax(), base.getMax()))
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+source.getPath(), "Element "+base.getPath()+": derived max ("+derived.getMax()+") cannot be greater than the base max ("+base.getMax()+")", ValidationMessage.IssueSeverity.ERROR));
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+source.getPath(), "Element "+base.getPath()+": derived max ("+derived.getMax()+") cannot be greater than the base max ("+base.getMax()+")", ValidationMessage.IssueSeverity.ERROR));
base.setMaxElement(derived.getMaxElement().copy());
} else if (trimDifferential)
derived.setMaxElement(null);
@ -2642,7 +2650,7 @@ public class ProfileUtilities {
}
if (!(base.hasMustSupportElement() && Base.compareDeep(base.getMustSupportElement(), mse, false))) {
if (base.hasMustSupport() && base.getMustSupport() && !derived.getMustSupport()) {
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "Illegal constraint [must-support = false] when [must-support = true] in the base profile", ValidationMessage.IssueSeverity.ERROR));
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "Illegal constraint [must-support = false] when [must-support = true] in the base profile", ValidationMessage.IssueSeverity.ERROR));
}
base.setMustSupportElement(mse);
} else if (trimDifferential)
@ -2654,7 +2662,7 @@ public class ProfileUtilities {
if (derived.hasMustHaveValueElement()) {
if (!(base.hasMustHaveValueElement() && Base.compareDeep(derived.getMustHaveValueElement(), base.getMustHaveValueElement(), false))) {
if (base.hasMustHaveValue() && base.getMustHaveValue() && !derived.getMustHaveValue()) {
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "Illegal constraint [must-have-value = false] when [must-have-value = true] in the base profile", ValidationMessage.IssueSeverity.ERROR));
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "Illegal constraint [must-have-value = false] when [must-have-value = true] in the base profile", ValidationMessage.IssueSeverity.ERROR));
}
base.setMustHaveValueElement(derived.getMustHaveValueElement().copy());
} else if (trimDifferential)
@ -2721,25 +2729,25 @@ public class ProfileUtilities {
if (!base.hasBinding() || !Base.compareDeep(derived.getBinding(), base.getBinding(), false)) {
if (base.hasBinding() && base.getBinding().getStrength() == BindingStrength.REQUIRED && derived.getBinding().getStrength() != BindingStrength.REQUIRED)
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "illegal attempt to change the binding on "+derived.getPath()+" from "+base.getBinding().getStrength().toCode()+" to "+derived.getBinding().getStrength().toCode(), ValidationMessage.IssueSeverity.ERROR));
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "illegal attempt to change the binding on "+derived.getPath()+" from "+base.getBinding().getStrength().toCode()+" to "+derived.getBinding().getStrength().toCode(), ValidationMessage.IssueSeverity.ERROR));
// throw new DefinitionException("StructureDefinition "+pn+" at "+derived.getPath()+": illegal attempt to change a binding from "+base.getBinding().getStrength().toCode()+" to "+derived.getBinding().getStrength().toCode());
else if (base.hasBinding() && derived.hasBinding() && base.getBinding().getStrength() == BindingStrength.REQUIRED && base.getBinding().hasValueSet() && derived.getBinding().hasValueSet()) {
ValueSet baseVs = context.findTxResource(ValueSet.class, base.getBinding().getValueSet(), srcSD);
ValueSet contextVs = context.findTxResource(ValueSet.class, derived.getBinding().getValueSet(), derivedSrc);
if (baseVs == null) {
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+base.getPath(), "Binding "+base.getBinding().getValueSet()+" could not be located", ValidationMessage.IssueSeverity.WARNING));
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+base.getPath(), "Binding "+base.getBinding().getValueSet()+" could not be located", ValidationMessage.IssueSeverity.WARNING));
} else if (contextVs == null) {
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "Binding "+derived.getBinding().getValueSet()+" could not be located", ValidationMessage.IssueSeverity.WARNING));
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "Binding "+derived.getBinding().getValueSet()+" could not be located", ValidationMessage.IssueSeverity.WARNING));
} else {
ValueSetExpansionOutcome expBase = context.expandVS(baseVs, true, false);
ValueSetExpansionOutcome expDerived = context.expandVS(contextVs, true, false);
if (expBase.getValueset() == null)
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+base.getPath(), "Binding "+base.getBinding().getValueSet()+" could not be expanded", ValidationMessage.IssueSeverity.WARNING));
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+base.getPath(), "Binding "+base.getBinding().getValueSet()+" could not be expanded", ValidationMessage.IssueSeverity.WARNING));
else if (expDerived.getValueset() == null)
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "Binding "+derived.getBinding().getValueSet()+" could not be expanded", ValidationMessage.IssueSeverity.WARNING));
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "Binding "+derived.getBinding().getValueSet()+" could not be expanded", ValidationMessage.IssueSeverity.WARNING));
else if (ToolingExtensions.hasExtension(expBase.getValueset().getExpansion(), ToolingExtensions.EXT_EXP_TOOCOSTLY)) {
if (ToolingExtensions.hasExtension(expDerived.getValueset().getExpansion(), ToolingExtensions.EXT_EXP_TOOCOSTLY) || expDerived.getValueset().getExpansion().getContains().size() > 100) {
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "Unable to check if "+derived.getBinding().getValueSet()+" is a proper subset of " +base.getBinding().getValueSet()+" - base value set is too large to check", ValidationMessage.IssueSeverity.WARNING));
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "Unable to check if "+derived.getBinding().getValueSet()+" is a proper subset of " +base.getBinding().getValueSet()+" - base value set is too large to check", ValidationMessage.IssueSeverity.WARNING));
} else {
boolean ok = true;
for (ValueSetExpansionContainsComponent cc : expDerived.getValueset().getExpansion().getContains()) {
@ -2750,11 +2758,11 @@ public class ProfileUtilities {
}
}
if (!ok) {
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "Binding "+derived.getBinding().getValueSet()+" is not a subset of binding "+base.getBinding().getValueSet(), ValidationMessage.IssueSeverity.ERROR));
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "Binding "+derived.getBinding().getValueSet()+" is not a subset of binding "+base.getBinding().getValueSet(), ValidationMessage.IssueSeverity.ERROR));
}
}
} else if (!isSubset(expBase.getValueset(), expDerived.getValueset()))
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "Binding "+derived.getBinding().getValueSet()+" is not a subset of binding "+base.getBinding().getValueSet(), ValidationMessage.IssueSeverity.ERROR));
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "Binding "+derived.getBinding().getValueSet()+" is not a subset of binding "+base.getBinding().getValueSet(), ValidationMessage.IssueSeverity.ERROR));
}
}
ElementDefinitionBindingComponent d = derived.getBinding();
@ -2965,11 +2973,7 @@ public class ProfileUtilities {
if (tgtOk) {
ok = true;
} else {
if (messages == null) {
throw new FHIRException(context.formatMessage(I18nConstants.ERROR_AT__THE_TARGET_PROFILE__IS_NOT__VALID_CONSTRAINT_ON_THE_BASE_, purl, derived.getPath(), url, td.getTargetProfile()));
} else {
messages.add(new ValidationMessage(Source.InstanceValidator, IssueType.BUSINESSRULE, derived.getPath(), "The target profile " + u.getValue() + " is not a valid constraint on the base (" + td.getTargetProfile() + ") at " + derived.getPath(), IssueSeverity.ERROR));
}
addMessage(new ValidationMessage(Source.InstanceValidator, IssueType.BUSINESSRULE, derived.getPath(), context.formatMessage(I18nConstants.ERROR_AT__THE_TARGET_PROFILE__IS_NOT__VALID_CONSTRAINT_ON_THE_BASE_, purl, derived.getPath(), url, td.getTargetProfile()), IssueSeverity.ERROR));
}
}
} else {
@ -2989,9 +2993,7 @@ public class ProfileUtilities {
}
StructureDefinition sd = context.fetchResource(StructureDefinition.class, url);
if (sd == null) {
if (messages != null) {
messages.add(new ValidationMessage(Source.InstanceValidator, IssueType.BUSINESSRULE, path, "Cannot check whether the target profile " + url + " on "+dPath+" is valid constraint on the base because it is not known", IssueSeverity.WARNING));
}
addMessage(new ValidationMessage(Source.InstanceValidator, IssueType.BUSINESSRULE, path, "Cannot check whether the target profile " + url + " on "+dPath+" is valid constraint on the base because it is not known", IssueSeverity.WARNING));
return true;
} else {
if (sd.hasBaseDefinition() && sdConformsToTargets(path, dPath, sd.getBaseDefinition(), td)) {
@ -3022,7 +3024,7 @@ public class ProfileUtilities {
}
if (!ok) {
messages.add(new ValidationMessage(Source.InstanceValidator, IssueType.CONFLICT, dest.getId(), "The "+fieldName+" value has type '"+ft+"' which is not valid (valid "+Utilities.pluralize("type", dest.getType().size())+": "+types.toString()+")", IssueSeverity.ERROR));
addMessage(new ValidationMessage(Source.InstanceValidator, IssueType.CONFLICT, dest.getId(), "The "+fieldName+" value has type '"+ft+"' which is not valid (valid "+Utilities.pluralize("type", dest.getType().size())+": "+types.toString()+")", IssueSeverity.ERROR));
}
}
@ -3936,10 +3938,7 @@ public class ProfileUtilities {
}
ed.setId(bs);
if (idList.containsKey(bs)) {
if (exception || messages == null) {
throw new DefinitionException(context.formatMessage(I18nConstants.SAME_ID_ON_MULTIPLE_ELEMENTS__IN_, bs, idList.get(bs), ed.getPath(), name));
} else
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, name+"."+bs, "Duplicate Element id "+bs, ValidationMessage.IssueSeverity.ERROR));
addMessage(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, name+"."+bs, context.formatMessage(I18nConstants.SAME_ID_ON_MULTIPLE_ELEMENTS__IN_, bs, idList.get(bs), ed.getPath(), name), ValidationMessage.IssueSeverity.ERROR));
}
idList.put(bs, ed.getPath());
if (ed.hasContentReference() && ed.getContentReference().startsWith("#")) {
@ -4315,12 +4314,12 @@ public class ProfileUtilities {
public boolean isThrowException() {
return exception;
return wantThrowExceptions;
}
public void setThrowException(boolean exception) {
this.exception = exception;
this.wantThrowExceptions = exception;
}
@ -4578,7 +4577,9 @@ public class ProfileUtilities {
}
public void setMessages(List<ValidationMessage> messages) {
this.messages = messages;
if (messages != null) {
this.messages = messages;
}
}
private Map<String, List<Property>> propertyCache = new HashMap<>();

View File

@ -750,9 +750,9 @@ public class ValueSetRenderer extends TerminologyRenderer {
if (cs == null) {
return "?cs-n?";
}
String ref = (String) cs.getUserData("filename");
String ref = cs.getWebPath();
if (ref == null) {
ref = (String) cs.getWebPath();
ref = cs.getUserString("filename");
}
return ref == null ? null : ref.replace("\\", "/");
}

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId>
<version>6.3.26-SNAPSHOT</version>
<version>6.3.27-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId>
<version>6.3.26-SNAPSHOT</version>
<version>6.3.27-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -1108,4 +1108,7 @@ public class I18nConstants {
public static final String SM_TARGET_TYPE_UNKNOWN = "SM_TARGET_TYPE_UNKNOWN";
public static final String XHTML_XHTML_ATTRIBUTE_XML_SPACE = "XHTML_XHTML_ATTRIBUTE_XML_SPACE";
public static final String VALIDATION_HL7_PUBLISHER_MULTIPLE_WGS = "VALIDATION_HL7_PUBLISHER_MULTIPLE_WGS";
public static final String SD_BASE_EXPERIMENTAL = "SD_BASE_EXPERIMENTAL";
public static final String SD_ED_EXPERIMENTAL_BINDING = "SD_ED_EXPERIMENTAL_BINDING";
public static final String VALIDATION_NO_EXPERIMENTAL_CONTENT = "VALIDATION_NO_EXPERIMENTAL_CONTENT";
}

View File

@ -1140,4 +1140,6 @@ XHTML_XHTML_ATTRIBUTE_XML_SPACE = The attribute 'xml:space' is legal but has a f
VALIDATION_HL7_PUBLISHER_MULTIPLE_WGS = This resource has more than workgroup extension (http://hl7.org/fhir/StructureDefinition/structuredefinition-wg)
NO_VALID_DISPLAY_FOUND_NONE_FOR_LANG = Wrong Display Name ''{0}'' for {1}#{2}. There are no valid display names found for language(s) ''{3}''. Default display is ''{4}''
NO_VALID_DISPLAY_AT_ALL = Cannot validate display Name ''{0}'' for {1}#{2}: No displays are known
SD_BASE_EXPERIMENTAL = The definition builds on ''{0}'' which is experimental, but this definition is not labeled as experimental
SD_ED_EXPERIMENTAL_BINDING = The definition for the element ''{0}'' binds to the value set ''{1}'' which is experimental, but this structure is not labeled as experimental
VALIDATION_NO_EXPERIMENTAL_CONTENT = Experimental content is not allowed in this context

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId>
<version>6.3.26-SNAPSHOT</version>
<version>6.3.27-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId>
<version>6.3.26-SNAPSHOT</version>
<version>6.3.27-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -227,6 +227,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP
@Getter @Setter private boolean checkIPSCodes;
@Getter @Setter private BestPracticeWarningLevel bestPracticeLevel;
@Getter @Setter private boolean unknownCodeSystemsCauseErrors;
@Getter @Setter private boolean noExperimentalContent;
@Getter @Setter private Locale locale;
@Getter @Setter private List<ImplementationGuide> igs = new ArrayList<>();
@Getter @Setter private List<String> extensionDomains = new ArrayList<>();
@ -909,6 +910,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP
validator.setPolicyAdvisor(policyAdvisor);
}
validator.setUnknownCodeSystemsCauseErrors(unknownCodeSystemsCauseErrors);
validator.setNoExperimentalContent(noExperimentalContent);
return validator;
}

View File

@ -166,6 +166,9 @@ public class CliContext {
@JsonProperty("unknownCodeSystemsCauseErrors")
private boolean unknownCodeSystemsCauseErrors;
@JsonProperty("noExperimentalContent")
private boolean noExperimentalContent;
@JsonProperty("baseEngine")
public String getBaseEngine() {
return baseEngine;
@ -836,6 +839,7 @@ public class CliContext {
Objects.equals(bestPracticeLevel, that.bestPracticeLevel) &&
Objects.equals(watchScanDelay, that.watchScanDelay) &&
Objects.equals(unknownCodeSystemsCauseErrors, that.unknownCodeSystemsCauseErrors) &&
Objects.equals(noExperimentalContent, that.noExperimentalContent) &&
Objects.equals(watchSettleTime, that.watchSettleTime) ;
}
@ -844,7 +848,7 @@ public class CliContext {
return Objects.hash(baseEngine, doNative, extensions, hintAboutNonMustSupport, recursive, doDebug, assumeValidRestReferences, canDoNative, noInternalCaching,
noExtensibleBindingMessages, noInvariants, displayWarnings, wantInvariantsInMessages, map, output, outputSuffix, htmlOutput, txServer, sv, txLog, txCache, mapLog, lang, srcLang, tgtLang, fhirpath, snomedCT,
targetVer, igs, questionnaireMode, level, profiles, sources, inputs, mode, locale, locations, crumbTrails, forPublication, showTimes, allowExampleUrls, outputStyle, jurisdiction, noUnicodeBiDiControlChars,
watchMode, watchScanDelay, watchSettleTime, bestPracticeLevel, unknownCodeSystemsCauseErrors, htmlInMarkdownCheck, allowDoubleQuotesInFHIRPath, checkIPSCodes);
watchMode, watchScanDelay, watchSettleTime, bestPracticeLevel, unknownCodeSystemsCauseErrors, noExperimentalContent, htmlInMarkdownCheck, allowDoubleQuotesInFHIRPath, checkIPSCodes);
}
@Override
@ -904,6 +908,7 @@ public class CliContext {
", watchSettleTime=" + watchSettleTime +
", watchScanDelay=" + watchScanDelay +
", unknownCodeSystemsCauseErrors=" + unknownCodeSystemsCauseErrors +
", noExperimentalContent=" + noExperimentalContent +
'}';
}
@ -973,5 +978,16 @@ public class CliContext {
this.unknownCodeSystemsCauseErrors = unknownCodeSystemsCauseErrors;
}
@JsonProperty("noExperimentalContent")
public boolean isNoExperimentalContent() {
return noExperimentalContent;
}
@JsonProperty("noExperimentalContent")
public void setNoExperimentalContent(boolean noExperimentalContent) {
this.noExperimentalContent = noExperimentalContent;
}
}

View File

@ -582,6 +582,7 @@ public class ValidationService {
validationEngine.getBundleValidationRules().addAll(cliContext.getBundleValidationRules());
validationEngine.setJurisdiction(CodeSystemUtilities.readCoding(cliContext.getJurisdiction()));
validationEngine.setUnknownCodeSystemsCauseErrors(cliContext.isUnknownCodeSystemsCauseErrors());
validationEngine.setNoExperimentalContent(cliContext.isNoExperimentalContent());
TerminologyCache.setNoCaching(cliContext.isNoInternalCaching());
validationEngine.prepare(); // generate any missing snapshots
System.out.println(" go (" + timeTracker.milestone() + ")");

View File

@ -95,6 +95,7 @@ public class Params {
public static final String CHECK_IPS_CODES = "-check-ips-codes";
public static final String BEST_PRACTICE = "-best-practice";
public static final String UNKNOWN_CODESYSTEMS_CAUSE_ERROR = "-unknown-codesystems-cause-errors";
public static final String NO_EXPERIMENTAL_CONTENT = "-no-experimental-content";
public static final String RUN_TESTS = "-run-tests";
@ -321,6 +322,8 @@ public class Params {
cliContext.setForPublication(true);
} else if (args[i].equals(UNKNOWN_CODESYSTEMS_CAUSE_ERROR)) {
cliContext.setUnknownCodeSystemsCauseErrors(true);
} else if (args[i].equals(NO_EXPERIMENTAL_CONTENT)) {
cliContext.setNoExperimentalContent(true);
} else if (args[i].equals(VERBOSE)) {
cliContext.setCrumbTrails(true);
} else if (args[i].equals(ALLOW_EXAMPLE_URLS)) {

View File

@ -599,6 +599,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
private IDigitalSignatureServices signatureServices;
private ContextUtilities cu;
private boolean unknownCodeSystemsCauseErrors;
private boolean noExperimentalContent;
public InstanceValidator(@Nonnull IWorkerContext theContext, @Nonnull IEvaluationContext hostServices, @Nonnull XVerExtensionManager xverManager) {
super(theContext, xverManager, false);
@ -5718,6 +5719,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
hint(errors, "2023-08-14", IssueType.BUSINESSRULE, element.line(), element.col(), stack.getLiteralPath(), statusCodesDeeplyConsistent(status, standardsStatus), I18nConstants.VALIDATION_VAL_STATUS_INCONSISTENT_HINT, status, standardsStatus);
}
}
if (noExperimentalContent) {
String exp = element.getNamedChildValue("experimental");
ok = rule(errors, "2024-09-17", IssueType.BUSINESSRULE, element.line(), element.col(), stack.getLiteralPath(), !"true".equals(exp), I18nConstants.VALIDATION_NO_EXPERIMENTAL_CONTENT) && ok;
}
if (isHL7Core(element) && !isExample()) {
ok = checkPublisherConsistency(valContext, errors, element, stack, contained) && ok;
@ -7840,5 +7845,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
this.unknownCodeSystemsCauseErrors = unknownCodeSystemsCauseErrors;
}
public boolean isNoExperimentalContent() {
return noExperimentalContent;
}
public void setNoExperimentalContent(boolean noExperimentalContent) {
this.noExperimentalContent = noExperimentalContent;
}
}

View File

@ -85,6 +85,7 @@ public class StructureDefinitionValidator extends BaseValidator {
List<ElementDefinition> snapshot = sd.getSnapshot().getElement();
sd.setSnapshot(null);
typeName = sd.getTypeName();
boolean experimental = "true".equals(src.getNamedChildValue("experimental", false));
StructureDefinition base = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition());
if (warning(errors, NO_RULE_DATE, IssueType.NOTFOUND, stack.getLiteralPath(), base != null, I18nConstants.UNABLE_TO_FIND_BASE__FOR_, sd.getBaseDefinition(), "StructureDefinition, so can't check the differential")) {
if (rule(errors, NO_RULE_DATE, IssueType.NOTFOUND, stack.getLiteralPath(), sd.hasDerivation(), I18nConstants.SD_MUST_HAVE_DERIVATION, sd.getUrl())) {
@ -129,6 +130,7 @@ public class StructureDefinitionValidator extends BaseValidator {
ok = rule(errors, "2022-11-02", IssueType.NOTFOUND, stack.getLiteralPath(), base.getKindElement().primitiveValue().equals(src.getChildValue("kind")),
I18nConstants.SD_DERIVATION_KIND_MISMATCH, base.getKindElement().primitiveValue(), src.getChildValue("kind")) && ok;
}
warning(errors, "2024-09-17", IssueType.BUSINESSRULE, stack.getLiteralPath(), !base.getExperimental() || experimental, I18nConstants.SD_BASE_EXPERIMENTAL, sd.getBaseDefinition());
}
List<Element> differentials = src.getChildrenByName("differential");
@ -136,10 +138,10 @@ public class StructureDefinitionValidator extends BaseValidator {
boolean logical = "logical".equals(src.getNamedChildValue("kind", false));
boolean constraint = "constraint".equals(src.getNamedChildValue("derivation", false));
for (Element differential : differentials) {
ok = validateElementList(errors, differential, stack.push(differential, -1, null, null), false, snapshots.size() > 0, sd, typeName, logical, constraint, src.getNamedChildValue("type", false), src.getNamedChildValue("url", false), src.getNamedChildValue("type", false), base) && ok;
ok = validateElementList(errors, differential, stack.push(differential, -1, null, null), false, snapshots.size() > 0, sd, typeName, logical, constraint, src.getNamedChildValue("type", false), src.getNamedChildValue("url", false), src.getNamedChildValue("type", false), base, experimental) && ok;
}
for (Element snapshotE : snapshots) {
ok = validateElementList(errors, snapshotE, stack.push(snapshotE, -1, null, null), true, true, sd, typeName, logical, constraint, src.getNamedChildValue("type", false), src.getNamedChildValue("url", false), src.getNamedChildValue("type", false), base) && ok;
ok = validateElementList(errors, snapshotE, stack.push(snapshotE, -1, null, null), true, true, sd, typeName, logical, constraint, src.getNamedChildValue("type", false), src.getNamedChildValue("url", false), src.getNamedChildValue("type", false), base, experimental) && ok;
}
// obligation profile support
@ -189,6 +191,7 @@ public class StructureDefinitionValidator extends BaseValidator {
}
}
} catch (Exception e) {
e.printStackTrace();
rule(errors, NO_RULE_DATE, IssueType.EXCEPTION, stack.getLiteralPath(), false, I18nConstants.ERROR_GENERATING_SNAPSHOT, e.getMessage());
ok = false;
}
@ -409,19 +412,19 @@ public class StructureDefinitionValidator extends BaseValidator {
}
}
private boolean validateElementList(List<ValidationMessage> errors, Element elementList, NodeStack stack, boolean snapshot, boolean hasSnapshot, StructureDefinition sd, String typeName, boolean logical, boolean constraint, String rootPath, String profileUrl, String profileType, StructureDefinition base) {
private boolean validateElementList(List<ValidationMessage> errors, Element elementList, NodeStack stack, boolean snapshot, boolean hasSnapshot, StructureDefinition sd, String typeName, boolean logical, boolean constraint, String rootPath, String profileUrl, String profileType, StructureDefinition base, boolean experimental) {
Map<String, String> invariantMap = new HashMap<>();
boolean ok = true;
List<Element> elements = elementList.getChildrenByName("element");
int cc = 0;
for (Element element : elements) {
ok = validateElementDefinition(errors, elements, element, stack.push(element, cc, null, null), snapshot, hasSnapshot, sd, typeName, logical, constraint, invariantMap, rootPath, profileUrl, profileType, base) && ok;
ok = validateElementDefinition(errors, elements, element, stack.push(element, cc, null, null), snapshot, hasSnapshot, sd, typeName, logical, constraint, invariantMap, rootPath, profileUrl, profileType, base, experimental) && ok;
cc++;
}
return ok;
}
private boolean validateElementDefinition(List<ValidationMessage> errors, List<Element> elements, Element element, NodeStack stack, boolean snapshot, boolean hasSnapshot, StructureDefinition sd, String typeName, boolean logical, boolean constraint, Map<String, String> invariantMap, String rootPath, String profileUrl, String profileType, StructureDefinition base) {
private boolean validateElementDefinition(List<ValidationMessage> errors, List<Element> elements, Element element, NodeStack stack, boolean snapshot, boolean hasSnapshot, StructureDefinition sd, String typeName, boolean logical, boolean constraint, Map<String, String> invariantMap, String rootPath, String profileUrl, String profileType, StructureDefinition base, boolean experimental) {
boolean ok = true;
boolean typeMustSupport = false;
String path = element.getNamedChildValue("path", false);
@ -522,7 +525,7 @@ public class StructureDefinitionValidator extends BaseValidator {
ok = rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), characteristics.contains("can-bind") , I18nConstants.SD_ILLEGAL_CHARACTERISTICS, "Binding", typeCodes) && ok;
}
Element binding = element.getNamedChild("binding", false);
ok = validateBinding(errors, binding, stack.push(binding, -1, null, null), typeCodes, snapshot, path) && ok;
ok = validateBinding(errors, binding, stack.push(binding, -1, null, null), typeCodes, snapshot, path, experimental) && ok;
} else {
// this is a good idea but there's plenty of cases where the rule isn't met; maybe one day it's worth investing the time to exclude these cases and bring this rule back
// String bt = boundType(typeCodes);
@ -986,7 +989,7 @@ public class StructureDefinitionValidator extends BaseValidator {
return null;
}
private boolean validateBinding(List<ValidationMessage> errors, Element binding, NodeStack stack, Set<String> typeCodes, boolean snapshot, String path) {
private boolean validateBinding(List<ValidationMessage> errors, Element binding, NodeStack stack, Set<String> typeCodes, boolean snapshot, String path, boolean experimental) {
boolean ok = true;
if (bindableType(typeCodes) == null) {
ok = rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), !snapshot, I18nConstants.SD_ED_BIND_NO_BINDABLE, path, typeCodes.toString()) && ok;
@ -1006,7 +1009,12 @@ public class StructureDefinitionValidator extends BaseValidator {
if (warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs != null || serverSupportsValueSet(ref), I18nConstants.SD_ED_BIND_UNKNOWN_VS, path, ref)) {
if (vs != null) {
ok = rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs instanceof ValueSet, I18nConstants.SD_ED_BIND_NOT_VS, path, ref, vs.fhirType()) && ok;
if (rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs instanceof ValueSet, I18nConstants.SD_ED_BIND_NOT_VS, path, ref, vs.fhirType())) {
ValueSet vsr = (ValueSet) vs;
warning(errors, "2024-09-17", IssueType.BUSINESSRULE, stack.getLiteralPath(), !vsr.getExperimental() || experimental, I18nConstants.SD_ED_EXPERIMENTAL_BINDING, path, ref);
} else {
ok = false;
}
}
}
}

View File

@ -362,6 +362,9 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
if (content.has("security-checks")) {
val.setSecurityChecks(content.get("security-checks").getAsBoolean());
}
if (content.has("no-experimental-content")) {
val.setNoExperimentalContent(content.get("no-experimental-content").getAsBoolean());
}
if (content.has("noHtmlInMarkdown")) {
val.setHtmlInMarkdownCheck(HtmlInMarkdownCheck.ERROR);
}

View File

@ -14,14 +14,14 @@
HAPI FHIR
-->
<artifactId>org.hl7.fhir.core</artifactId>
<version>6.3.26-SNAPSHOT</version>
<version>6.3.27-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<commons_compress_version>1.26.0</commons_compress_version>
<guava_version>32.0.1-jre</guava_version>
<hapi_fhir_version>6.4.1</hapi_fhir_version>
<validator_test_case_version>1.5.22-SNAPSHOT</validator_test_case_version>
<validator_test_case_version>1.5.22</validator_test_case_version>
<jackson_version>2.17.0</jackson_version>
<junit_jupiter_version>5.9.2</junit_jupiter_version>
<junit_platform_launcher_version>1.8.2</junit_platform_launcher_version>