From 9fc8b35797d3e914e0cc62e5752e6784b3c0c3f4 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Mon, 14 Sep 2020 18:10:31 +1000 Subject: [PATCH] Fix issue with discriminating by address and human name --- .../fhir/utilities/i18n/I18nConstants.java | 2 + .../src/main/resources/Messages.properties | 2 + .../instance/InstanceValidator.java | 120 +++++++++++++++++- .../validation/tests/ValidationTests.java | 7 + 4 files changed, 128 insertions(+), 3 deletions(-) diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java index 99f9fe519..1d5359d76 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java @@ -528,6 +528,8 @@ public class I18nConstants { public static final String UNSUPPORTED_FIXED_PATTERN_TYPE_FOR_DISCRIMINATOR_FOR_SLICE__ = "Unsupported_fixed_pattern_type_for_discriminator_for_slice__"; public static final String UNSUPPORTED_FIXED_VALUE_TYPE_FOR_DISCRIMINATOR_FOR_SLICE__ = "Unsupported_fixed_value_type_for_discriminator_for_slice__"; public static final String UNSUPPORTED_IDENTIFIER_PATTERN__EXTENSIONS_ARE_NOT_ALLOWED__FOR_DISCRIMINATOR_FOR_SLICE_ = "Unsupported_Identifier_pattern__extensions_are_not_allowed__for_discriminator_for_slice_"; + public static final String UNSUPPORTED_IDENTIFIER_PATTERN_PROPERTY_NOT_SUPPORTED_FOR_DISCRIMINATOR_FOR_SLICE = "UNSUPPORTED_IDENTIFIER_PATTERN_PROPERTY_NOT_SUPPORTED_FOR_DISCRIMINATOR_FOR_SLICE"; + public static final String UNSUPPORTED_IDENTIFIER_PATTERN_NO_PROPERTY_NOT_SUPPORTED_FOR_DISCRIMINATOR_FOR_SLICE = "UNSUPPORTED_IDENTIFIER_PATTERN_NO_PROPERTY_NOT_SUPPORTED_FOR_DISCRIMINATOR_FOR_SLICE"; public static final String UNSUPPORTED_VERSION_R1 = "Unsupported_version_R1"; public static final String UNSUPPORTED_VERSION_R2 = "Unsupported_version_R2"; public static final String UNSUPPORTED_VERSION_R2B = "Unsupported_version_R2B"; diff --git a/org.hl7.fhir.utilities/src/main/resources/Messages.properties b/org.hl7.fhir.utilities/src/main/resources/Messages.properties index bde0e0936..8a13d0256 100644 --- a/org.hl7.fhir.utilities/src/main/resources/Messages.properties +++ b/org.hl7.fhir.utilities/src/main/resources/Messages.properties @@ -607,3 +607,5 @@ FHIRPATH_FOCUS_PLURAL = Error evaluating FHIRPath expression: focus for {0} has REFERENCE_REF_SUSPICIOUS = The syntax of the reference ''{0}'' looks incorrect, and it should be checked TYPE_SPECIFIC_CHECKS_DT_QTY_NO_ANNOTATIONS = UCUM Codes that contain human readable annotations like {0} can be misleading. Best Practice is not to use annotations in the UCUM code, and rather to make sure that Quantity.unit is correctly human readable XHTML_XHTML_ELEMENT_ILLEGAL_IN_PARA = Illegal element name inside in a paragraph in the XHTML (''{0}'') +UNSUPPORTED_IDENTIFIER_PATTERN_PROPERTY_NOT_SUPPORTED_FOR_DISCRIMINATOR_FOR_SLICE = Unsupported property {3} on type {2} for pattern for discriminator({0}) for slice {1} +UNSUPPORTED_IDENTIFIER_PATTERN_NO_PROPERTY_NOT_SUPPORTED_FOR_DISCRIMINATOR_FOR_SLICE = Unsupported: no properties with values found on type {2} for pattern for discriminator({0}) for slice {1} \ No newline at end of file diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java index 465e0835b..5f641c3c9 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java @@ -3436,8 +3436,16 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat Identifier ii = (Identifier) pattern; expression.append(" and "); buildIdentifierExpression(ed, expression, discriminator, ii); + } else if (pattern instanceof HumanName) { + HumanName name = (HumanName) pattern; + expression.append(" and "); + buildHumanNameExpression(ed, expression, discriminator, name); + } else if (pattern instanceof Address) { + Address add = (Address) pattern; + expression.append(" and "); + buildAddressExpression(ed, expression, discriminator, add); } else { - throw new DefinitionException(context.formatMessage(I18nConstants.UNSUPPORTED_FIXED_PATTERN_TYPE_FOR_DISCRIMINATOR_FOR_SLICE__, discriminator, ed.getId(), pattern.getClass().getName())); + throw new DefinitionException(context.formatMessage(I18nConstants.UNSUPPORTED_FIXED_PATTERN_TYPE_FOR_DISCRIMINATOR_FOR_SLICE__, discriminator, ed.getId(), pattern.fhirType())); } } @@ -3472,6 +3480,101 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat expression.append(" and "); buildCodeableConceptExpression(ed, expression, TYPE, ii.getType()); } + if (first) { + throw new DefinitionException(context.formatMessage(I18nConstants.UNSUPPORTED_IDENTIFIER_PATTERN_NO_PROPERTY_NOT_SUPPORTED_FOR_DISCRIMINATOR_FOR_SLICE, discriminator, ed.getId(), ii.fhirType())); + } + expression.append(").exists()"); + } + + private void buildHumanNameExpression(ElementDefinition ed, StringBuilder expression, String discriminator, HumanName name) throws DefinitionException { + if (name.hasExtension()) + throw new DefinitionException(context.formatMessage(I18nConstants.UNSUPPORTED_IDENTIFIER_PATTERN__EXTENSIONS_ARE_NOT_ALLOWED__FOR_DISCRIMINATOR_FOR_SLICE_, discriminator, ed.getId())); + boolean first = true; + expression.append(discriminator + ".where("); + if (name.hasUse()) { + first = false; + expression.append("use = '" + name.getUse().toCode() + "'"); + } + if (name.hasText()) { + if (first) + first = false; + else + expression.append(" and "); + expression.append("text = '" + name.getText() + "'"); + } + if (name.hasFamily()) { + if (first) + first = false; + else + expression.append(" and "); + expression.append("family = '" + name.getFamily() + "'"); + } + if (name.hasGiven()) { + throw new DefinitionException(context.formatMessage(I18nConstants.UNSUPPORTED_IDENTIFIER_PATTERN_PROPERTY_NOT_SUPPORTED_FOR_DISCRIMINATOR_FOR_SLICE, discriminator, ed.getId(), name.fhirType(), "given")); + } + if (name.hasPrefix()) { + throw new DefinitionException(context.formatMessage(I18nConstants.UNSUPPORTED_IDENTIFIER_PATTERN_PROPERTY_NOT_SUPPORTED_FOR_DISCRIMINATOR_FOR_SLICE, discriminator, ed.getId(), name.fhirType(), "prefix")); + } + if (name.hasSuffix()) { + throw new DefinitionException(context.formatMessage(I18nConstants.UNSUPPORTED_IDENTIFIER_PATTERN_PROPERTY_NOT_SUPPORTED_FOR_DISCRIMINATOR_FOR_SLICE, discriminator, ed.getId(), name.fhirType(), "suffix")); + } + if (name.hasPeriod()) { + throw new DefinitionException(context.formatMessage(I18nConstants.UNSUPPORTED_IDENTIFIER_PATTERN_PROPERTY_NOT_SUPPORTED_FOR_DISCRIMINATOR_FOR_SLICE, discriminator, ed.getId(), name.fhirType(), "period")); + } + if (first) { + throw new DefinitionException(context.formatMessage(I18nConstants.UNSUPPORTED_IDENTIFIER_PATTERN_NO_PROPERTY_NOT_SUPPORTED_FOR_DISCRIMINATOR_FOR_SLICE, discriminator, ed.getId(), name.fhirType())); + } + + expression.append(").exists()"); + } + + private void buildAddressExpression(ElementDefinition ed, StringBuilder expression, String discriminator, Address add) throws DefinitionException { + if (add.hasExtension()) { + throw new DefinitionException(context.formatMessage(I18nConstants.UNSUPPORTED_IDENTIFIER_PATTERN__EXTENSIONS_ARE_NOT_ALLOWED__FOR_DISCRIMINATOR_FOR_SLICE_, discriminator, ed.getId())); + } + boolean first = true; + expression.append(discriminator + ".where("); + if (add.hasUse()) { + first = false; + expression.append("use = '" + add.getUse().toCode() + "'"); + } + if (add.hasType()) { + if (first) first = false; else expression.append(" and "); + expression.append("type = '" + add.getType().toCode() + "'"); + } + if (add.hasText()) { + if (first) first = false; else expression.append(" and "); + expression.append("text = '" + add.getText() + "'"); + } + if (add.hasCity()) { + if (first) first = false; else expression.append(" and "); + expression.append("city = '" + add.getCity() + "'"); + } + if (add.hasDistrict()) { + if (first) first = false; else expression.append(" and "); + expression.append("district = '" + add.getDistrict() + "'"); + } + if (add.hasState()) { + if (first) first = false; else expression.append(" and "); + expression.append("state = '" + add.getState() + "'"); + } + if (add.hasPostalCode()) { + if (first) first = false; else expression.append(" and "); + expression.append("postalCode = '" + add.getPostalCode() + "'"); + } + if (add.hasCountry()) { + if (first) first = false; else expression.append(" and "); + expression.append("country = '" + add.getCountry() + "'"); + } + if (add.hasLine()) { + throw new DefinitionException(context.formatMessage(I18nConstants.UNSUPPORTED_IDENTIFIER_PATTERN_PROPERTY_NOT_SUPPORTED_FOR_DISCRIMINATOR_FOR_SLICE, discriminator, ed.getId(), add.fhirType(), "line")); + } + if (add.hasPeriod()) { + throw new DefinitionException(context.formatMessage(I18nConstants.UNSUPPORTED_IDENTIFIER_PATTERN_PROPERTY_NOT_SUPPORTED_FOR_DISCRIMINATOR_FOR_SLICE, discriminator, ed.getId(), add.fhirType(), "period")); + } + if (first) { + throw new DefinitionException(context.formatMessage(I18nConstants.UNSUPPORTED_IDENTIFIER_PATTERN_NO_PROPERTY_NOT_SUPPORTED_FOR_DISCRIMINATOR_FOR_SLICE, discriminator, ed.getId(), add.fhirType())); + } expression.append(").exists()"); } @@ -3510,6 +3613,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat else expression.append(" and "); expression.append("display = '" + c.getDisplay() + "'"); } + if (first) { + throw new DefinitionException(context.formatMessage(I18nConstants.UNSUPPORTED_IDENTIFIER_PATTERN_NO_PROPERTY_NOT_SUPPORTED_FOR_DISCRIMINATOR_FOR_SLICE, discriminator, ed.getId(), cc.fhirType())); + } expression.append(").exists()"); } } @@ -3539,6 +3645,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat else expression.append(" and "); expression.append("display = '" + c.getDisplay() + "'"); } + if (first) { + throw new DefinitionException(context.formatMessage(I18nConstants.UNSUPPORTED_IDENTIFIER_PATTERN_NO_PROPERTY_NOT_SUPPORTED_FOR_DISCRIMINATOR_FOR_SLICE, discriminator, ed.getId(), c.fhirType())); + } expression.append(").exists()"); } @@ -4137,7 +4246,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } } - } else if (type.equals("Resource")) { + } else if (type.equals("Resource") || isResource(type)) { validateContains(hostContext, errors, ei.getPath(), ei.definition, definition, resource, ei.getElement(), localStack, idStatusForEntry(element, ei)); // if elementValidated = true; // (str.matches(".*([.,/])work\\1$")) @@ -4228,7 +4337,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (!elementValidated) { if (ei.getElement().getSpecial() == SpecialElement.BUNDLE_ENTRY || ei.getElement().getSpecial() == SpecialElement.BUNDLE_OUTCOME || ei.getElement().getSpecial() == SpecialElement.PARAMETER) - validateElement(hostContext, errors, p, getElementByTail(p, tail), profile, ei.definition, ei.getElement(), ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension); + validateElement(hostContext, errors, p, getElementByTail(p, tail), profile, ei.definition, ei.getElement(), ei.getElement(), type, localStack.resetIds(), thisIsCodeableConcept, checkDisplay, thisExtension); else validateElement(hostContext, errors, p, getElementByTail(p, tail), profile, ei.definition, resource, ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension); } @@ -4242,6 +4351,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } + private boolean isResource(String type) { + StructureDefinition sd = context.fetchTypeDefinition(type); + return sd != null && sd.getKind().equals(StructureDefinitionKind.RESOURCE); + } + private void trackUsage(StructureDefinition profile, ValidatorHostContext hostContext, Element element) { if (tracker != null) { tracker.recordProfileUsage(profile, hostContext.getAppContext(), element); diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTests.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTests.java index a2e775817..7dc58c52c 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTests.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTests.java @@ -259,6 +259,8 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe checkOutcomes(errorsProfile, profile, filename, name); } if (content.has("logical")) { + System.out.print("** Logical: "); + JsonObject logical = content.getAsJsonObject("logical"); if (logical.has("supporting")) { for (JsonElement e : logical.getAsJsonArray("supporting")) { @@ -271,6 +273,11 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe val.getContext().cacheResource(mr); } } + if (logical.has("packages")) { + for (JsonElement e : logical.getAsJsonArray("packages")) { + vCurr.loadIg(e.getAsString(), true); + } + } List errorsLogical = new ArrayList(); Element le = val.validate(null, errorsLogical, IOUtils.toInputStream(testCaseContent, Charsets.UTF_8), (name.endsWith(".json")) ? FhirFormat.JSON : FhirFormat.XML); if (logical.has("expressions")) {