Merge pull request #44 from lmckenzi/multi-targetprofiles-per-same-resource

1. Cleaned up test cases to work after version changes in R5 (not sur…
This commit is contained in:
Grahame Grieve 2019-06-08 05:24:50 +10:00 committed by GitHub
commit 90f8b829f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 187 additions and 47 deletions

View File

@ -405,7 +405,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
StructureDefinition sd = null;
if (profile.startsWith("#")) {
if (!rule(errors, IssueType.INVALID, element.line(), element.col(), path, sd != null, "StructureDefinition reference \"{0}\" is local, but there is not local context", profile)) {
if (!rule(errors, IssueType.INVALID, element.line(), element.col(), path, containingProfile != null, "StructureDefinition reference \"{0}\" is local, but there is not local context", profile)) {
return false;
}
@ -1863,33 +1863,62 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
// we validate as much as we can. First, can we infer a type from the profile?
if (!type.hasTargetProfile() || type.hasTargetProfile("http://hl7.org/fhir/StructureDefinition/Resource"))
ok = true;
else for (UriType u : type.getTargetProfile()) {
else {
List<String> candidateProfiles = new ArrayList<String>();
for (UriType u : type.getTargetProfile()) {
String pr = u.getValue();
String bt = getBaseType(profile, pr);
StructureDefinition sd = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + bt);
if (rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, bt != null, "Unable to resolve the profile reference '" + pr + "'")) {
b.append(bt);
ok = bt.equals(ft);
if (ok && we!=null && pol.checkValid()) {
doResourceProfile(hostContext, we, pr, errors, stack.push(we, -1, null, null), path, element, profile);
if (bt.equals(ft)) {
ok = true;
if (we!=null && pol.checkValid())
candidateProfiles.add(pr);
}
} else
ok = true; // suppress following check
if (ok && type.hasAggregation()) {
boolean modeOk;
}
}
HashMap<String, List<ValidationMessage>> goodProfiles = new HashMap<String, List<ValidationMessage>>();
List<List<ValidationMessage>> badProfiles = new ArrayList<List<ValidationMessage>>();
List<String> profiles = new ArrayList<String>();
if (!candidateProfiles.isEmpty()) {
for (String pr: candidateProfiles) {
profiles.add(pr);
List<ValidationMessage> profileErrors = new ArrayList<ValidationMessage>();
doResourceProfile(hostContext, we, pr, profileErrors, stack.push(we, -1, null, null), path, element, profile);
if (hasErrors(profileErrors))
badProfiles.add(profileErrors);
else
goodProfiles.put(pr, profileErrors);
if (type.hasAggregation()) {
boolean modeOk = false;
for (Enumeration<AggregationMode> mode : type.getAggregation()) {
if (mode.getValue().equals(AggregationMode.CONTAINED) && refType.equals("contained"))
ok = true;
modeOk = true;
else if (mode.getValue().equals(AggregationMode.BUNDLED) && refType.equals("bundled"))
ok = true;
modeOk = true;
else if (mode.getValue().equals(AggregationMode.REFERENCED) && (refType.equals("bundled")||refType.equals("remote")))
ok = true;
modeOk = true;
}
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, modeOk, "Reference is " + refType + " which isn't supported by the specified aggregation mode(s) for the reference");
}
}
if (goodProfiles.size()==1) {
errors.addAll(goodProfiles.values().iterator().next());
} else if (goodProfiles.size()==0) {
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, profiles.size()==1, "Unable to find matching profile among choices: " + StringUtils.join("; ", profiles));
for (List<ValidationMessage> messages : badProfiles) {
errors.addAll(messages);
}
} else {
warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, "Found multiple matching profiles among choices: " + StringUtils.join("; ", goodProfiles.keySet()));
for (List<ValidationMessage> messages : goodProfiles.values()) {
errors.addAll(messages);
}
}
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ok, "Reference is " + refType + " which isn't supported by the specified aggregation mode(s) for the reference");
}
if (ok)
break;
}
}
if (!ok && type.getCode().equals("*")) {
@ -4001,14 +4030,7 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L
if (rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.path, p != null, "Unknown profile " + typeProfile)) {
List<ValidationMessage> profileErrors = new ArrayList<ValidationMessage>();
validateElement(hostContext, profileErrors, p, getElementByTail(p, tail), profile, ei.definition, resource, ei.element, type, localStack, thisIsCodeableConcept, checkDisplay);
boolean hasError = false;
for (ValidationMessage msg : profileErrors) {
if (msg.getLevel()==ValidationMessage.IssueSeverity.ERROR || msg.getLevel()==ValidationMessage.IssueSeverity.FATAL) {
hasError = true;
break;
}
}
if (hasError)
if (hasErrors(profileErrors))
badProfiles.add(profileErrors);
else
goodProfiles.put(typeProfile, profileErrors);

View File

@ -101,7 +101,9 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour
@SuppressWarnings("deprecation")
@Test
public void test() throws Exception {
System.out.println("Name: " + name);
String v = "5.0";
List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
if (content.has("version"))
v = content.get("version").getAsString();
@ -138,8 +140,9 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour
if (content.has("profiles")) {
for (JsonElement je : content.getAsJsonArray("profiles")) {
String p = je.getAsString();
System.out.println("Profile: " + p);
String filename = TestUtilities.resourceNameToFile("validation-examples", p);
StructureDefinition sd = loadProfile(filename, v);
StructureDefinition sd = loadProfile(filename, v, messages);
val.getContext().cacheResource(sd);
}
}
@ -154,7 +157,7 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour
JsonObject profile = content.getAsJsonObject("profile");
String filename = TestUtilities.resourceNameToFile("validation-examples", profile.get("source").getAsString());
v = content.has("version") ? content.get("version").getAsString() : Constants.VERSION;
StructureDefinition sd = loadProfile(filename, v);
StructureDefinition sd = loadProfile(filename, v, messages);
if (name.startsWith("Json."))
val.validate(null, errorsProfile, new FileInputStream(path), FhirFormat.JSON, sd);
else
@ -163,14 +166,23 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour
}
}
public StructureDefinition loadProfile(String filename, String v) throws IOException, FHIRFormatError, FileNotFoundException, FHIRException, DefinitionException {
public StructureDefinition loadProfile(String filename, String v, List<ValidationMessage> messages) throws IOException, FHIRFormatError, FileNotFoundException, FHIRException, DefinitionException {
StructureDefinition sd = (StructureDefinition) loadResource(filename, v);
ProfileUtilities pu = new ProfileUtilities(TestingUtilities.context(), messages, null);
if (!sd.hasSnapshot()) {
ProfileUtilities pu = new ProfileUtilities(TestingUtilities.context(), null, null);
StructureDefinition base = TestingUtilities.context().fetchResource(StructureDefinition.class, sd.getBaseDefinition());
pu.generateSnapshot(base, sd, sd.getUrl(), null, sd.getTitle());
// (debugging) new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path("[tmp]", sd.getId()+".xml")), sd);
}
for (Resource r: sd.getContained()) {
if (r instanceof StructureDefinition) {
StructureDefinition childSd = (StructureDefinition)r;
if (!childSd.hasSnapshot()) {
StructureDefinition base = TestingUtilities.context().fetchResource(StructureDefinition.class, childSd.getBaseDefinition());
pu.generateSnapshot(base, childSd, childSd.getUrl(), null, childSd.getTitle());
}
}
}
return sd;
}

View File

@ -360,6 +360,7 @@
]
},
"slice-by-polymorphic-type.xml": {
"version": "4.0",
"errorCount": 0,
"profile": {
"source": "slice-by-polymorphic-type-profile.xml",
@ -411,6 +412,7 @@
}
},
"slicing-types-by-string.xml": {
"version": "4.0",
"errorCount": 0,
"profile": {
"source": "slicing-types-by-string-profile.xml",
@ -729,6 +731,7 @@
"source": "patient-translated-codes.profile.xml",
"errorCount": 0,
"warningCount": 0
}
},
"patient-contained-org.xml": {
"errorCount": 0,
@ -736,6 +739,12 @@
"source": "patient-contained-org-profile.xml",
"errorCount": 1
}
},
"multi-profile-same-resource.xml": {
"errorCount": 0,
"profile": {
"source": "multi-profile-same-resource-profile.xml",
"errorCount": 0
}
}
}

View File

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<StructureDefinition xmlns="http://hl7.org/fhir">
<id value="multi-profile-same-resource-profile"/>
<contained>
<StructureDefinition>
<id value="pract1"/>
<url value="http://hl7.org/fhir/StructureDefinition/multi-profile-same-resource-profile-pract1"/>
<name value="MultiProfileSameResourceProfilePract1"/>
<status value="draft"/>
<kind value="resource"/>
<abstract value="false"/>
<type value="Practitioner"/>
<baseDefinition value="http://hl7.org/fhir/StructureDefinition/Practitioner"/>
<derivation value="constraint"/>
<differential>
<element id="Practitioner">
<path value="Practitioner"/>
</element>
<element id="Practitioner.name">
<path value="Practitioner.name"/>
<min value="1"/>
</element>
<element id="Practitioner.gender">
<path value="Practitioner.gender"/>
<max value="0"/>
</element>
</differential>
</StructureDefinition>
</contained>
<contained>
<StructureDefinition>
<id value="pract2"/>
<url value="http://hl7.org/fhir/StructureDefinition/multi-profile-same-resource-profile-pract2"/>
<name value="MultiProfileSameResourceProfilePract2"/>
<status value="draft"/>
<kind value="resource"/>
<abstract value="false"/>
<type value="Practitioner"/>
<baseDefinition value="http://hl7.org/fhir/StructureDefinition/Practitioner"/>
<derivation value="constraint"/>
<differential>
<element id="Practitioner">
<path value="Practitioner"/>
</element>
<element id="Practitioner.name">
<path value="Practitioner.name"/>
<max value="0"/>
</element>
<element id="Practitioner.gender">
<path value="Practitioner.gender"/>
<min value="1"/>
</element>
</differential>
</StructureDefinition>
</contained>
<url value="http://hl7.org/fhir/StructureDefinition/multi-profile-same-resource-profile"/>
<name value="MultiProfileSameResourceProfile"/>
<status value="draft"/>
<description value="Profile with multiple profiles allowed for teh same resource"/>
<kind value="resource"/>
<abstract value="false"/>
<type value="Patient"/>
<baseDefinition value="http://hl7.org/fhir/StructureDefinition/Patient"/>
<derivation value="constraint"/>
<differential>
<element id="Patient">
<path value="Patient"/>
</element>
<element id="Patient.generalPractitioner">
<path value="Patient.generalPractitioner"/>
<min value="1"/>
<max value="1"/>
<type>
<code value="Reference"/>
<targetProfile value="#pract1"/>
<targetProfile value="#pract2"/>
</type>
</element>
</differential>
</StructureDefinition>

View File

@ -0,0 +1,17 @@
<Patient xmlns="http://hl7.org/fhir">
<id value="multi-profile-same-resource"/>
<text>
<status value="generated"/>
<div xmlns="http://www.w3.org/1999/xhtml">
</div>
</text>
<contained>
<Practitioner>
<id value="practitioner"/>
<gender value="other"/>
</Practitioner>
</contained>
<generalPractitioner>
<reference value="#practitioner"/>
</generalPractitioner>
</Patient>

View File

@ -2,7 +2,7 @@
"resourceType": "Encounter",
"id": "f003",
"class" : {
"system" : "http://hl7.org/fhir/v3/ActCode",
"system" : "http://terminology.hl7.org/CodeSystem/v3-ActCode",
"code" : "AMB"
},
"text": {
@ -22,7 +22,7 @@
"reference": "Patient/f001",
"display": "P. van de Heuvel"
},
"reason": {
"reasonCode": {
"extension": [
{
"url": "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor",