hopefully final prep for 5.2.2 (#387)

* Add on the fly retrieval of profiles in the validator, and fix bug where validator reported 'not done yet' for invalid contained references.

* fix loading issue for content references in versions before R3

* fix content resolution issue for versions from before 3

* Fix for bundle renderer trying to render resources from the wrong version

* Fix for wrong reference to Any in cross version extensions

* fix test case reference

* fix mistake generating snapshots around element ids

* fix tests
This commit is contained in:
Grahame Grieve 2020-11-28 01:20:49 +11:00 committed by GitHub
parent 3e1e00b37d
commit d4087590bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 314 additions and 35 deletions

View File

@ -1,8 +1,14 @@
Validator Changes:
* no effective changes
* fix issue for content references in versions before R3
* Add on the fly retrieval of profiles in the validator
* Fix bug where validator reported 'not done yet' for invalid contained references
* Fix for wrong reference to Any in cross version extensions
Other Code Changes:
* fix bug converting type mode in Structure Map
* fix bug converting Timing.when (issue 383)
* fix bug doing date time comparisons with seconds in FHIRPath
* Add support for instance-name and instance-description extensions
* Add support for instance-name and instance-description extensions
* Fix for bundle renderer trying to render resources from the wrong version

View File

@ -36,6 +36,7 @@ import java.net.URISyntaxException;
import org.hl7.fhir.r5.model.FhirPublication;
import org.hl7.fhir.r5.terminologies.TerminologyClient;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.VersionUtilities;
public class TerminologyClientFactory {
@ -53,6 +54,20 @@ public class TerminologyClientFactory {
}
}
public static TerminologyClient makeClient(String url, String v) throws URISyntaxException {
if (v == null)
return new TerminologyClientR5(checkEndsWith("/r4", url));
v = VersionUtilities.getMajMin(v);
switch (v) {
case "1.0": return new TerminologyClientR2(checkEndsWith("/r2", url));
case "1.4": return new TerminologyClientR3(checkEndsWith("/r3", url)); // r3 is the least worst match
case "3.0": return new TerminologyClientR3(checkEndsWith("/r3", url));
case "4.0": return new TerminologyClientR4(checkEndsWith("/r4", url));
case "4.5": return new TerminologyClientR5(checkEndsWith("/r4", url)); // r4 for now, since the terminology is currently the same
default: throw new Error("The version "+v.toString()+" is not currently supported");
}
}
private static String checkEndsWith(String term, String url) {
if (url.endsWith(term))
return url;

View File

@ -36,15 +36,19 @@ import java.util.Map;
import org.hl7.fhir.convertors.VersionConvertor_10_50;
import org.hl7.fhir.convertors.VersionConvertor_40_50;
import org.hl7.fhir.dstu2.model.Resource;
import org.hl7.fhir.dstu2.utils.client.FHIRToolingClient;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.TerminologyServiceException;
import org.hl7.fhir.r5.model.Bundle;
import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.model.CapabilityStatement;
import org.hl7.fhir.r5.model.Parameters;
import org.hl7.fhir.r5.model.TerminologyCapabilities;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.terminologies.TerminologyClient;
import org.hl7.fhir.utilities.ToolingClientLogger;
import org.hl7.fhir.utilities.Utilities;
public class TerminologyClientR2 implements TerminologyClient {
@ -124,5 +128,27 @@ public class TerminologyClientR2 implements TerminologyClient {
return (Bundle) VersionConvertor_10_50.convertResource(client.transaction((org.hl7.fhir.dstu2.model.Bundle) VersionConvertor_10_50.convertResource(batch)));
}
@Override
public CanonicalResource read(String type, String id) {
Class<Resource> t;
try {
t = (Class<Resource>) Class.forName("org.hl7.fhir.dstu2.model."+type);// todo: do we have to deal with any resource renaming? Use cases are limited...
} catch (ClassNotFoundException e) {
throw new FHIRException("Unable to fetch resources of type "+type+" in R2");
}
org.hl7.fhir.dstu2.model.Resource r2 = client.read(t, id);
if (r2 == null) {
throw new FHIRException("Unable to fetch resource "+Utilities.pathURL(getAddress(), type, id));
}
org.hl7.fhir.r5.model.Resource r5 = VersionConvertor_10_50.convertResource(r2);
if (r5 != null) {
throw new FHIRException("Unable to convert resource "+Utilities.pathURL(getAddress(), type, id)+" to R5 (internal representation)");
}
if (!(r5 instanceof CanonicalResource)) {
throw new FHIRException("Unable to convert resource "+Utilities.pathURL(getAddress(), type, id)+" to R5 canonical resource (internal representation)");
}
return (CanonicalResource) r5;
}
}

View File

@ -34,17 +34,21 @@ package org.hl7.fhir.convertors.txClient;
import java.net.URISyntaxException;
import java.util.Map;
import org.hl7.fhir.convertors.VersionConvertor_10_50;
import org.hl7.fhir.convertors.VersionConvertor_30_50;
import org.hl7.fhir.convertors.VersionConvertor_40_50;
import org.hl7.fhir.dstu3.model.Resource;
import org.hl7.fhir.dstu3.utils.client.FHIRToolingClient;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.model.Bundle;
import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.model.CapabilityStatement;
import org.hl7.fhir.r5.model.Parameters;
import org.hl7.fhir.r5.model.TerminologyCapabilities;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.terminologies.TerminologyClient;
import org.hl7.fhir.utilities.ToolingClientLogger;
import org.hl7.fhir.utilities.Utilities;
public class TerminologyClientR3 implements TerminologyClient {
@ -124,5 +128,27 @@ public class TerminologyClientR3 implements TerminologyClient {
return (Bundle) VersionConvertor_30_50.convertResource(client.transaction((org.hl7.fhir.dstu3.model.Bundle) VersionConvertor_30_50.convertResource(batch, false)), false);
}
@Override
public CanonicalResource read(String type, String id) {
Class<Resource> t;
try {
t = (Class<Resource>) Class.forName("org.hl7.fhir.dstu3.model."+type);// todo: do we have to deal with any resource renaming? Use cases are limited...
} catch (ClassNotFoundException e) {
throw new FHIRException("Unable to fetch resources of type "+type+" in R2");
}
org.hl7.fhir.dstu3.model.Resource r3 = client.read(t, id);
if (r3 == null) {
throw new FHIRException("Unable to fetch resource "+Utilities.pathURL(getAddress(), type, id));
}
org.hl7.fhir.r5.model.Resource r5 = VersionConvertor_30_50.convertResource(r3, false);
if (r5 != null) {
throw new FHIRException("Unable to convert resource "+Utilities.pathURL(getAddress(), type, id)+" to R5 (internal representation)");
}
if (!(r5 instanceof CanonicalResource)) {
throw new FHIRException("Unable to convert resource "+Utilities.pathURL(getAddress(), type, id)+" to R5 canonical resource (internal representation)");
}
return (CanonicalResource) r5;
}
}

View File

@ -34,17 +34,21 @@ package org.hl7.fhir.convertors.txClient;
import java.net.URISyntaxException;
import java.util.Map;
import org.hl7.fhir.convertors.VersionConvertor_30_50;
import org.hl7.fhir.convertors.VersionConvertor_40_50;
import org.hl7.fhir.convertors.conv40_50.TerminologyCapabilities40_50;
import org.hl7.fhir.r4.model.Resource;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r4.utils.client.FHIRToolingClient;
import org.hl7.fhir.r5.model.Bundle;
import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.model.CapabilityStatement;
import org.hl7.fhir.r5.model.Parameters;
import org.hl7.fhir.r5.model.TerminologyCapabilities;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.terminologies.TerminologyClient;
import org.hl7.fhir.utilities.ToolingClientLogger;
import org.hl7.fhir.utilities.Utilities;
public class TerminologyClientR4 implements TerminologyClient {
@ -124,4 +128,26 @@ public class TerminologyClientR4 implements TerminologyClient {
return (Bundle) VersionConvertor_40_50.convertResource(client.transaction((org.hl7.fhir.r4.model.Bundle) VersionConvertor_40_50.convertResource(batch)));
}
@Override
public CanonicalResource read(String type, String id) {
Class<Resource> t;
try {
t = (Class<Resource>) Class.forName("org.hl7.fhir.r4.model."+type);// todo: do we have to deal with any resource renaming? Use cases are limited...
} catch (ClassNotFoundException e) {
throw new FHIRException("Unable to fetch resources of type "+type+" in R2");
}
org.hl7.fhir.r4.model.Resource r4 = client.read(t, id);
if (r4 == null) {
throw new FHIRException("Unable to fetch resource "+Utilities.pathURL(getAddress(), type, id));
}
org.hl7.fhir.r5.model.Resource r5 = VersionConvertor_40_50.convertResource(r4);
if (r5 != null) {
throw new FHIRException("Unable to convert resource "+Utilities.pathURL(getAddress(), type, id)+" to R5 (internal representation)");
}
if (!(r5 instanceof CanonicalResource)) {
throw new FHIRException("Unable to convert resource "+Utilities.pathURL(getAddress(), type, id)+" to R5 canonical resource (internal representation)");
}
return (CanonicalResource) r5;
}
}

View File

@ -34,8 +34,11 @@ package org.hl7.fhir.convertors.txClient;
import java.net.URISyntaxException;
import java.util.Map;
import org.hl7.fhir.convertors.VersionConvertor_40_50;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.Bundle;
import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.model.CapabilityStatement;
import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.Parameters;
@ -44,6 +47,7 @@ import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.terminologies.TerminologyClient;
import org.hl7.fhir.r5.utils.client.FHIRToolingClient;
import org.hl7.fhir.utilities.ToolingClientLogger;
import org.hl7.fhir.utilities.Utilities;
public class TerminologyClientR5 implements TerminologyClient {
@ -116,4 +120,22 @@ public class TerminologyClientR5 implements TerminologyClient {
return client.transaction(batch);
}
@Override
public CanonicalResource read(String type, String id) {
Class<Resource> t;
try {
t = (Class<Resource>) Class.forName("org.hl7.fhir.r5.model."+type);// todo: do we have to deal with any resource renaming? Use cases are limited...
} catch (ClassNotFoundException e) {
throw new FHIRException("Unable to fetch resources of type "+type+" in R5");
}
org.hl7.fhir.r5.model.Resource r5 = client.read(t, id);
if (r5 != null) {
throw new FHIRException("Unable to convert resource "+Utilities.pathURL(getAddress(), type, id)+" to R5 (internal representation)");
}
if (!(r5 instanceof CanonicalResource)) {
throw new FHIRException("Unable to convert resource "+Utilities.pathURL(getAddress(), type, id)+" to R5 canonical resource (internal representation)");
}
return (CanonicalResource) r5;
}
}

View File

@ -4141,11 +4141,13 @@ public class ProfileUtilities extends TranslatingUtilities {
first = false;
}
}
if (first)
if (first) {
c.getPieces().add(gen.new Piece(null, "Any", null));
}
if (ADD_REFERENCE_TO_TABLE)
if (ADD_REFERENCE_TO_TABLE) {
c.getPieces().add(gen.new Piece(null, ")", null));
}
} else {
StructureDefinition sd = context.fetchTypeDefinition(t);
@ -5617,6 +5619,7 @@ public class ProfileUtilities extends TranslatingUtilities {
return;
Map<String, String> idList = new HashMap<String, String>();
Map<String, String> replacedIds = new HashMap<String, String>();
SliceList sliceInfo = new SliceList();
// first pass, update the element ids
@ -5643,6 +5646,9 @@ public class ProfileUtilities extends TranslatingUtilities {
}
}
String bs = b.toString();
if (ed.hasId()) {
replacedIds.put(ed.getId(), ed.getPath());
}
ed.setId(bs);
if (idList.containsKey(bs)) {
if (exception || messages == null) {
@ -5653,7 +5659,11 @@ public class ProfileUtilities extends TranslatingUtilities {
idList.put(bs, ed.getPath());
if (ed.hasContentReference() && ed.getContentReference().startsWith("#")) {
String s = ed.getContentReference();
ed.setContentReference("http://hl7.org/fhir/StructureDefinition/"+type+s);
if (replacedIds.containsKey(s.substring(1))) {
ed.setContentReference("http://hl7.org/fhir/StructureDefinition/"+type+"#"+replacedIds.get(s.substring(1)));
} else {
ed.setContentReference("http://hl7.org/fhir/StructureDefinition/"+type+s);
}
}
}
// second path - fix up any broken path based id references

View File

@ -109,17 +109,31 @@ public class Property {
return definition.getPath();
ElementDefinition ed = definition;
if (definition.hasContentReference()) {
if (!definition.getContentReference().startsWith("#"))
throw new Error("not handled yet");
String url = null;
String path = definition.getContentReference();
if (!path.startsWith("#")) {
if (path.contains("#")) {
url = path.substring(0, path.indexOf("#"));
path = path.substring(path.indexOf("#")+1);
} else {
throw new Error("Illegal content reference '"+path+"'");
}
} else {
path = path.substring(1);
}
StructureDefinition sd = (url == null || url.equals(structure.getUrl())) ? structure : context.fetchResource(StructureDefinition.class, url, structure);
if (sd == null) {
throw new Error("Unknown Type in content reference '"+path+"'");
}
boolean found = false;
for (ElementDefinition d : structure.getSnapshot().getElement()) {
if (d.hasId() && d.getId().equals(definition.getContentReference().substring(1))) {
for (ElementDefinition d : sd.getSnapshot().getElement()) {
if (d.hasId() && d.getId().equals(path)) {
found = true;
ed = d;
}
}
if (!found)
throw new Error("Unable to resolve "+definition.getContentReference()+" at "+definition.getPath()+" on "+structure.getUrl());
throw new Error("Unable to resolve "+definition.getContentReference()+" at "+definition.getPath()+" on "+sd.getUrl());
}
if (ed.getType().size() == 0)
return null;

View File

@ -399,4 +399,14 @@ public class BundleRenderer extends ResourceRenderer {
return "??";
}
public boolean canRender(Bundle b) {
for (BundleEntryComponent be : b.getEntry()) {
ResourceRenderer rr = RendererFactory.factory(be.getResource(), context);
if (!rr.canRender(be.getResource())) {
return false;
}
}
return true;
}
}

View File

@ -939,5 +939,8 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
return path.substring(path.lastIndexOf(".")+1);
}
public boolean canRender(Resource resource) {
return context.getWorker().getResourceNames().contains(resource.fhirType());
}
}

View File

@ -366,4 +366,8 @@ public abstract class ResourceRenderer extends DataRenderer {
return fullUrl.replace(":", "-");
}
public boolean canRender(Resource resource) {
return true;
}
}

View File

@ -35,6 +35,7 @@ import java.util.Map;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.model.Bundle;
import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.model.CapabilityStatement;
import org.hl7.fhir.r5.model.Parameters;
import org.hl7.fhir.r5.model.TerminologyCapabilities;
@ -54,4 +55,5 @@ public interface TerminologyClient {
public CapabilityStatement getCapabilitiesStatementQuick() throws FHIRException;
public Parameters lookupCode(Map<String, String> params) throws FHIRException;
public Bundle validateBatch(Bundle batch);
public CanonicalResource read(String type, String id);
}

View File

@ -34,6 +34,7 @@ package org.hl7.fhir.r5.utils;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Locale;
@ -42,6 +43,7 @@ import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.elementmodel.Element;
import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.utils.IResourceValidator.BundleValidationRule;
import org.hl7.fhir.utilities.Utilities;
@ -102,6 +104,7 @@ public interface IResourceValidator {
void recordProfileUsage(StructureDefinition profile, Object appContext, Element element);
}
public interface IValidatorResourceFetcher {
Element fetch(Object appContext, String url) throws FHIRFormatError, DefinitionException, FHIRException, IOException;
@ -111,6 +114,29 @@ public interface IResourceValidator {
byte[] fetchRaw(String url) throws MalformedURLException, IOException; // for attachment checking
void setLocale(Locale locale);
/**
* this is used when the validator encounters a reference to a structure definition, value set or code system at some random URL reference
* while validating.
*
* Added in v5.2.2. return null to leave functionality as it was before then.
*
* @param primitiveValue
* @return an R5 version of the resource
* @throws URISyntaxException
*/
CanonicalResource fetchCanonicalResource(String url) throws URISyntaxException;
/**
* Whether to try calling fetchCanonicalResource for this reference (not whether it will succeed - just throw an exception from fetchCanonicalResource if it doesn't resolve. This is a policy thing.
*
* Added in v5.2.2. return false to leave functionality as it was before then.
*
* @param url
* @return
*/
boolean fetchesCanonicalResource(String url);
}
public enum BestPracticeWarningLevel {

View File

@ -135,7 +135,11 @@ public class XVerExtensionManager {
if (hasTargets(tr.getCode()) ) {
s = s.substring(t.length()+1);
for (String p : s.substring(0, s.length()-1).split("\\|")) {
tr.addTargetProfile("http://hl7.org/fhir/StructureDefinition/"+p);
if ("Any".equals(p)) {
tr.addTargetProfile("http://hl7.org/fhir/StructureDefinition/Resource");
} else {
tr.addTargetProfile("http://hl7.org/fhir/StructureDefinition/"+p);
}
}
}
} else {

View File

@ -1,5 +1,7 @@
package org.hl7.fhir.r5.utils.client;
import org.hl7.fhir.exceptions.FHIRException;
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
@ -152,7 +154,7 @@ public class FHIRToolingClient {
capabilities = (CapabilityStatement) client.issueGetResourceRequest(resourceAddress.resolveMetadataUri(true),
getPreferredResourceFormat(), "CapabilitiesStatement-Quick", TIMEOUT_NORMAL).getReference();
} catch (Exception e) {
handleException("An error has occurred while trying to fetch the server's conformance statement", e);
handleException("An error fetching the server's capability statement: "+e.getMessage(), e);
}
return capabilities;
}
@ -166,7 +168,7 @@ public class FHIRToolingClient {
throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome) result.getPayload());
}
} catch (Exception e) {
handleException("An error has occurred while trying to read this resource", e);
throw new FHIRException(e);
}
return result.getPayload();
}

View File

@ -572,6 +572,8 @@ public class I18nConstants {
public static final String VALIDATION_VAL_PROFILE_THIS_VERSION_OK = "VALIDATION_VAL_PROFILE_THIS_VERSION_OK";
public static final String VALIDATION_VAL_PROFILE_THIS_VERSION_OTHER = "VALIDATION_VAL_PROFILE_THIS_VERSION_OTHER";
public static final String VALIDATION_VAL_PROFILE_UNKNOWN = "Validation_VAL_Profile_Unknown";
public static final String VALIDATION_VAL_PROFILE_UNKNOWN_NOT_POLICY = "VALIDATION_VAL_PROFILE_UNKNOWN_NOT_POLICY";
public static final String VALIDATION_VAL_PROFILE_UNKNOWN_ERROR = "VALIDATION_VAL_PROFILE_UNKNOWN_ERROR";
public static final String VALIDATION_VAL_PROFILE_WRONGTYPE = "Validation_VAL_Profile_WrongType";
public static final String VALIDATION_VAL_PROFILE_WRONGTYPE2 = "Validation_VAL_Profile_WrongType2";
public static final String VALIDATION_VAL_UNKNOWN_PROFILE = "Validation_VAL_Unknown_Profile";

View File

@ -223,7 +223,9 @@ Validation_VAL_Profile_NotAllowed = This element is not allowed by the profile {
Validation_VAL_Profile_NotSlice = This element does not match any known slice {0} and slicing is CLOSED: {1}
Validation_VAL_Profile_OutOfOrder = As specified by profile {0}, Element ''{1}'' is out of order
Validation_VAL_Profile_SliceOrder = As specified by profile {0}, Element ''{1}'' is out of order in ordered slice
Validation_VAL_Profile_Unknown = Profile reference ''{0}'' could not be resolved, so has not been checked
Validation_VAL_Profile_Unknown = Profile reference ''{0}'' has not been checked because it is unknown
VALIDATION_VAL_PROFILE_UNKNOWN_NOT_POLICY = Profile reference ''{0}'' has not been checked because it is unknown, and the validator is set to not fetch unknown profiles
VALIDATION_VAL_PROFILE_UNKNOWN_ERROR = Profile reference ''{0}'' has not been checked because it is unknown, and fetching it resulted in the error {1}
Validation_VAL_Profile_WrongType = Specified profile type was ''{0}'', but found type ''{1}''
Validation_VAL_Unknown_Profile = Unknown profile {0}
XHTML_XHTML_Attribute_Illegal = Illegal attribute name in the XHTML (''{0}'' on ''{1}'')

View File

@ -68,6 +68,7 @@ import org.hl7.fhir.r5.formats.JsonParser;
import org.hl7.fhir.r5.formats.XmlParser;
import org.hl7.fhir.r5.model.Base;
import org.hl7.fhir.r5.model.Bundle;
import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.model.Coding;
import org.hl7.fhir.r5.model.Constants;
import org.hl7.fhir.r5.model.DomainResource;
@ -1907,6 +1908,17 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst
this.locale = locale;
}
@Override
public CanonicalResource fetchCanonicalResource(String url) throws URISyntaxException {
return fetcher != null ? fetcher.fetchCanonicalResource(url) : null;
}
@Override
public boolean fetchesCanonicalResource(String url) {
return fetcher != null ? fetcher.fetchesCanonicalResource(url) : false;
}
public void handleOutput(Resource r, String output, String version) throws FHIRException, IOException {
if (output.startsWith("http://") || output.startsWith("http://")) {
ByteArrayOutputStream bs = new ByteArrayOutputStream();

View File

@ -2,13 +2,17 @@ package org.hl7.fhir.validation.cli.services;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.util.Locale;
import org.hl7.fhir.convertors.txClient.TerminologyClientFactory;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.elementmodel.Element;
import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.terminologies.TerminologyClient;
import org.hl7.fhir.r5.utils.IResourceValidator.IValidatorResourceFetcher;
import org.hl7.fhir.r5.utils.IResourceValidator.ReferenceValidationPolicy;
import org.hl7.fhir.utilities.Utilities;
@ -119,4 +123,32 @@ public class StandAloneValidatorFetcher implements IValidatorResourceFetcher {
throw new Error("Not done yet");
}
@Override
public CanonicalResource fetchCanonicalResource(String url) throws URISyntaxException {
String[] p = url.split("\\/");
String root = getRoot(p, url);
if (root != null) {
TerminologyClient c;
c = TerminologyClientFactory.makeClient(root, context.getVersion());
return c.read(p[p.length-2], p[p.length-1]);
} else {
throw new FHIRException("Not done yet");
}
}
private String getRoot(String[] p, String url) {
if (p.length > 3 && Utilities.isValidId(p[p.length-1]) && context.getResourceNames().contains(p[p.length-2])) {
url = url.substring(0, url.lastIndexOf("/"));
return url.substring(0, url.lastIndexOf("/"));
} else {
return null;
}
}
@Override
public boolean fetchesCanonicalResource(String url) {
return true;
}
}

View File

@ -2446,25 +2446,26 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (pol.checkExists()) {
if (we == null) {
if (fetcher == null) {
if (!refType.equals("contained"))
if (!refType.equals("contained")) {
if (fetcher == null) {
throw new FHIRException(context.formatMessage(I18nConstants.RESOURCE_RESOLUTION_SERVICES_NOT_PROVIDED));
} else {
Element ext = null;
if (fetchCache.containsKey(ref)) {
ext = fetchCache.get(ref);
} else {
try {
ext = fetcher.fetch(hostContext.getAppContext(), ref);
} catch (IOException e) {
throw new FHIRException(e);
}
if (ext != null) {
setParents(ext);
fetchCache.put(ref, ext);
Element ext = null;
if (fetchCache.containsKey(ref)) {
ext = fetchCache.get(ref);
} else {
try {
ext = fetcher.fetch(hostContext.getAppContext(), ref);
} catch (IOException e) {
throw new FHIRException(e);
}
if (ext != null) {
setParents(ext);
fetchCache.put(ref, ext);
}
}
we = ext == null ? null : makeExternalRef(ext, path);
}
we = ext == null ? null : makeExternalRef(ext, path);
}
}
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, (allowExamples && (ref.contains("example.org") || ref.contains("acme.com"))) || (we != null || pol == ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS), I18nConstants.REFERENCE_REF_CANTRESOLVE, ref);
@ -3760,10 +3761,29 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
StructureDefinition sdt = context.fetchResource(StructureDefinition.class, vu.getUrl());
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath() + ".meta.profile[" + i + "]", false, I18nConstants.VALIDATION_VAL_PROFILE_THIS_VERSION_OTHER, sdt == null ? "null" : sdt.getType());
}
} else if (warning(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath() + ".meta.profile[" + i + "]", sd != null, I18nConstants.VALIDATION_VAL_PROFILE_UNKNOWN, profile.primitiveValue())) {
signpost(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), !crumbTrails, I18nConstants.VALIDATION_VAL_PROFILE_SIGNPOST_META, sd.getUrl());
stack.resetIds();
startInner(hostContext, errors, resource, element, sd, stack, false);
} else {
if (sd == null) {
// we'll try fetching it directly from it's source, but this is likely to fail later even if the resolution succeeds
if (fetcher == null) {
warning(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath() + ".meta.profile[" + i + "]", false, I18nConstants.VALIDATION_VAL_PROFILE_UNKNOWN, profile.primitiveValue());
} else if (!fetcher.fetchesCanonicalResource(profile.primitiveValue())) {
warning(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath() + ".meta.profile[" + i + "]", false, I18nConstants.VALIDATION_VAL_PROFILE_UNKNOWN_NOT_POLICY, profile.primitiveValue());
} else {
try {
sd = (StructureDefinition) fetcher.fetchCanonicalResource(profile.primitiveValue());
} catch (Exception e) {
warning(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath() + ".meta.profile[" + i + "]", false, I18nConstants.VALIDATION_VAL_PROFILE_UNKNOWN_ERROR, profile.primitiveValue(), e.getMessage());
}
if (sd != null) {
context.cacheResource(sd);
}
}
}
if (sd != null) {
signpost(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), !crumbTrails, I18nConstants.VALIDATION_VAL_PROFILE_SIGNPOST_META, sd.getUrl());
stack.resetIds();
startInner(hostContext, errors, resource, element, sd, stack, false);
}
}
}
i++;

View File

@ -57,6 +57,7 @@ import org.hl7.fhir.utilities.npm.NpmPackage;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import org.hl7.fhir.validation.ValidationEngine;
import org.hl7.fhir.validation.cli.services.StandAloneValidatorFetcher;
import org.hl7.fhir.validation.instance.InstanceValidator;
import org.junit.AfterClass;
import org.junit.Assert;
@ -147,7 +148,6 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
throw new Exception("unknown version " + version);
}
vCurr = ve.get(version);
vCurr.setFetcher(this);
if (TestingUtilities.fcontexts == null) {
TestingUtilities.fcontexts = new HashMap<>();
}
@ -161,6 +161,12 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
val.setWantCheckSnapshotUnchanged(true);
val.getContext().setClientRetryCount(4);
val.setDebug(false);
if (content.has("fetcher") && "standalone".equals(JSONUtil.str(content, "fetcher"))) {
val.setFetcher(vCurr);
vCurr.setFetcher(new StandAloneValidatorFetcher(vCurr.getPcm(), vCurr.getContext(), vCurr));
} else {
val.setFetcher(this);
}
if (content.has("allowed-extension-domain"))
val.getExtensionDomains().add(content.get("allowed-extension-domain").getAsString());
if (content.has("allowed-extension-domains"))
@ -170,7 +176,6 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
val.setValidationLanguage(content.get("language").getAsString());
else
val.setValidationLanguage(null);
val.setFetcher(this);
if (content.has("packages")) {
for (JsonElement e : content.getAsJsonArray("packages")) {
String n = e.getAsString();
@ -526,4 +531,14 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
URLConnection c = url.openConnection();
return TextFile.streamToBytes(c.getInputStream());
}
@Override
public CanonicalResource fetchCanonicalResource(String url) {
return null;
}
@Override
public boolean fetchesCanonicalResource(String url) {
return false;
}
}

View File

@ -19,7 +19,7 @@
<properties>
<hapi_fhir_version>5.1.0</hapi_fhir_version>
<validator_test_case_version>1.1.51-SNAPSHOT</validator_test_case_version>
<validator_test_case_version>1.1.52-SNAPSHOT</validator_test_case_version>
<junit_jupiter_version>5.6.2</junit_jupiter_version>
<maven_surefire_version>3.0.0-M4</maven_surefire_version>
<jacoco_version>0.8.5</jacoco_version>