rework the way intermediary content is handled in the validator + fix impose-profile handling for IPS-AU
This commit is contained in:
parent
be4957c9af
commit
5cc4e3ef03
|
@ -50,7 +50,7 @@ public class FmlParser extends ParserBase {
|
|||
ByteArrayInputStream stream = new ByteArrayInputStream(content);
|
||||
String text = TextFile.streamToString(stream);
|
||||
List<NamedElement> result = new ArrayList<>();
|
||||
NamedElement ctxt = new NamedElement("fml", content);
|
||||
NamedElement ctxt = new NamedElement("focus", "fml", content);
|
||||
ctxt.setElement(parse(ctxt.getErrors(), text));
|
||||
result.add(ctxt);
|
||||
return result;
|
||||
|
|
|
@ -119,7 +119,7 @@ public class JsonParser extends ParserBase {
|
|||
@Override
|
||||
public List<NamedElement> parse(InputStream inStream) throws IOException, FHIRException {
|
||||
byte[] content = TextFile.streamToBytes(inStream);
|
||||
NamedElement ctxt = new NamedElement("json", content);
|
||||
NamedElement ctxt = new NamedElement("focus", "json", content);
|
||||
|
||||
ByteArrayInputStream stream = new ByteArrayInputStream(content);
|
||||
|
||||
|
|
|
@ -71,21 +71,24 @@ public abstract class ParserBase {
|
|||
|
||||
public class NamedElement {
|
||||
private String name;
|
||||
private String extension;
|
||||
private Element element;
|
||||
private byte[] content;
|
||||
private List<ValidationMessage> errors = new ArrayList<>();
|
||||
|
||||
public NamedElement(String name, Element element, byte[] content) {
|
||||
public NamedElement(String name, String extension, Element element, byte[] content) {
|
||||
super();
|
||||
this.name = name;
|
||||
this.element = element;
|
||||
this.content = content;
|
||||
this.extension = extension;
|
||||
}
|
||||
|
||||
public NamedElement(String name, byte[] content) {
|
||||
public NamedElement(String name, String extension, byte[] content) {
|
||||
super();
|
||||
this.name = name;
|
||||
this.content = content;
|
||||
this.extension = extension;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
|
@ -106,7 +109,10 @@ public abstract class ParserBase {
|
|||
|
||||
public void setElement(Element element) {
|
||||
this.element = element;
|
||||
|
||||
}
|
||||
|
||||
public String getFilename() {
|
||||
return name+"."+extension;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ public class SHCParser extends ParserBase {
|
|||
byte[] content = TextFile.streamToBytes(inStream);
|
||||
ByteArrayInputStream stream = new ByteArrayInputStream(content);
|
||||
List<NamedElement> res = new ArrayList<>();
|
||||
NamedElement shc = new NamedElement("shc", content);
|
||||
NamedElement shc = new NamedElement("shc", "json", content);
|
||||
res.add(shc);
|
||||
|
||||
String src = TextFile.streamToString(stream).trim();
|
||||
|
@ -146,7 +146,7 @@ public class SHCParser extends ParserBase {
|
|||
return res;
|
||||
}
|
||||
// ok. all checks passed, we can now validate the bundle
|
||||
NamedElement bnd = new NamedElement(path, org.hl7.fhir.utilities.json.parser.JsonParser.composeBytes(cs.getJsonObject("fhirBundle")));
|
||||
NamedElement bnd = new NamedElement(path, "json", org.hl7.fhir.utilities.json.parser.JsonParser.composeBytes(cs.getJsonObject("fhirBundle")));
|
||||
res.add(bnd);
|
||||
bnd.setElement(jsonParser.parse(bnd.getErrors(), cs.getJsonObject("fhirBundle")));
|
||||
}
|
||||
|
|
|
@ -76,7 +76,7 @@ public class SHLParser extends ParserBase {
|
|||
byte[] content = TextFile.streamToBytes(inStream);
|
||||
|
||||
List<NamedElement> res = new ArrayList<>();
|
||||
NamedElement shl = addNamedElement(res, "shl", content);
|
||||
NamedElement shl = addNamedElement(res, "shl", "txt", content);
|
||||
String src = TextFile.bytesToString(content);
|
||||
|
||||
if (src.startsWith("shlink:/")) {
|
||||
|
@ -92,8 +92,8 @@ public class SHLParser extends ParserBase {
|
|||
src = null;
|
||||
}
|
||||
if (src != null) {
|
||||
NamedElement json = addNamedElement(res, "json", TextFile.stringToBytes(src, false));
|
||||
byte[] cntin = Base64.getUrlDecoder().decode(src);
|
||||
NamedElement json = addNamedElement(res, "json", "json", cntin);
|
||||
JsonObject j = null;
|
||||
try {
|
||||
j = org.hl7.fhir.utilities.json.parser.JsonParser.parseObject(cntin);
|
||||
|
@ -146,7 +146,7 @@ public class SHLParser extends ParserBase {
|
|||
|
||||
|
||||
private void checkManifest(List<NamedElement> res, HTTPResult cnt) throws IOException {
|
||||
NamedElement manifest = addNamedElement(res, "manifest", cnt.getContent());
|
||||
NamedElement manifest = addNamedElement(res, "manifest", "json", cnt.getContent());
|
||||
|
||||
if (!cnt.getContentType().equals("application/json")) {
|
||||
logError(manifest.getErrors(), "202-08-31", 1, 1, "manifest", IssueType.STRUCTURE, "The mime type should be application/json not "+cnt.getContentType(), IssueSeverity.ERROR);
|
||||
|
@ -243,6 +243,7 @@ public class SHLParser extends ParserBase {
|
|||
}
|
||||
|
||||
private void processContent(List<NamedElement> res, List<ValidationMessage> errors, String path, String name, String jose, String ct) throws FHIRFormatError, DefinitionException, FHIRException, IOException {
|
||||
NamedElement bin = addNamedElement(res, "encrypted", "jose", TextFile.stringToBytes(jose, false));
|
||||
byte[] cnt = null;
|
||||
JWEObject jwe;
|
||||
try {
|
||||
|
@ -250,10 +251,9 @@ public class SHLParser extends ParserBase {
|
|||
jwe.decrypt(new DirectDecrypter(key));
|
||||
cnt = jwe.getPayload().toBytes();
|
||||
} catch (Exception e) {
|
||||
logError(errors, "202-08-31", 1, 1, path, IssueType.STRUCTURE, "Decruption failed: "+e.getMessage(), IssueSeverity.ERROR);
|
||||
logError(bin.getErrors(), "202-08-31", 1, 1, path, IssueType.STRUCTURE, "Decruption failed: "+e.getMessage(), IssueSeverity.ERROR);
|
||||
}
|
||||
if (cnt != null) {
|
||||
NamedElement doc = addNamedElement(res, name, cnt);
|
||||
switch (ct) {
|
||||
case "application/smart-health-card":
|
||||
//a JSON file with a .verifiableCredential array containing SMART Health Card JWS strings, as specified by https://spec.smarthealth.cards#via-file-download.
|
||||
|
@ -261,23 +261,26 @@ public class SHLParser extends ParserBase {
|
|||
res.addAll(shc.parse(new ByteArrayInputStream(cnt)));
|
||||
break;
|
||||
case "application/fhir+json":
|
||||
NamedElement doc = addNamedElement(res, name, "json", cnt);
|
||||
// a JSON file containing any FHIR resource (e.g., an individual resource or a Bundle of resources). Generally this format may not be tamper-proof.
|
||||
logError(doc.getErrors(), "202-08-31", 1, 1, name, IssueType.STRUCTURE, "Processing content of type 'application/smart-api-access' is not done yet", IssueSeverity.INFORMATION);
|
||||
break;
|
||||
case "application/smart-api-access":
|
||||
doc = addNamedElement(res, name, "api.json", cnt);
|
||||
// a JSON file with a SMART Access Token Response (see SMART App Launch). Two additional properties are defined:
|
||||
// aud Required string indicating the FHIR Server Base URL where this token can be used (e.g., "https://server.example.org/fhir")
|
||||
// query: Optional array of strings acting as hints to the client, indicating queries it might want to make (e.g., ["Coverage?patient=123&_tag=family-insurance"])
|
||||
logError(doc.getErrors(), "202-08-31", 1, 1, name, IssueType.STRUCTURE, "Processing content of type 'application/smart-api-access' is not done yet", IssueSeverity.INFORMATION);
|
||||
break;
|
||||
default:
|
||||
doc = addNamedElement(res, name, "bin", cnt);
|
||||
logError(doc.getErrors(), "202-08-31", 1, 1, name, IssueType.STRUCTURE, "The Content-Type '"+ct+"' is not known", IssueSeverity.INFORMATION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private NamedElement addNamedElement(List<NamedElement> res, String name, byte[] content) {
|
||||
NamedElement result = new NamedElement(name, content);
|
||||
private NamedElement addNamedElement(List<NamedElement> res, String name, String type, byte[] content) {
|
||||
NamedElement result = new NamedElement(name, type, content);
|
||||
res.add(result);
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ public class TurtleParser extends ParserBase {
|
|||
@Override
|
||||
public List<NamedElement> parse(InputStream inStream) throws IOException, FHIRException {
|
||||
byte[] content = TextFile.streamToBytes(inStream);
|
||||
NamedElement ctxt = new NamedElement("ttl", content);
|
||||
NamedElement ctxt = new NamedElement("focus", "ttl", content);
|
||||
ByteArrayInputStream stream = new ByteArrayInputStream(content);
|
||||
|
||||
Turtle src = new Turtle();
|
||||
|
|
|
@ -465,7 +465,7 @@ public class VerticalBarParser extends ParserBase {
|
|||
while (!reader.isFinished()) // && (getOptions().getSegmentLimit() == 0 || getOptions().getSegmentLimit() > message.getSegments().size()))
|
||||
readSegment(message, reader);
|
||||
List<NamedElement> res = new ArrayList<>();
|
||||
res.add(new NamedElement(null, message, content));
|
||||
res.add(new NamedElement("focus", "hl7", message, content));
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
|
@ -114,7 +114,7 @@ public class XmlParser extends ParserBase {
|
|||
public List<NamedElement> parse(InputStream inStream) throws FHIRFormatError, DefinitionException, FHIRException, IOException {
|
||||
|
||||
byte[] content = TextFile.streamToBytes(inStream);
|
||||
NamedElement context = new NamedElement("xml", content);
|
||||
NamedElement context = new NamedElement("focus", "xml", content);
|
||||
|
||||
ByteArrayInputStream stream = new ByteArrayInputStream(content);
|
||||
Document doc = null;
|
||||
|
|
|
@ -176,6 +176,7 @@ import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
|
|||
import org.hl7.fhir.utilities.MarkDownProcessor;
|
||||
import org.hl7.fhir.utilities.SIDUtilities;
|
||||
import org.hl7.fhir.utilities.StandardsStatus;
|
||||
import org.hl7.fhir.utilities.TextFile;
|
||||
import org.hl7.fhir.utilities.UnicodeUtilities;
|
||||
import org.hl7.fhir.utilities.Utilities;
|
||||
import org.hl7.fhir.utilities.Utilities.DecimalStatus;
|
||||
|
@ -269,6 +270,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
private static final String HTML_FRAGMENT_REGEX = "[a-zA-Z]\\w*(((\\s+)(\\S)*)*)";
|
||||
private static final boolean STACK_TRACE = false;
|
||||
private static final boolean DEBUG_ELEMENT = false;
|
||||
private static final boolean SAVE_INTERMEDIARIES = false; // set this to true to get the intermediary formats while we are waiting for a UI around this z(SHC/SHL)
|
||||
|
||||
private static final HashSet<String> NO_TX_SYSTEM_EXEMPT = new HashSet<>(Arrays.asList("http://loinc.org", "http://unitsofmeasure.org", "http://hl7.org/fhir/sid/icd-9-cm", "http://snomed.info/sct", "http://www.nlm.nih.gov/research/umls/rxnorm"));
|
||||
private static final HashSet<String> NO_HTTPS_LIST = new HashSet<>(Arrays.asList("https://loinc.org", "https://unitsofmeasure.org", "https://snomed.info/sct", "https://www.nlm.nih.gov/research/umls/rxnorm"));
|
||||
|
@ -741,6 +743,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
}
|
||||
timeTracker.load(t);
|
||||
if (validatedContent != null && !validatedContent.isEmpty()) {
|
||||
if (SAVE_INTERMEDIARIES) {
|
||||
int index = 0;
|
||||
for (NamedElement ne : validatedContent) {
|
||||
index++;
|
||||
saveValidatedContent(ne, index);
|
||||
}
|
||||
}
|
||||
String url = parser.getImpliedProfile();
|
||||
if (url != null) {
|
||||
StructureDefinition sd = context.fetchResource(StructureDefinition.class, url);
|
||||
|
@ -760,6 +769,19 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
return (validatedContent == null || validatedContent.isEmpty()) ? null : validatedContent.get(0).getElement(); // todo: this is broken, but fixing it really complicates things elsewhere, so we do this for now
|
||||
}
|
||||
|
||||
private void saveValidatedContent(NamedElement ne, int index) {
|
||||
String tgt = null;
|
||||
try {
|
||||
tgt = Utilities.path("[tmp]", "validator", "content");
|
||||
Utilities.createDirectory(tgt);
|
||||
tgt = Utilities.path(tgt, "content-"+index+"-"+ne.getFilename());
|
||||
TextFile.bytesToFile(ne.getContent(), tgt);
|
||||
} catch (Exception e) {
|
||||
System.out.println("Error saving internal content to '"+tgt+"': "+e.getLocalizedMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List<ValidationMessage> errors, Resource resource) throws FHIRException {
|
||||
return validate(appContext, errors, resource, new ArrayList<>());
|
||||
|
@ -4986,6 +5008,30 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
pct.done();
|
||||
}
|
||||
|
||||
|
||||
if (defn.hasExtension(ToolingExtensions.EXT_SD_IMPOSE_PROFILE)) {
|
||||
for (Extension ext : defn.getExtensionsByUrl(ToolingExtensions.EXT_SD_IMPOSE_PROFILE)) {
|
||||
StructureDefinition sdi = context.fetchResource(StructureDefinition.class, ext.getValue().primitiveValue());
|
||||
if (sdi == null) {
|
||||
warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, element.line(), element.col(), stack.getLiteralPath(), false, I18nConstants.VALIDATION_VAL_PROFILE_DEPENDS_NOT_RESOLVED, ext.getValue().primitiveValue(), defn.getVersionedUrl());
|
||||
} else {
|
||||
if (crumbTrails) {
|
||||
element.addMessage(signpost(errors, NO_RULE_DATE, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), I18nConstants.VALIDATION_VAL_PROFILE_SIGNPOST_DEP, sdi.getUrl(), defn.getVersionedUrl()));
|
||||
}
|
||||
stack.resetIds();
|
||||
if (pctOwned) {
|
||||
pct = new PercentageTracker(resource.countDescendents(), resource.fhirType(), sdi.getUrl(), logProgress);
|
||||
}
|
||||
ok = startInner(hostContext, errors, resource, element, sdi, stack, false, pct, mode.withSource(ProfileSource.ProfileDependency)) && ok;
|
||||
if (pctOwned) {
|
||||
pct.done();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Element meta = element.getNamedChild(META);
|
||||
if (meta != null) {
|
||||
List<Element> profiles = new ArrayList<Element>();
|
||||
|
|
Loading…
Reference in New Issue