Merge pull request #1369 from hapifhir/2023-07-gg-more_fhirpath_checking
2023 07 gg more fhirpath checking
This commit is contained in:
commit
ac6933bfc2
|
@ -42,6 +42,7 @@ import org.hl7.fhir.exceptions.DefinitionException;
|
|||
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||
import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent;
|
||||
import org.hl7.fhir.r5.model.ExpressionNode.CollectionStatus;
|
||||
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
|
||||
import org.hl7.fhir.utilities.Utilities;
|
||||
|
||||
|
||||
|
@ -115,6 +116,16 @@ public class TypeDetails {
|
|||
public boolean isSystemType() {
|
||||
return uri.startsWith(FP_NS);
|
||||
}
|
||||
|
||||
public String describeMin() {
|
||||
if (uri.startsWith(FP_NS)) {
|
||||
return "System."+uri.substring(FP_NS.length());
|
||||
}
|
||||
if (uri.startsWith("http://hl7.org/fhir/StructureDefinition/")) {
|
||||
return "FHIR."+uri.substring("http://hl7.org/fhir/StructureDefinition/".length());
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -399,6 +410,12 @@ public class TypeDetails {
|
|||
public String describe() {
|
||||
return getTypes().toString();
|
||||
}
|
||||
public String describeMin() {
|
||||
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
|
||||
for (ProfiledType pt : types)
|
||||
b.append(pt.describeMin());
|
||||
return b.toString();
|
||||
}
|
||||
public String getType() {
|
||||
for (ProfiledType pt : types)
|
||||
return pt.uri;
|
||||
|
|
|
@ -67,6 +67,7 @@ import org.hl7.fhir.r5.model.Timing.EventTiming;
|
|||
import org.hl7.fhir.r5.model.Timing.TimingRepeatComponent;
|
||||
import org.hl7.fhir.r5.model.Timing.UnitsOfTime;
|
||||
import org.hl7.fhir.r5.model.UriType;
|
||||
import org.hl7.fhir.r5.model.UsageContext;
|
||||
import org.hl7.fhir.r5.model.ValueSet;
|
||||
import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceComponent;
|
||||
import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceDesignationComponent;
|
||||
|
@ -1517,6 +1518,12 @@ public class DataRenderer extends Renderer implements CodeResolver {
|
|||
x.addText(!p.hasEnd() ? "(ongoing)" : displayDateTime(p.getEndElement()));
|
||||
}
|
||||
|
||||
public void renderUsageContext(XhtmlNode x, UsageContext u) throws FHIRFormatError, DefinitionException, IOException {
|
||||
renderCoding(x, u.getCode());
|
||||
x.tx(": ");
|
||||
render(x, u.getValue());
|
||||
}
|
||||
|
||||
public void renderDataRequirement(XhtmlNode x, DataRequirement dr) throws FHIRFormatError, DefinitionException, IOException {
|
||||
XhtmlNode tbl = x.table("grid");
|
||||
XhtmlNode tr = tbl.tr();
|
||||
|
|
|
@ -485,8 +485,11 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
|
|||
} else if (e instanceof Resource) {
|
||||
return;
|
||||
} else if (e instanceof DataRequirement) {
|
||||
DataRequirement p = (DataRequirement) e;
|
||||
DataRequirement p = (DataRequirement) e;
|
||||
renderDataRequirement(x, p);
|
||||
} else if (e instanceof UsageContext) {
|
||||
UsageContext p = (UsageContext) e;
|
||||
renderUsageContext(x, p);
|
||||
} else if (e instanceof PrimitiveType) {
|
||||
x.tx(((PrimitiveType) e).primitiveValue());
|
||||
} else if (e instanceof ElementDefinition) {
|
||||
|
|
|
@ -3387,8 +3387,12 @@ public class FHIRPathEngine {
|
|||
return td;
|
||||
}
|
||||
case OfType : {
|
||||
checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String));
|
||||
TypeDetails td = new TypeDetails(CollectionStatus.SINGLETON, exp.getParameters().get(0).getName());
|
||||
checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String));
|
||||
String tn = exp.getParameters().get(0).getName();
|
||||
if (typeCastIsImpossible(focus, tn)) {
|
||||
typeWarnings.add(worker.formatMessage(I18nConstants.FHIRPATH_OFTYPE_IMPOSSIBLE, focus.describeMin(), tn, exp.toString()));
|
||||
}
|
||||
TypeDetails td = new TypeDetails(CollectionStatus.SINGLETON, tn);
|
||||
if (td.typesHaveTargets()) {
|
||||
td.addTargets(focus.getTargets());
|
||||
}
|
||||
|
@ -3707,6 +3711,10 @@ public class FHIRPathEngine {
|
|||
throw new Error("not Implemented yet");
|
||||
}
|
||||
|
||||
private boolean typeCastIsImpossible(TypeDetails focus, String tn) {
|
||||
return !focus.hasType(tn);
|
||||
}
|
||||
|
||||
private boolean isExpressionParameter(ExpressionNode exp, int i) {
|
||||
switch (i) {
|
||||
case 0:
|
||||
|
|
|
@ -12,7 +12,7 @@ public interface IValidatorResourceFetcher {
|
|||
|
||||
Element fetch(IResourceValidator validator, Object appContext, String url) throws FHIRException, IOException;
|
||||
|
||||
boolean resolveURL(IResourceValidator validator, Object appContext, String path, String url, String type) throws IOException, FHIRException;
|
||||
boolean resolveURL(IResourceValidator validator, Object appContext, String path, String url, String type, boolean canonical) throws IOException, FHIRException;
|
||||
|
||||
byte[] fetchRaw(IResourceValidator validator, String url) throws IOException; // for attachment checking
|
||||
|
||||
|
|
|
@ -947,6 +947,8 @@ public class I18nConstants {
|
|||
public static final String FHIRPATH_COLLECTION_STATUS_OPERATION_LEFT = "FHIRPATH_COLLECTION_STATUS_OPERATION_LEFT";
|
||||
public static final String FHIRPATH_COLLECTION_STATUS_OPERATION_RIGHT = "FHIRPATH_COLLECTION_STATUS_OPERATION_RIGHT";
|
||||
public static final String ED_INVARIANT_KEY_ALREADY_USED = "ED_INVARIANT_KEY_ALREADY_USED";
|
||||
public static final String FHIRPATH_OFTYPE_IMPOSSIBLE = "FHIRPATH_OFTYPE_IMPOSSIBLE";
|
||||
public static final String ED_SEARCH_EXPRESSION_ERROR = "ED_SEARCH_EXPRESSION_ERROR";
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1003,3 +1003,5 @@ LIQUID_VARIABLE_ILLEGAL = Liquid Exception: The variable name ''{0}'' cannot be
|
|||
ED_INVARIANT_DIFF_NO_SOURCE = The invariant {0} defined in the differential must have no source, or the source must be the same as the profile
|
||||
FHIRPATH_COLLECTION_STATUS_OPERATION_LEFT = The left side is inherently a collection, and so the expression ''{0}'' may fail or return false if there is more than one item in the content being evaluated
|
||||
FHIRPATH_COLLECTION_STATUS_OPERATION_RIGHT = The right side is inherently a collection, and so this expression ''{0}'' may fail or return false if there is more than one item in the content being evaluated
|
||||
FHIRPATH_OFTYPE_IMPOSSIBLE = The type specified in ofType is {1} which is not a possible candidate for the existing types ({0}) in the expression {2}. Check the paths and types to be sure this is what is intended
|
||||
ED_SEARCH_EXPRESSION_ERROR = Error in search expression ''{0}'': {1}
|
|
@ -1112,7 +1112,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean resolveURL(IResourceValidator validator, Object appContext, String path, String url, String type) throws FHIRException {
|
||||
public boolean resolveURL(IResourceValidator validator, Object appContext, String path, String url, String type, boolean canonical) throws FHIRException {
|
||||
// some of this logic might take a while, and it's not going to change once loaded
|
||||
if (resolvedUrls .containsKey(type+"|"+url)) {
|
||||
return resolvedUrls.get(type+"|"+url);
|
||||
|
@ -1154,7 +1154,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP
|
|||
}
|
||||
if (fetcher != null) {
|
||||
try {
|
||||
boolean ok = fetcher.resolveURL(validator, appContext, path, url, type);
|
||||
boolean ok = fetcher.resolveURL(validator, appContext, path, url, type, canonical);
|
||||
resolvedUrls.put(type+"|"+url, ok);
|
||||
return ok;
|
||||
} catch (Exception e) {
|
||||
|
|
|
@ -78,7 +78,7 @@ public class StandAloneValidatorFetcher implements IValidatorResourceFetcher, IV
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean resolveURL(IResourceValidator validator, Object appContext, String path, String url, String type) throws IOException, FHIRException {
|
||||
public boolean resolveURL(IResourceValidator validator, Object appContext, String path, String url, String type, boolean canonical) throws IOException, FHIRException {
|
||||
if (!Utilities.isAbsoluteUrl(url)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -283,7 +283,7 @@ public class StandAloneValidatorFetcher implements IValidatorResourceFetcher, IV
|
|||
@Override
|
||||
public void findResource(Object validator, String url) {
|
||||
try {
|
||||
resolveURL((IResourceValidator) validator, null, null, url, null);
|
||||
resolveURL((IResourceValidator) validator, null, null, url, null, false);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2766,7 +2766,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
found = isDefinitionURL(url) || (allowExamples && (url.contains("example.org") || url.contains("acme.com")) || url.contains("acme.org")) /* || (url.startsWith("http://hl7.org/fhir/tools")) */ ||
|
||||
SpecialExtensions.isKnownExtension(url) || isXverUrl(url);
|
||||
if (!found) {
|
||||
found = fetcher.resolveURL(this, hostContext, path, url, type);
|
||||
found = fetcher.resolveURL(this, hostContext, path, url, type, type.equals("canonical"));
|
||||
}
|
||||
} catch (IOException e1) {
|
||||
found = false;
|
||||
|
|
|
@ -46,9 +46,16 @@ public class SearchParameterValidator extends BaseValidator {
|
|||
|
||||
public boolean validateSearchParameter(List<ValidationMessage> errors, Element cs, NodeStack stack) {
|
||||
boolean ok = true;
|
||||
String url = cs.getNamedChildValue("url");
|
||||
String master = cs.getNamedChildValue("derivedFrom");
|
||||
// String url = cs.getNamedChildValue("url");
|
||||
|
||||
if (cs.hasChild("expression")) {
|
||||
List<String> bases = new ArrayList<>();
|
||||
for (Element b : cs.getChildrenByName("base")) {
|
||||
bases.add(b.primitiveValue());
|
||||
}
|
||||
ok = checkExpression(errors, stack.push(cs.getNamedChild("expression"), -1, null, null), cs.getNamedChildValue("expression"), bases) && ok;
|
||||
}
|
||||
String master = cs.getNamedChildValue("derivedFrom");
|
||||
if (!Utilities.noString(master)) {
|
||||
SearchParameter sp = context.fetchResource(SearchParameter.class, master);
|
||||
if (warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE,stack.getLiteralPath(), sp != null, I18nConstants.SEARCHPARAMETER_NOTFOUND, master)) {
|
||||
|
@ -67,13 +74,33 @@ public class SearchParameterValidator extends BaseValidator {
|
|||
String expOther = canonicalise(sp.getExpression(), bases);
|
||||
warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE,stack.getLiteralPath(), expThis.equals(expOther), I18nConstants.SEARCHPARAMETER_EXP_WRONG, master, sp.getExpression(), cs.getNamedChildValue("expression"));
|
||||
}
|
||||
|
||||
// todo: check compositions
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
private String canonicalise(String path, List<String> bases) {
|
||||
private boolean checkExpression(List<ValidationMessage> errors, NodeStack stack, String expression, List<String> bases) {
|
||||
boolean ok = true;
|
||||
try {
|
||||
List<String> warnings = new ArrayList<>();
|
||||
fpe.checkOnTypes(null, null, bases, fpe.parse(expression), warnings);
|
||||
for (String s : warnings) {
|
||||
warning(errors, "2023-07-27", IssueType.BUSINESSRULE, stack, false, s);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (debug) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
ok = rule(errors, "2023-06-19", IssueType.INVALID, stack, false, I18nConstants.ED_SEARCH_EXPRESSION_ERROR, expression, e.getMessage()) && ok;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
private String canonicalise(String path, List<String> bases) {
|
||||
|
||||
|
||||
ExpressionNode exp = fpe.parse(path);
|
||||
List<ExpressionNode> pass = new ArrayList<>();
|
||||
while (exp != null) {
|
||||
|
|
|
@ -433,7 +433,7 @@ public class R4R5MapTester implements IValidatorResourceFetcher {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean resolveURL(IResourceValidator validator, Object appContext, String path, String url, String type)
|
||||
public boolean resolveURL(IResourceValidator validator, Object appContext, String path, String url, String type, boolean canonical)
|
||||
throws IOException, FHIRException {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -732,7 +732,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
|
|||
return CodedContentValidationPolicy.VALUESET;
|
||||
}
|
||||
@Override
|
||||
public boolean resolveURL(IResourceValidator validator, Object appContext, String path, String url, String type) throws IOException, FHIRException {
|
||||
public boolean resolveURL(IResourceValidator validator, Object appContext, String path, String url, String type, boolean canonical) throws IOException, FHIRException {
|
||||
return !url.contains("example.org") && !url.startsWith("http://hl7.org/fhir/invalid");
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue