refactor profile handling in validator

This commit is contained in:
Grahame Grieve 2020-01-16 23:37:30 +11:00
parent 494b268d0b
commit 8482ba43c5
5 changed files with 674 additions and 582 deletions

View File

@ -60,7 +60,12 @@ public interface IResourceValidator {
}
}
public interface IValidationProfileUsageTracker {
void recordProfileUsage(StructureDefinition profile, Object appContext, Element element);
}
public interface IValidatorResourceFetcher {
Element fetch(Object appContext, String url) throws FHIRFormatError, DefinitionException, FHIRException, IOException;
ReferenceValidationPolicy validationPolicy(Object appContext, String path, String url);
boolean resolveURL(Object appContext, String path, String url) throws IOException, FHIRException;
@ -113,7 +118,10 @@ public interface IResourceValidator {
IValidatorResourceFetcher getFetcher();
IResourceValidator setFetcher(IValidatorResourceFetcher value);
IValidationProfileUsageTracker getTracker();
IResourceValidator setTracker(IValidationProfileUsageTracker value);
boolean isNoBindingMsgSuppressed();
IResourceValidator setNoBindingMsgSuppressed(boolean noBindingMsgSuppressed);
@ -133,6 +141,9 @@ public interface IResourceValidator {
public boolean isErrorForUnknownProfiles();
public void setErrorForUnknownProfiles(boolean errorForUnknownProfiles);
public boolean isShowMessagesFromReferences();
public void setShowMessagesFromReferences(boolean value);
public String getValidationLanguage();
public void setValidationLanguage(String value);
@ -155,34 +166,28 @@ public interface IResourceValidator {
* @throws IOException
*/
void validate(Object Context, List<ValidationMessage> errors, org.hl7.fhir.r5.elementmodel.Element element) throws FHIRException;
void validate(Object Context, List<ValidationMessage> errors, org.hl7.fhir.r5.elementmodel.Element element, ValidationProfileSet profiles) throws FHIRException;
void validate(Object Context, List<ValidationMessage> errors, org.hl7.fhir.r5.elementmodel.Element element, String profile) throws FHIRException;
void validate(Object Context, List<ValidationMessage> errors, org.hl7.fhir.r5.elementmodel.Element element, StructureDefinition profile) throws FHIRException;
void validate(Object Context, List<ValidationMessage> errors, org.hl7.fhir.r5.elementmodel.Element element, List<StructureDefinition> profiles) throws FHIRException;
org.hl7.fhir.r5.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, InputStream stream, FhirFormat format) throws FHIRException;
org.hl7.fhir.r5.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, InputStream stream, FhirFormat format, ValidationProfileSet profiles) throws FHIRException;
org.hl7.fhir.r5.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, InputStream stream, FhirFormat format, String profile) throws FHIRException;
org.hl7.fhir.r5.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, InputStream stream, FhirFormat format, StructureDefinition profile) throws FHIRException;
org.hl7.fhir.r5.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, InputStream stream, FhirFormat format, List<StructureDefinition> profiles) throws FHIRException;
org.hl7.fhir.r5.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, org.hl7.fhir.r5.model.Resource resource) throws FHIRException;
org.hl7.fhir.r5.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, org.hl7.fhir.r5.model.Resource resource, ValidationProfileSet profiles) throws FHIRException;
org.hl7.fhir.r5.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, org.hl7.fhir.r5.model.Resource resource, String profile) throws FHIRException;
org.hl7.fhir.r5.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, org.hl7.fhir.r5.model.Resource resource, StructureDefinition profile) throws FHIRException;
org.hl7.fhir.r5.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, org.hl7.fhir.r5.model.Resource resource, List<StructureDefinition> profiles) throws FHIRException;
org.hl7.fhir.r5.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, org.w3c.dom.Element element) throws FHIRException;
org.hl7.fhir.r5.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, org.w3c.dom.Element element, ValidationProfileSet profiles) throws FHIRException;
org.hl7.fhir.r5.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, org.w3c.dom.Element element, String profile) throws FHIRException;
org.hl7.fhir.r5.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, org.w3c.dom.Element element, StructureDefinition profile) throws FHIRException;
org.hl7.fhir.r5.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, org.w3c.dom.Element element, List<StructureDefinition> profile) throws FHIRException;
org.hl7.fhir.r5.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, org.w3c.dom.Document document) throws FHIRException;
org.hl7.fhir.r5.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, org.w3c.dom.Document document, ValidationProfileSet profiles) throws FHIRException;
org.hl7.fhir.r5.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, org.w3c.dom.Document document, String profile) throws FHIRException;
org.hl7.fhir.r5.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, org.w3c.dom.Document document, StructureDefinition profile) throws FHIRException;
org.hl7.fhir.r5.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, org.w3c.dom.Document document, List<StructureDefinition> profile) throws FHIRException;
org.hl7.fhir.r5.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, JsonObject object) throws FHIRException;
org.hl7.fhir.r5.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, JsonObject object, ValidationProfileSet profiles) throws FHIRException;
org.hl7.fhir.r5.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, JsonObject object, String profile) throws FHIRException;
org.hl7.fhir.r5.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, JsonObject object, StructureDefinition profile) throws FHIRException;
org.hl7.fhir.r5.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, JsonObject object, List<StructureDefinition> profile) throws FHIRException;
}

View File

@ -1,111 +0,0 @@
package org.hl7.fhir.r5.utils;
/*-
* #%L
* org.hl7.fhir.r5
* %%
* Copyright (C) 2014 - 2019 Health Level 7
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.hl7.fhir.r5.model.StructureDefinition;
public class ValidationProfileSet {
public static class ProfileRegistration {
private String profile;
private boolean errorOnMissing;
public ProfileRegistration(String profile, boolean errorOnMissing) {
super();
this.profile = profile;
this.errorOnMissing = errorOnMissing;
}
public String getProfile() {
return profile;
}
public boolean isErrorOnMissing() {
return errorOnMissing;
}
}
private List<ProfileRegistration> canonical = new ArrayList<ProfileRegistration>();
private List<StructureDefinition> definitions = new ArrayList<StructureDefinition>();
public ValidationProfileSet(String profile, boolean isError) {
super();
canonical.add(new ProfileRegistration(profile, isError));
}
public ValidationProfileSet() {
super();
}
public ValidationProfileSet(StructureDefinition profile) {
super();
definitions.add(profile);
canonical.add(new ProfileRegistration(profile.getUrl(), true));
}
public ValidationProfileSet(List<String> profiles, boolean isError) {
super();
if (profiles != null)
for (String p : profiles)
canonical.add(new ProfileRegistration(p, isError));
}
public List<String> getCanonicalUrls() {
List<String> res = new ArrayList<String>();
for (ProfileRegistration c : canonical) {
res.add(c.getProfile());
}
return res;
}
public List<StructureDefinition> getDefinitions() {
return definitions;
}
public boolean empty() {
return canonical.isEmpty() && definitions.isEmpty();
}
public List<String> getCanonicalAll() {
Set<String> res = new HashSet<String>();
res.addAll(getCanonicalUrls());
for (StructureDefinition sd : definitions)
res.add(sd.getUrl());
return new ArrayList<String>(res);
}
public List<ProfileRegistration> getCanonical() {
return canonical;
}
public StructureDefinition fetch(String effectiveProfile) {
for (StructureDefinition sd : definitions)
if (effectiveProfile.equals(sd.getUrl()))
return sd;
return null;
}
}

View File

@ -146,7 +146,6 @@ import org.hl7.fhir.r5.utils.StructureMapUtilities;
import org.hl7.fhir.r5.utils.StructureMapUtilities.ITransformerServices;
import org.hl7.fhir.r5.validation.ValidationEngine.ScanOutputItem;
import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.r5.utils.ValidationProfileSet;
import org.hl7.fhir.utilities.IniFile;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.Utilities;
@ -307,6 +306,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
private PrintWriter mapLog;
private boolean debug;
private Set<String> loadedIgs = new HashSet<>();
private IValidatorResourceFetcher fetcher;
private class AsteriskFilter implements FilenameFilter {
String dir;
@ -946,9 +946,23 @@ public class ValidationEngine implements IValidatorResourceFetcher {
public OperationOutcome validate(FhirFormat format, InputStream stream, List<String> profiles) throws Exception {
List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
InstanceValidator validator = getValidator();
validator.validate(null, messages, stream, format, new ValidationProfileSet(profiles, true));
validator.validate(null, messages, stream, format, asSdList(profiles));
return messagesToOutcome(messages);
}
public List<StructureDefinition> asSdList(List<String> profiles) throws Error {
List<StructureDefinition> list = new ArrayList<>();
if (profiles != null) {
for (String p : profiles) {
StructureDefinition sd = context.fetchResource(StructureDefinition.class, p);
if (sd == null) {
throw new Error("Unable to resolve profile "+p);
}
list.add(sd);
}
}
return list;
}
public OperationOutcome validate(String source, List<String> profiles) throws Exception {
List<String> l = new ArrayList<String>();
@ -986,7 +1000,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
try {
System.out.println("Validate "+ref+" against "+ig.getUrl());
messages.clear();
validator.validate(null, messages, new ByteArrayInputStream(cnt.focus), cnt.cntType, new ValidationProfileSet(url, true));
validator.validate(null, messages, new ByteArrayInputStream(cnt.focus), cnt.cntType, url);
res.add(new ScanOutputItem(ref, ig, null, messagesToOutcome(messages)));
} catch (Exception ex) {
res.add(new ScanOutputItem(ref, ig, null, exceptionToOutcome(ex)));
@ -1000,7 +1014,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
try {
System.out.println("Validate "+ref+" against "+sd.getUrl());
messages.clear();
validator.validate(null, messages, new ByteArrayInputStream(cnt.focus), cnt.cntType, new ValidationProfileSet(sd.getUrl(), true));
validator.validate(null, messages, new ByteArrayInputStream(cnt.focus), cnt.cntType, asSdList(sd));
res.add(new ScanOutputItem(ref, ig, sd, messagesToOutcome(messages)));
} catch (Exception ex) {
res.add(new ScanOutputItem(ref, ig, sd, exceptionToOutcome(ex)));
@ -1014,6 +1028,12 @@ public class ValidationEngine implements IValidatorResourceFetcher {
return res;
}
private List<StructureDefinition> asSdList(StructureDefinition sd) {
List<StructureDefinition> res = new ArrayList<StructureDefinition>();
res.add(sd);
return res;
}
private Resource resolve(Reference reference) {
return null;
}
@ -1122,7 +1142,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
validateSHEX(location, messages);
}
InstanceValidator validator = getValidator();
validator.validate(null, messages, new ByteArrayInputStream(source), cntType, new ValidationProfileSet(profiles, true));
validator.validate(null, messages, new ByteArrayInputStream(source), cntType, asSdList(profiles));
return messagesToOutcome(messages);
}
@ -1140,7 +1160,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
validator.setResourceIdRule(resourceIdRule);
validator.setBestPracticeWarningLevel(bpWarnings);
validator.setCheckDisplay(displayOption);
validator.validate(null, messages, new ByteArrayInputStream(source), cntType, new ValidationProfileSet(profiles, true));
validator.validate(null, messages, new ByteArrayInputStream(source), cntType, asSdList(profiles));
return messagesToOutcome(messages);
}
@ -1597,13 +1617,15 @@ public class ValidationEngine implements IValidatorResourceFetcher {
@Override
public Element fetch(Object appContext, String url) throws FHIRFormatError, DefinitionException, FHIRException, IOException {
return null;
return fetcher != null ? fetcher.fetch(appContext, url) : null;
}
@Override
public ReferenceValidationPolicy validationPolicy(Object appContext, String path, String url) {
if (!url.startsWith("http://hl7.org/fhir")) {
return ReferenceValidationPolicy.IGNORE;
} else if (fetcher != null) {
return fetcher.validationPolicy(appContext, path, url);
} else {
return ReferenceValidationPolicy.CHECK_EXISTS_AND_TYPE;
}
@ -1620,6 +1642,9 @@ public class ValidationEngine implements IValidatorResourceFetcher {
"http://hl7.org/fhir/workflow", "http://hl7.org/fhir/ConsentPolicy/opt-out", "http://hl7.org/fhir/ConsentPolicy/opt-in")) {
return true;
}
if (fetcher != null) {
return fetcher.resolveURL(appContext, path, url);
};
return false;
}
@ -1711,6 +1736,14 @@ public class ValidationEngine implements IValidatorResourceFetcher {
context.getExpansionParameters().addParameter("system-version", "http://snomed.info/sct|"+sct);
}
public IValidatorResourceFetcher getFetcher() {
return fetcher;
}
public void setFetcher(IValidatorResourceFetcher fetcher) {
this.fetcher = fetcher;
}
}

View File

@ -119,6 +119,7 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour
throw new Exception("unknown version "+v);
}
vCurr = ve.get(v);
vCurr.setFetcher(this);
TestingUtilities.fcontext = vCurr.getContext();
if (content.has("use-test") && !content.get("use-test").getAsBoolean())
@ -185,11 +186,12 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour
System.out.println("Name: " + name+" - profile : "+profile.get("source").getAsString());
v = content.has("version") ? content.get("version").getAsString() : Constants.VERSION;
StructureDefinition sd = loadProfile(filename, contents, v, messages);
val.getContext().cacheResource(sd);
List<ValidationMessage> errorsProfile = new ArrayList<ValidationMessage>();
if (name.endsWith(".json"))
val.validate(null, errorsProfile, IOUtils.toInputStream(testCaseContent, Charsets.UTF_8), FhirFormat.JSON, sd);
val.validate(null, errorsProfile, IOUtils.toInputStream(testCaseContent, Charsets.UTF_8), FhirFormat.JSON, asSdList(sd));
else
val.validate(null, errorsProfile, IOUtils.toInputStream(testCaseContent, Charsets.UTF_8), FhirFormat.XML, sd);
val.validate(null, errorsProfile, IOUtils.toInputStream(testCaseContent, Charsets.UTF_8), FhirFormat.XML, asSdList(sd));
checkOutcomes(errorsProfile, profile);
}
if (content.has("logical")) {
@ -218,6 +220,11 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour
}
}
private List<StructureDefinition> asSdList(StructureDefinition sd) {
List<StructureDefinition> res = new ArrayList<StructureDefinition>();
res.add(sd);
return res;
}
public StructureDefinition loadProfile(String filename, String contents, String v, List<ValidationMessage> messages) throws IOException, FHIRFormatError, FileNotFoundException, FHIRException, DefinitionException {
StructureDefinition sd = (StructureDefinition) loadResource(filename, contents, v);
@ -355,15 +362,23 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour
@Override
public Element fetch(Object appContext, String url) throws FHIRFormatError, DefinitionException, IOException, FHIRException {
if (url.equals("Patient/test"))
return new ObjectConverter(TestingUtilities.context()).convert(new Patient());
if (TestingUtilities.findTestResource("validator", url.replace("/", "-").toLowerCase()+".json")) {
return Manager.makeParser(TestingUtilities.context(), FhirFormat.JSON).parse(TestingUtilities.loadTestResourceStream("validator", url.replace("/", "-").toLowerCase()+".json"));
Element res = null;
if (url.equals("Patient/test")) {
res = new ObjectConverter(TestingUtilities.context()).convert(new Patient());
} else if (TestingUtilities.findTestResource("validator", url.replace("/", "-").toLowerCase()+".json")) {
res = Manager.makeParser(TestingUtilities.context(), FhirFormat.JSON).parse(TestingUtilities.loadTestResourceStream("validator", url.replace("/", "-").toLowerCase()+".json"));
} else if (TestingUtilities.findTestResource("validator", url.replace("/", "-").toLowerCase()+".xml")) {
res = Manager.makeParser(TestingUtilities.context(), FhirFormat.XML).parse(TestingUtilities.loadTestResourceStream("validator", url.replace("/", "-").toLowerCase()+".xml"));
}
if (TestingUtilities.findTestResource("validator", url.replace("/", "-").toLowerCase()+".xml")) {
return Manager.makeParser(TestingUtilities.context(), FhirFormat.JSON).parse(TestingUtilities.loadTestResourceStream("validator", url.replace("/", "-").toLowerCase()+".xml"));
if (res == null && url.contains("/")) {
String tail = url.substring(url.indexOf("/")+1);
if (TestingUtilities.findTestResource("validator", tail.replace("/", "-").toLowerCase()+".json")) {
res = Manager.makeParser(TestingUtilities.context(), FhirFormat.JSON).parse(TestingUtilities.loadTestResourceStream("validator", tail.replace("/", "-").toLowerCase()+".json"));
} else if (TestingUtilities.findTestResource("validator", tail.replace("/", "-").toLowerCase()+".xml")) {
res = Manager.makeParser(TestingUtilities.context(), FhirFormat.XML).parse(TestingUtilities.loadTestResourceStream("validator", tail.replace("/", "-").toLowerCase()+".xml"));
}
}
return null;
return res;
}
@Override