mirror of
https://github.com/hapifhir/org.hl7.fhir.core.git
synced 2025-03-06 19:39:27 +00:00
v2 tests, support discriminator by position, and don't check type characteristics for unknown types
This commit is contained in:
parent
a8ba174383
commit
b766cffdb1
@ -899,7 +899,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
|
||||
TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED));
|
||||
} else if (noTerminologyServer) {
|
||||
t.setResult(new ValidationResult(IssueSeverity.ERROR,
|
||||
formatMessage(I18nConstants.ERROR_VALIDATING_CODE_RUNNING_WITHOUT_TERMINOLOGY_SERVICES),
|
||||
formatMessage(I18nConstants.ERROR_VALIDATING_CODE_RUNNING_WITHOUT_TERMINOLOGY_SERVICES, t.getCoding().getCode(), t.getCoding().getSystem()),
|
||||
TerminologyServiceErrorClass.NOSERVICE));
|
||||
}
|
||||
}
|
||||
@ -1019,7 +1019,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
|
||||
// if that failed, we try to validate on the server
|
||||
if (noTerminologyServer) {
|
||||
return new ValidationResult(IssueSeverity.ERROR,
|
||||
formatMessage(I18nConstants.ERROR_VALIDATING_CODE_RUNNING_WITHOUT_TERMINOLOGY_SERVICES),
|
||||
formatMessage(I18nConstants.ERROR_VALIDATING_CODE_RUNNING_WITHOUT_TERMINOLOGY_SERVICES, code.getCode(), code.getSystem()),
|
||||
TerminologyServiceErrorClass.NOSERVICE);
|
||||
}
|
||||
String csumm = txCache != null ? txCache.summary(code) : null;
|
||||
|
@ -1082,7 +1082,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
|
||||
} else if (unsupportedCodeSystems.contains(codeKey)) {
|
||||
t.setResult(new ValidationResult(IssueSeverity.ERROR,formatMessage(I18nConstants.UNKNOWN_CODESYSTEM, t.getCoding().getSystem()), TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED, null));
|
||||
} else if (noTerminologyServer) {
|
||||
t.setResult(new ValidationResult(IssueSeverity.ERROR,formatMessage(I18nConstants.ERROR_VALIDATING_CODE_RUNNING_WITHOUT_TERMINOLOGY_SERVICES), TerminologyServiceErrorClass.NOSERVICE, null));
|
||||
t.setResult(new ValidationResult(IssueSeverity.ERROR,formatMessage(I18nConstants.ERROR_VALIDATING_CODE_RUNNING_WITHOUT_TERMINOLOGY_SERVICES, t.getCoding().getCode(), t.getCoding().getSystem()), TerminologyServiceErrorClass.NOSERVICE, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1190,7 +1190,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
|
||||
} else if (unsupportedCodeSystems.contains(codeKey)) {
|
||||
t.setResult(new ValidationResult(IssueSeverity.ERROR,formatMessage(I18nConstants.UNKNOWN_CODESYSTEM, t.getCoding().getSystem()), TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED, null));
|
||||
} else if (noTerminologyServer) {
|
||||
t.setResult(new ValidationResult(IssueSeverity.ERROR,formatMessage(I18nConstants.ERROR_VALIDATING_CODE_RUNNING_WITHOUT_TERMINOLOGY_SERVICES), TerminologyServiceErrorClass.NOSERVICE, null));
|
||||
t.setResult(new ValidationResult(IssueSeverity.ERROR,formatMessage(I18nConstants.ERROR_VALIDATING_CODE_RUNNING_WITHOUT_TERMINOLOGY_SERVICES, t.getCoding().getCode(), t.getCoding().getSystem()), TerminologyServiceErrorClass.NOSERVICE, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1346,7 +1346,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
|
||||
|
||||
// if that failed, we try to validate on the server
|
||||
if (noTerminologyServer) {
|
||||
return new ValidationResult(IssueSeverity.ERROR,formatMessage(I18nConstants.ERROR_VALIDATING_CODE_RUNNING_WITHOUT_TERMINOLOGY_SERVICES), TerminologyServiceErrorClass.NOSERVICE, issues);
|
||||
return new ValidationResult(IssueSeverity.ERROR,formatMessage(I18nConstants.ERROR_VALIDATING_CODE_RUNNING_WITHOUT_TERMINOLOGY_SERVICES, code.getCode(), code.getSystem()), TerminologyServiceErrorClass.NOSERVICE, issues);
|
||||
}
|
||||
|
||||
Set<String> systems = findRelevantSystems(code, vs);
|
||||
|
@ -963,6 +963,15 @@ public class ValidationMessage implements Comparator<ValidationMessage>, Compara
|
||||
|
||||
public void incCount() {
|
||||
count++;
|
||||
}
|
||||
|
||||
public boolean containsText(List<String> fragements) {
|
||||
for (String s : fragements) {
|
||||
if ((getMessage() != null && getMessage().contains(s)) || (getMessageId() != null && getMessageId().contains(s))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -4960,7 +4960,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
* @throws IOException
|
||||
* @throws FHIRException
|
||||
*/
|
||||
private boolean sliceMatches(ValidationContext valContext, Element element, String path, ElementDefinition slicer, ElementDefinition ed, StructureDefinition profile, List<ValidationMessage> errors, List<ValidationMessage> sliceInfo, NodeStack stack, StructureDefinition srcProfile) throws DefinitionException, FHIRException {
|
||||
private boolean sliceMatches(ValidationContext valContext, Element element, String path, ElementDefinition slicer, List<ElementDefinition> slicerSlices, ElementDefinition ed, StructureDefinition profile, List<ValidationMessage> errors, List<ValidationMessage> sliceInfo, NodeStack stack, StructureDefinition srcProfile) throws DefinitionException, FHIRException {
|
||||
if (!slicer.getSlicing().hasDiscriminator())
|
||||
return false; // cannot validate in this case
|
||||
|
||||
@ -5034,6 +5034,25 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
} else {
|
||||
throw new FHIRException(context.formatMessage(I18nConstants.DISCRIMINATOR__IS_BASED_ON_ELEMENT_EXISTENCE_BUT_SLICE__NEITHER_SETS_MIN1_OR_MAX0, discriminator, ed.getId()));
|
||||
}
|
||||
} else if (s.getType() == DiscriminatorType.POSITION) {
|
||||
// we don't evaluate this one using FHIRPath, and it can't share
|
||||
if (slicer.getSlicing().getDiscriminator().size() != 1) {
|
||||
throw new DefinitionException(context.formatMessagePlural(slicer.getSlicing().getDiscriminator().size(), I18nConstants.Could_not_match_discriminator_for_slice_in_profile, discriminators, ed.getId(), profile.getVersionedUrl(), discriminators));
|
||||
} else {
|
||||
int offset = 0;
|
||||
for (ElementDefinition ts : slicerSlices) {
|
||||
if (ts == ed) {
|
||||
break;
|
||||
} else if (!ts.getMax().equals(Integer.toString(ts.getMin()))) {
|
||||
throw new DefinitionException(context.formatMessagePlural(slicer.getSlicing().getDiscriminator().size(), I18nConstants.Could_not_match_discriminator_for_slice_in_profile, discriminators, ed.getId(), profile.getVersionedUrl(), discriminators));
|
||||
} else {
|
||||
offset = offset + ts.getMin();
|
||||
}
|
||||
}
|
||||
int maxPos = (ed.getMax().equals("*") ? Integer.MAX_VALUE : offset + Integer.parseInt(ed.getMax()));
|
||||
int position = path.endsWith("]") ? Integer.parseInt(path.substring(path.lastIndexOf("[")+1).replace("]", "")) : 0;
|
||||
return position >= offset && position < maxPos;
|
||||
}
|
||||
} else if (criteriaElement.hasFixed()) {
|
||||
buildFixedExpression(ed, expression, discriminator, criteriaElement);
|
||||
} else if (criteriaElement.hasPattern()) {
|
||||
@ -6808,6 +6827,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
// 2. assign children to a definition
|
||||
// for each definition, for each child, check whether it belongs in the slice
|
||||
ElementDefinition slicer = null;
|
||||
List<ElementDefinition> slicerSlices = null;
|
||||
|
||||
boolean unsupportedSlicing = false;
|
||||
List<String> problematicPaths = new ArrayList<String>();
|
||||
String slicingPath = null;
|
||||
@ -6835,14 +6856,22 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
slicer = ed;
|
||||
process = false;
|
||||
sliceOffset = i;
|
||||
} else if (slicer != null && !slicer.getPath().equals(ed.getPath()))
|
||||
slicerSlices = new ArrayList<>();
|
||||
for (int j = i+1; j < childDefinitions.getList().size(); j++) {
|
||||
if (ed.getPath().equals(childDefinitions.getList().get(j).getPath())) {
|
||||
slicerSlices.add(childDefinitions.getList().get(j));
|
||||
}
|
||||
}
|
||||
} else if (slicer != null && !slicer.getPath().equals(ed.getPath())) {
|
||||
slicer = null;
|
||||
slicerSlices = null;
|
||||
}
|
||||
|
||||
for (ElementInfo ei : children) {
|
||||
if (ei.sliceInfo == null) {
|
||||
ei.sliceInfo = new ArrayList<>();
|
||||
}
|
||||
unsupportedSlicing = matchSlice(valContext, errors, ei.sliceInfo, profile, stack, slicer, unsupportedSlicing, problematicPaths, sliceOffset, i, ed, childUnsupportedSlicing, ei, bh);
|
||||
unsupportedSlicing = matchSlice(valContext, errors, ei.sliceInfo, profile, stack, slicer, slicerSlices, unsupportedSlicing, problematicPaths, sliceOffset, i, ed, childUnsupportedSlicing, ei, bh);
|
||||
}
|
||||
}
|
||||
int last = -1;
|
||||
@ -6928,7 +6957,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
}
|
||||
|
||||
public boolean matchSlice(ValidationContext valContext, List<ValidationMessage> errors, List<ValidationMessage> sliceInfo, StructureDefinition profile, NodeStack stack,
|
||||
ElementDefinition slicer, boolean unsupportedSlicing, List<String> problematicPaths, int sliceOffset, int i, ElementDefinition ed,
|
||||
ElementDefinition slicer, List<ElementDefinition> slicerSlices, boolean unsupportedSlicing, List<String> problematicPaths, int sliceOffset, int i, ElementDefinition ed,
|
||||
boolean childUnsupportedSlicing, ElementInfo ei, BooleanHolder bh) {
|
||||
boolean match = false;
|
||||
if (slicer == null || slicer == ed) {
|
||||
@ -6937,7 +6966,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
if (nameMatches(ei.getName(), tail(ed.getPath())))
|
||||
try {
|
||||
// System.out.println("match slices for "+stack.getLiteralPath()+": "+slicer.getId()+" = "+slicingSummary(slicer.getSlicing()));
|
||||
match = sliceMatches(valContext, ei.getElement(), ei.getPath(), slicer, ed, profile, errors, sliceInfo, stack, profile);
|
||||
match = sliceMatches(valContext, ei.getElement(), ei.getPath(), slicer, slicerSlices, ed, profile, errors, sliceInfo, stack, profile);
|
||||
if (match) {
|
||||
ei.slice = slicer;
|
||||
|
||||
|
@ -411,9 +411,11 @@ public class StructureDefinitionValidator extends BaseValidator {
|
||||
List<Element> types = element.getChildrenByName("type");
|
||||
Set<String> typeCodes = new HashSet<>();
|
||||
Set<String> characteristics = new HashSet<>();
|
||||
boolean characteristicsValid = false;
|
||||
if (!path.contains(".")) {
|
||||
typeCodes.add(path); // root is type
|
||||
addCharacteristics(characteristics, path);
|
||||
characteristicsValid = true;
|
||||
}
|
||||
|
||||
if (!snapshot && (element.hasChild("fixed") || element.hasChild("pattern")) && base != null) {
|
||||
@ -463,17 +465,20 @@ public class StructureDefinitionValidator extends BaseValidator {
|
||||
typeCodes.add(tc);
|
||||
Set<String> tcharacteristics = new HashSet<>();
|
||||
StructureDefinition tsd = context.fetchTypeDefinition(tc);
|
||||
if (tsd != null && tsd.hasExtension(ToolingExtensions.EXT_TYPE_CHARACTERISTICS)) {
|
||||
for (Extension ext : tsd.getExtensionsByUrl(ToolingExtensions.EXT_TYPE_CHARACTERISTICS)) {
|
||||
tcharacteristics.add(ext.getValue().primitiveValue());
|
||||
if (tsd != null) {
|
||||
characteristicsValid = true;
|
||||
if (tsd.hasExtension(ToolingExtensions.EXT_TYPE_CHARACTERISTICS)) {
|
||||
for (Extension ext : tsd.getExtensionsByUrl(ToolingExtensions.EXT_TYPE_CHARACTERISTICS)) {
|
||||
tcharacteristics.add(ext.getValue().primitiveValue());
|
||||
}
|
||||
} else {
|
||||
// nothing specified, so infer from known types
|
||||
addCharacteristics(tcharacteristics, tc);
|
||||
}
|
||||
characteristics.addAll(tcharacteristics);
|
||||
if (type.hasChildren("targetProfile")) {
|
||||
ok = rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), tcharacteristics.contains("has-target") , I18nConstants.SD_ILLEGAL_CHARACTERISTICS, "targetProfile", tc) && ok;
|
||||
}
|
||||
} else {
|
||||
// nothing specified, so infer from known types
|
||||
addCharacteristics(tcharacteristics, tc);
|
||||
}
|
||||
characteristics.addAll(tcharacteristics);
|
||||
if (type.hasChildren("targetProfile")) {
|
||||
ok = rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), tcharacteristics.contains("has-target") , I18nConstants.SD_ILLEGAL_CHARACTERISTICS, "targetProfile", tc) && ok;
|
||||
}
|
||||
// check the stated profile - must be a constraint on the type
|
||||
if (snapshot || sd != null) {
|
||||
@ -489,7 +494,7 @@ public class StructureDefinitionValidator extends BaseValidator {
|
||||
}
|
||||
}
|
||||
if (element.hasChild("binding", false)) {
|
||||
if (!typeCodes.isEmpty()) {
|
||||
if (!typeCodes.isEmpty() && characteristicsValid) {
|
||||
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);
|
||||
@ -499,7 +504,7 @@ public class StructureDefinitionValidator extends BaseValidator {
|
||||
// String bt = boundType(typeCodes);
|
||||
// hint(errors, UNKNOWN_DATE_TIME, IssueType.BUSINESSRULE, stack.getLiteralPath(), !snapshot || bt == null, I18nConstants.SD_ED_SHOULD_BIND, element.getNamedChildValue("path", false), bt);
|
||||
}
|
||||
if (!typeCodes.isEmpty()) {
|
||||
if (!typeCodes.isEmpty() && characteristicsValid) {
|
||||
if (element.hasChild("maxLength", false)) {
|
||||
ok = rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), characteristics.contains("has-length") , I18nConstants.SD_ILLEGAL_CHARACTERISTICS, "MaxLength", typeCodes) && ok;
|
||||
}
|
||||
|
@ -353,13 +353,20 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
|
||||
if (content.has("noHtmlInMarkdown")) {
|
||||
val.setHtmlInMarkdownCheck(HtmlInMarkdownCheck.ERROR);
|
||||
}
|
||||
List<String> suppress = new ArrayList<>();
|
||||
if (content.has("suppress")) {
|
||||
for (JsonElement c : content.getAsJsonArray("suppress")) {
|
||||
suppress.add(c.getAsString());
|
||||
}
|
||||
}
|
||||
|
||||
val.setSignatureServices(this);
|
||||
if (content.has("logical")==false) {
|
||||
val.setAssumeValidRestReferences(content.has("assumeValidRestReferences") ? content.get("assumeValidRestReferences").getAsBoolean() : false);
|
||||
logOutput(String.format("Start Validating (%d to set up)", (System.nanoTime() - setup) / 1000000));
|
||||
val.validate(null, errors, new ByteArrayInputStream(testCaseContent), fmt);
|
||||
logOutput(val.reportTimes());
|
||||
checkOutcomes(errors, content, null, name);
|
||||
checkOutcomes(errors, content, null, name, suppress);
|
||||
}
|
||||
if (content.has("profile")) {
|
||||
System.out.print("** Profile: ");
|
||||
@ -388,6 +395,11 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
|
||||
}
|
||||
}
|
||||
}
|
||||
if (content.has("suppress")) {
|
||||
for (JsonElement c : content.getAsJsonArray("suppress")) {
|
||||
suppress.add(c.getAsString());
|
||||
}
|
||||
}
|
||||
String filename = profile.get("source").getAsString();
|
||||
if (Utilities.isAbsoluteUrl(filename)) {
|
||||
sd = val.getContext().fetchResource(StructureDefinition.class, filename);
|
||||
@ -403,7 +415,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
|
||||
List<ValidationMessage> errorsProfile = new ArrayList<ValidationMessage>();
|
||||
val.validate(null, errorsProfile, new ByteArrayInputStream(testCaseContent), fmt, asSdList(sd));
|
||||
logOutput(val.reportTimes());
|
||||
checkOutcomes(errorsProfile, profile, filename, name);
|
||||
checkOutcomes(errorsProfile, profile, filename, name, suppress);
|
||||
}
|
||||
if (content.has("logical")) {
|
||||
System.out.print("** Logical: ");
|
||||
@ -445,7 +457,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
|
||||
Assert.assertTrue(fp.evaluateToBoolean(null, le, le, le, fp.parse(exp)));
|
||||
}
|
||||
}
|
||||
checkOutcomes(errorsLogical, logical, "logical", name);
|
||||
checkOutcomes(errorsLogical, logical, "logical", name, suppress);
|
||||
}
|
||||
logger.verifyHasNoRequests();
|
||||
}
|
||||
@ -457,7 +469,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
|
||||
}
|
||||
|
||||
private ValidationEngine buildVersionEngine(String ver, String txLog) throws Exception {
|
||||
String server = FhirSettings.getTxFhirDevelopment();
|
||||
String server = FhirSettings.getTxFhirLocal();
|
||||
switch (ver) {
|
||||
case "1.0": return TestUtilities.getValidationEngine("hl7.fhir.r2.core#1.0.2", server, txLog, FhirPublication.DSTU2, true, "1.0.2");
|
||||
case "1.4": return TestUtilities.getValidationEngine("hl7.fhir.r2b.core#1.4.0", server, txLog, FhirPublication.DSTU2016May, true, "1.4.0");
|
||||
@ -533,7 +545,8 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
|
||||
}
|
||||
}
|
||||
|
||||
private void checkOutcomes(List<ValidationMessage> errors, JsonObject focus, String profile, String name) throws IOException {
|
||||
private void checkOutcomes(List<ValidationMessage> errors, JsonObject focus, String profile, String name, List<String> suppress) throws IOException {
|
||||
errors.removeIf(vm -> vm.containsText(suppress));
|
||||
JsonObject java = focus.getAsJsonObject("java");
|
||||
OperationOutcome goal = java.has("outcome") ? (OperationOutcome) new JsonParser().parse(java.getAsJsonObject("outcome")) : new OperationOutcome();
|
||||
OperationOutcome actual = OperationOutcomeUtilities.createOutcomeSimple(errors);
|
||||
|
Loading…
x
Reference in New Issue
Block a user