Merge pull request #1330 from hapifhir/gg-202206-cda-fml-fixes

Gg 202206 cda fml fixes
This commit is contained in:
Grahame Grieve 2023-06-29 14:05:34 +10:00 committed by GitHub
commit 7b4032d87b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 122 additions and 21 deletions

View File

@ -1,7 +1,10 @@
## Validator Changes
* no changes
* Fix issue loading SPDX value set + Fix missing code for 'not-open-source'
* Allow target to be treated as source when validating FML
* Fix issues validating names and urls for logical models
* Don't duplicate warnings about missing expressions on invariants in R5
## Other code changes
* no changes
* Handle case where base hasn't got a snapshot generating snapshots

View File

@ -200,7 +200,7 @@ public abstract class BaseLoaderR5 implements IContextResourceLoader {
} else {
return pi.isCore() && Utilities.tail(pri.getUrl()).equals(pri.getStatedType());
}
} else if (pi.isCore() && "spdx-license".equals(pri.getId())) {
} else if (pi.isCore() && "CodeSystem".equals(pri.getResourceType()) && "spdx-license".equals(pri.getId())) {
return false;
} else {
return true;

View File

@ -37,8 +37,12 @@ public class SPDXImporter {
cs.addProperty().setCode("status").setType(PropertyType.CODE).setUri("http://hl7.org/fhir/concept-properties#status");
cs.addProperty().setCode("seeAlso").setType(PropertyType.STRING);
cs.setVersion(json.asString("licenseListVersion"));
for (JsonObject l : json.getJsonObjects("licenses")) {
ConceptDefinitionComponent cc = cs.addConcept();
cc.setCode("not-open-source");
cc.setDisplay("Not open source");
cc.setDefinition("Not an open source license.");
for (JsonObject l : json.getJsonObjects("licenses")) {
cc = cs.addConcept();
cc.setCode(l.asString("licenseId"));
cc.setDisplay(l.asString("name"));
cc.setDefinition(l.asString("name"));

View File

@ -607,8 +607,13 @@ public class ProfilePathProcessor {
}
if (src == null)
throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.UNABLE_TO_FIND_ELEMENT__IN_, eid, firstTypeProfile.getValue()));
} else
} else {
if (firstTypeStructureDefinition.getSnapshot().getElement().isEmpty()) {
throw new FHIRException(profileUtilities.getContext().formatMessage(I18nConstants.SNAPSHOT_IS_EMPTY, firstTypeStructureDefinition.getVersionedUrl(), "Source for first element"));
} else {
src = firstTypeStructureDefinition.getSnapshot().getElement().get(0);
}
}
template = src.copy().setPath(currentBase.getPath());
template.setSliceName(null);
// temporary work around

View File

@ -621,7 +621,14 @@ public class ProfileUtilities extends TranslatingUtilities {
if (!base.getType().equals(derived.getType()) && derived.getDerivation() == TypeDerivationRule.CONSTRAINT) {
throw new DefinitionException(context.formatMessage(I18nConstants.BASE__DERIVED_PROFILES_HAVE_DIFFERENT_TYPES____VS___, base.getUrl(), base.getType(), derived.getUrl(), derived.getType()));
}
if (!base.hasSnapshot()) {
StructureDefinition sdb = context.fetchResource(StructureDefinition.class, base.getBaseDefinition());
if (sdb == null)
throw new DefinitionException(context.formatMessage(I18nConstants.UNABLE_TO_FIND_BASE__FOR_, base.getBaseDefinition(), base.getUrl()));
checkNotGenerating(sdb, "an extension base");
generateSnapshot(sdb, base, base.getUrl(), (sdb.hasWebPath()) ? Utilities.extractBaseUrl(sdb.getWebPath()) : webUrl, base.getName());
}
fixTypeOfResourceId(base);
if (snapshotStack.contains(derived.getUrl())) {
@ -810,7 +817,7 @@ public class ProfileUtilities extends TranslatingUtilities {
}
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, url+"#"+slice.getFocus().getId(), msg, ValidationMessage.IssueSeverity.WARNING));
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url+"#"+"#"+slice.getFocus().getId(), msg, ValidationMessage.IssueSeverity.WARNING));
}
slices.remove(s);
}
@ -2289,7 +2296,7 @@ public class ProfileUtilities extends TranslatingUtilities {
}
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 && !"Extension".equals(profile.getType()) && profile.getKind() != StructureDefinitionKind.RESOURCE) {
if (profile != null && !"Extension".equals(profile.getType()) && profile.getKind() != StructureDefinitionKind.RESOURCE && profile.getKind() != StructureDefinitionKind.LOGICAL) {
profile = null;
}
}
@ -4379,4 +4386,8 @@ public class ProfileUtilities extends TranslatingUtilities {
return messages;
}
public static boolean isResourceBoundary(ElementDefinition ed) {
return ed.getType().size() == 1 && "Resource".equals(ed.getTypeFirstRep().getCode());
}
}

View File

@ -821,6 +821,7 @@ public class I18nConstants {
public static final String SM_TARGET_TRANSFORM_EXPRESSION_ERROR = "SM_TARGET_TRANSFORM_EXPRESSION_ERROR";
public static final String SM_IMPORT_NOT_FOUND = "SM_IMPORT_NOT_FOUND";
public static final String SM_TARGET_TYPE_MULTIPLE_POSSIBLE = "SM_TARGET_TYPE_MULTIPLE_POSSIBLE";
public static final String SM_TARGET_TRANSFORM_TYPE_UNKNOWN = "SM_TARGET_TRANSFORM_TYPE_UNKNOWN";
public static final String SM_DEPENDENT_PARAM_NOT_FOUND = "SM_DEPENDENT_PARAM_NOT_FOUND";
public static final String SM_DEPENDENT_PARAM_MODE_MISMATCH = "SM_DEPENDENT_PARAM_MODE_MISMATCH";
public static final String SM_DEPENDENT_PARAM_TYPE_MISMATCH = "SM_DEPENDENT_PARAM_TYPE_MISMATCH";
@ -915,6 +916,7 @@ public class I18nConstants {
public static final String ED_INVARIANT_NO_EXPRESSION = "ED_INVARIANT_NO_EXPRESSION";
public static final String ED_INVARIANT_EXPRESSION_CONFLICT = "ED_INVARIANT_EXPRESSION_CONFLICT";
public static final String ED_INVARIANT_EXPRESSION_ERROR = "ED_INVARIANT_EXPRESSION_ERROR";
public static final String SNAPSHOT_IS_EMPTY = "SNAPSHOT_IS_EMPTY";
}

View File

@ -186,7 +186,7 @@ Terminology_TX_System_Relative = Coding.system must be an absolute reference, no
Terminology_TX_System_Unknown = Unknown Code System ''{0}''
Terminology_TX_System_ValueSet = Invalid System URI: {0} - cannot use a value set URI as a system
Terminology_TX_System_ValueSet2 = The Coding references a value set, not a code system (''{0}'')
Terminology_TX_ValueSet_NotFound = ValueSet {0} not found by validator
Terminology_TX_ValueSet_NotFound = ValueSet {0} not found
Terminology_TX_ValueSet_NotFound_CS = Found a reference to a CodeSystem ({0}) where a ValueSet belongs
Type_Specific_Checks_DT_Base64_Valid = The value ''{0}'' is not a valid Base64 value
Type_Specific_Checks_DT_Boolean_Value = Boolean values must be ''true'' or ''false''
@ -848,9 +848,9 @@ SM_NAME_INVALID = The name {0} is not valid
SM_GROUP_NAME_DUPLICATE = The Group name ''{0}'' is already used
SM_GROUP_INPUT_DUPLICATE = The name {0} is already used
SM_GROUP_INPUT_MODE_INVALID = The group parameter {0} mode {1} isn''t valid
SM_GROUP_INPUT_NO_TYPE = The group parameter {0} has no type, so the paths cannot be validated
SM_GROUP_INPUT_NO_TYPE = Group {1} parameter {0} has no type, so the paths cannot be validated
SM_GROUP_INPUT_TYPE_NOT_DECLARED = The type {0} is not declared and is unknown
SM_GROUP_INPUT_MODE_MISMATCH = The type {0} has mode {1} which doesn''t match the structure definition {2}
SM_GROUP_INPUT_MODE_MISMATCH = The type ''{0}'' has mode ''{1}'' which doesn''t match the structure definition mode of ''{2}''
SM_GROUP_INPUT_TYPE_UNKNOWN_STRUCTURE = The type {0} which maps to the canonical URL {1} is not known, so the paths cannot be validated
SM_GROUP_INPUT_TYPE_UNKNOWN_TYPE = The type {0} is not known, so the paths cannot be validated
SM_SOURCE_CONTEXT_UNKNOWN = The source context {0} is not known at this point
@ -875,6 +875,7 @@ SM_TARGET_TRANSFORM_PARAM_UNPROCESSIBLE = The parameter at index {0} could not b
SM_TARGET_TRANSFORM_EXPRESSION_ERROR = The FHIRPath expression passed as the evaluate parameter is invalid: {0}
SM_IMPORT_NOT_FOUND = No maps were found to match {0} - validation may be wrong
SM_TARGET_TYPE_MULTIPLE_POSSIBLE = Multiple types are possible here ({0}) so further type checking is not possible
SM_TARGET_TRANSFORM_TYPE_UNKNOWN = The type ''{0}'' is not known
SM_DEPENDENT_PARAM_MODE_MISMATCH = The parameter {0} refers to the variable {1} but it''s mode is {2} which is not the same as the mode required for the group {3}
SM_DEPENDENT_PARAM_NOT_FOUND = The {1} parameter ''{0}'' was not found
SM_DEPENDENT_PARAM_TYPE_MISMATCH = The parameter ''{0}'' refers to the variable ''{1}'' but it''s type is ''{2}'' which is not compatible with the type required for the group ''{3}'', which is ''{4}'' (from map ''{5}'')
@ -969,4 +970,4 @@ ED_INVARIANT_NO_KEY = The invariant has no key, so the content cannot be validat
ED_INVARIANT_NO_EXPRESSION = The invariant ''{0}'' has no computable expression, so validators will not be able to check it
ED_INVARIANT_EXPRESSION_CONFLICT = The invariant ''{0}'' has an expression ''{1}'', which differs from the earlier expression provided of ''{2}'' (invariants are allowed to repeat, but cannot differ)
ED_INVARIANT_EXPRESSION_ERROR = Error in invariant ''{0}'' with expression ''{1}'': {2}
SNAPSHOT_IS_EMPTY = The snapshot for the profile ''{0}'' is empty (which should not happen)

View File

@ -60,6 +60,10 @@
"type" : "string"
}],
"concept" : [{
"code": "not-open-source",
"display": "Not open source",
"definition": "Not an open source license."
}, {
"code" : "0BSD",
"display" : "BSD Zero Clause License",
"property" : [{

View File

@ -2648,7 +2648,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
String d = sd.getChildValue("derivation");
String k = sd.getChildValue("kind");
if (Utilities.isAbsoluteUrl(v)) {
warning(errors, "2022-11-02", IssueType.INVALID, e.line(), e.col(), path, ns(v).equals(ns(url)) || ns(v).equals(ns(url).replace("StructureDefinition/", "")), I18nConstants.SD_TYPE_NOT_MATCH_NS, v, url);
warning(errors, "2022-11-02", IssueType.INVALID, e.line(), e.col(), path, d.equals("constraint") || ns(v).equals(ns(url)) || ns(v).equals(ns(url).replace("StructureDefinition/", "")), I18nConstants.SD_TYPE_NOT_MATCH_NS, v, url);
return rule(errors, "2022-11-02", IssueType.INVALID, e.line(), e.col(), path, "logical".equals(k), I18nConstants.SD_TYPE_NOT_LOGICAL, v, k);
} else {
boolean tok = false;

View File

@ -484,7 +484,7 @@ public class StructureDefinitionValidator extends BaseValidator {
String expression = invariant.getNamedChildValue("expression");
String source = invariant.getNamedChildValue("source");
if (warning(errors, "2023-06-19", IssueType.INFORMATIONAL, stack, !Utilities.noString(key), I18nConstants.ED_INVARIANT_NO_KEY)) {
if (hint(errors, "2023-06-19", IssueType.INFORMATIONAL, stack, !Utilities.noString(expression), I18nConstants.ED_INVARIANT_NO_EXPRESSION, key)) {
if (hint(errors, "2023-06-19", IssueType.INFORMATIONAL, stack, !Utilities.noString(expression) || VersionUtilities.isR5Plus(context.getVersion()), I18nConstants.ED_INVARIANT_NO_EXPRESSION, key)) { // not for R5 - there's an invariant
if (invariantMap.containsKey(key)) {
// it's legal - and common - for a list of elemnts to contain the same invariant more than once, but it's not valid if it's not always the same
ok = rule(errors, "2023-06-19", IssueType.INVALID, stack, expression.equals(invariantMap.get(key)) || "ele-1".equals(key), I18nConstants.ED_INVARIANT_EXPRESSION_CONFLICT, key, expression, invariantMap.get(key));

View File

@ -470,6 +470,7 @@ public class StructureMapValidator extends BaseValidator {
private boolean validateInput(List<ValidationMessage> errors, Element src, Element group, Element input, NodeStack stack, List<Element> structures, VariableSet variables, VariableSet pvars) {
boolean ok = false;
String gname = group.getChildValue("name");
String name = input.getChildValue("name");
String mode = input.getChildValue("mode");
String type = input.getChildValue("type");
@ -478,6 +479,11 @@ public class StructureMapValidator extends BaseValidator {
pv = pvars.getVariable(name, mode.equals("source"));
if (pv != null) {
type = pv.getWorkingType();
} else {
pv = pvars.getVariable(name, mode.equals("target")); // target can become source
if (pv != null) {
type = pv.getWorkingType();
}
}
}
@ -485,7 +491,7 @@ public class StructureMapValidator extends BaseValidator {
rule(errors, "2023-03-01", IssueType.DUPLICATE, input.line(), input.col(), stack.getLiteralPath(), !variables.hasVariable(name), I18nConstants.SM_GROUP_INPUT_DUPLICATE, name)) { // the name {0} is not valid)
VariableDefn v = variables.add(name, mode);
if (rule(errors, "2023-03-01", IssueType.INVALID, input.line(), input.col(), stack.getLiteralPath(), Utilities.existsInList(mode, "source", "target"), I18nConstants.SM_GROUP_INPUT_MODE_INVALID, name, mode) && // the group parameter {0} mode {1} isn't valid
warning(errors, "2023-03-01", IssueType.NOTSUPPORTED, input.line(), input.col(), stack.getLiteralPath(), type != null, I18nConstants.SM_GROUP_INPUT_NO_TYPE, name)) { // the group parameter {0} has no type, so the paths cannot be validated
warning(errors, "2023-03-01", IssueType.NOTSUPPORTED, input.line(), input.col(), stack.getLiteralPath(), type != null, I18nConstants.SM_GROUP_INPUT_NO_TYPE, name, gname)) { // the group parameter {0} has no type, so the paths cannot be validated
String smode = null;
StructureDefinition sd = null;
ElementDefinition ed = null;
@ -514,7 +520,7 @@ public class StructureMapValidator extends BaseValidator {
ed = sd.getSnapshot().getElementFirstRep();
}
}
if (rule(errors, "2023-03-01", IssueType.NOTSUPPORTED, input.line(), input.col(), stack.getLiteralPath(), smode == null || mode.equals(smode), I18nConstants.SM_GROUP_INPUT_MODE_MISMATCH, type, mode, smode)) { // the type {0} has mode {1} which doesn't match the structure definition {2}
if (rule(errors, "2023-03-01", IssueType.NOTSUPPORTED, input.line(), input.col(), stack.getLiteralPath(), smode == null || mode.equals(smode) || (smode.equals("target") && mode.equals("source")), I18nConstants.SM_GROUP_INPUT_MODE_MISMATCH, type, mode, smode)) { // the type {0} has mode {1} which doesn't match the structure definition {2}
v.setType(1, sd, ed, null);
ok = true;
}
@ -840,8 +846,17 @@ public class StructureMapValidator extends BaseValidator {
// it's just a warning: maybe this'll work out at run time?
warning(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), type != null, I18nConstants.SM_TARGET_TYPE_MULTIPLE_POSSIBLE, el.getEd().typeSummary());
if (ProfileUtilities.isResourceBoundary(el.getEd()) && type != null) {
StructureDefinition sdt = this.context.fetchTypeDefinition(type);
if (rule(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), sdt != null, I18nConstants.SM_TARGET_TRANSFORM_TYPE_UNKNOWN, type)) {
vn.setType(ruleInfo.getMaxCount(), sdt, sdt.getSnapshot().getElementFirstRep(), null); // may overwrite
} else {
vn.setType(ruleInfo.getMaxCount(), el.getSd(), el.getEd(), type); // may overwrite
}
} else {
vn.setType(ruleInfo.getMaxCount(), el.getSd(), el.getEd(), type); // may overwrite
}
}
}
} else {
@ -1143,8 +1158,13 @@ public class StructureMapValidator extends BaseValidator {
String iType = resolveType(grp, input, src);
String pname = input.getName();
VariableDefn v = getParameter(errors, param, pstack, variables, input.getMode());
if (v == null && input.getMode() == StructureMapInputMode.SOURCE) {
// target can transition to the source
v = getParameter(errors, param, pstack, variables, StructureMapInputMode.TARGET);
}
if (rule(errors, "2023-06-27", IssueType.INVALID, param.line(), param.col(), pstack.getLiteralPath(), v != null, I18nConstants.SM_DEPENDENT_PARAM_NOT_FOUND, pname, input.getMode().toCode())) {
if (rule(errors, "2023-03-01", IssueType.INVALID, param.line(), param.col(), pstack.getLiteralPath(), v.mode.equals(input.getMode().toCode()), I18nConstants.SM_DEPENDENT_PARAM_MODE_MISMATCH, param.getChildValue("name"), v.mode, input.getMode().toCode(), grp.getTargetGroup().getName()) &&
if (rule(errors, "2023-03-01", IssueType.INVALID, param.line(), param.col(), pstack.getLiteralPath(),
v.mode.equals(input.getMode().toCode()) || (v.mode.equals("target") && input.getMode() == StructureMapInputMode.SOURCE), I18nConstants.SM_DEPENDENT_PARAM_MODE_MISMATCH, param.getChildValue("name"), v.mode, input.getMode().toCode(), grp.getTargetGroup().getName()) &&
rule(errors, "2023-03-01", IssueType.INVALID, param.line(), param.col(), pstack.getLiteralPath(), typesMatch(v, iType), I18nConstants.SM_DEPENDENT_PARAM_TYPE_MISMATCH,
pname, v.summary(), input.getType(), grp.getTargetGroup().getName(), input.getType(), grp.getTargetMap() == null ? "$this" : grp.getTargetMap().getVersionedUrl())) {
lvars.add(pname, v);

View File

@ -2221,3 +2221,54 @@ v: {
"class" : "UNKNOWN"
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://snomed.info/sct",
"code" : "371525003"
}, "url": "http://fhir.ch/ig/ch-epr-term/ValueSet/DocumentEntry.classCode--0", "version": "2.0.10-cibuild", "langs":"[en]", "useServer":"true", "useClient":"false", "guessSystem":"false", "valueSetMode":"CHECK_MEMERSHIP_ONLY", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
"resourceType" : "Parameters",
"parameter" : [{
"name" : "profile-url",
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
}]
}}####
v: {
"severity" : "error",
"error" : "The provided code http://snomed.info/sct#371525003 is not in the value set 'http://fhir.ch/ig/ch-epr-term/ValueSet/DocumentEntry.classCode--0|2.0.10-cibuild' (from Tx-Server)",
"class" : "UNKNOWN"
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://snomed.info/sct",
"code" : "371525003"
}, "url": "http://fhir.ch/ig/ch-epr-term/ValueSet/DocumentEntry.classCode--1", "version": "2.0.10-cibuild", "langs":"[en]", "useServer":"true", "useClient":"false", "guessSystem":"false", "valueSetMode":"CHECK_MEMERSHIP_ONLY", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
"resourceType" : "Parameters",
"parameter" : [{
"name" : "profile-url",
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
}]
}}####
v: {
"display" : "Clinical procedure report (record artifact)",
"code" : "371525003",
"system" : "http://snomed.info/sct",
"version" : "http://snomed.info/sct/2011000195101/version/20230607"
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://snomed.info/sct",
"code" : "371525003",
"display" : "Clinical procedure report"
}, "url": "http://fhir.ch/ig/ch-epr-term/ValueSet/DocumentEntry.classCode", "version": "2.0.10-cibuild", "langs":"[en]", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"NO_MEMBERSHIP_CHECK", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
"resourceType" : "Parameters",
"parameter" : [{
"name" : "profile-url",
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
}]
}}####
v: {
"display" : "Clinical procedure report",
"code" : "371525003",
"system" : "http://snomed.info/sct",
"version" : "http://snomed.info/sct/900000000000207008/version/20230131"
}
-------------------------------------------------------------------------------------

View File

@ -20,7 +20,7 @@
<properties>
<guava_version>32.0.1-jre</guava_version>
<hapi_fhir_version>6.4.1</hapi_fhir_version>
<validator_test_case_version>1.3.12</validator_test_case_version>
<validator_test_case_version>1.3.13-SNAPSHOT</validator_test_case_version>
<jackson_version>2.14.0</jackson_version>
<junit_jupiter_version>5.9.2</junit_jupiter_version>
<junit_platform_launcher_version>1.8.2</junit_platform_launcher_version>