diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ValidateCommand.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ValidateCommand.java index 3d652730a79..2015e98f2e0 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ValidateCommand.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ValidateCommand.java @@ -35,6 +35,7 @@ import org.apache.commons.cli.ParseException; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.text.WordUtils; import org.fusesource.jansi.Ansi.Color; +import org.hl7.fhir.common.hapi.validation.support.NpmPackageValidationSupport; import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain; import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator; @@ -149,21 +150,25 @@ public class ValidateCommand extends BaseCommand { } if (theCommandLine.hasOption("p")) { + ValidationSupportChain validationSupportChain; switch (ctx.getVersion().getVersion()) { case DSTU2: { FhirInstanceValidator instanceValidator; - ValidationSupportChain validationSupportChain = + validationSupportChain = ValidationSupportChainCreator.getValidationSupportChainDstu2(ctx, theCommandLine); + validationSupportChain.oneCodingIsSufficient = true; instanceValidator = new FhirInstanceValidator(validationSupportChain); val.registerValidatorModule(instanceValidator); break; } case DSTU3: case R4: { + ourLog.info("Adding FHIR R4 validation support chain"); FhirInstanceValidator instanceValidator = new FhirInstanceValidator(ctx); val.registerValidatorModule(instanceValidator); - ValidationSupportChain validationSupportChain = + validationSupportChain = ValidationSupportChainCreator.getValidationSupportChainR4(ctx, theCommandLine); + validationSupportChain.oneCodingIsSufficient = true; instanceValidator.setValidationSupport(validationSupportChain); break; } @@ -171,6 +176,21 @@ public class ValidateCommand extends BaseCommand { throw new ParseException( Msg.code(1620) + "Profile validation (-p) is not supported for this FHIR version"); } + + // If they want to do profile validation - there might be a whole IG to load... + if (theCommandLine.hasOption("igpack")) { + String igPack = theCommandLine.getOptionValue("igpack"); + ourLog.info("Loading IG Package from {} to the validation support chain", igPack); + try { + NpmPackageValidationSupport npmPackageSupport = new NpmPackageValidationSupport(ctx); + npmPackageSupport.loadPackageFromFile(igPack); + validationSupportChain.addValidationSupport(0, npmPackageSupport); + } catch (IOException iox) { + throw new ParseException( + Msg.code(1620) + "IGpack validation (--igpack) failed: " + iox.getMessage()); + } + ourLog.info("Completed loading IG Package from {}", igPack); + } } val.setValidateAgainstStandardSchema(theCommandLine.hasOption("x")); diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/NpmPackageValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/NpmPackageValidationSupport.java index fa409f5f93b..6c560d2a045 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/NpmPackageValidationSupport.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/NpmPackageValidationSupport.java @@ -3,13 +3,14 @@ package org.hl7.fhir.common.hapi.validation.support; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.parser.LenientErrorHandler; -import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.util.ClasspathUtil; import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.npm.NpmPackage; +import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; @@ -31,11 +32,16 @@ public class NpmPackageValidationSupport extends PrePopulatedValidationSupport { super(theFhirContext); } + @Override + public String getName() { + return getFhirContext().getVersion().getVersion() + " NPM Package Validation Support"; + } + /** * Load an NPM package using a classpath specification, e.g. /path/to/resource/my_package.tgz. The * classpath spec can optionally be prefixed with the string classpath: * - * @throws InternalErrorException If the classpath file can't be found + * @throws IOException If the classpath file can't be found */ public void loadPackageFromClasspath(String theClasspath) throws IOException { try (InputStream is = ClasspathUtil.loadResourceAsStream(theClasspath)) { @@ -47,6 +53,21 @@ public class NpmPackageValidationSupport extends PrePopulatedValidationSupport { } } + /** + * Load an NPM package from the filesystem e.g. my_package.tgz. + * + * @throws IOException If the package file can't be found + */ + public void loadPackageFromFile(String theFile) throws IOException { + File inFile = new File(theFile); + InputStream is = new FileInputStream(inFile); + NpmPackage pkg = NpmPackage.fromPackage(is); + if (pkg.getFolders().containsKey("package")) { + loadResourcesFromPackage(pkg); + loadBinariesFromPackage(pkg); + } + } + private void loadResourcesFromPackage(NpmPackage thePackage) { NpmPackage.NpmPackageFolder packageFolder = thePackage.getFolders().get("package"); diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChain.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChain.java index 4eea50efc11..789fcc0ee02 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChain.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChain.java @@ -371,12 +371,13 @@ public class ValidationSupportChain implements IValidationSupport { if (retVal != null) { if (ourLog.isDebugEnabled()) { ourLog.debug( - "Code {}|{} '{}' in ValueSet {} validated by {}", + "Code {}|{} '{}' in ValueSet {} validated by {} result: {}", theCodeSystem, theCode, theDisplay, theValueSetUrl, - next.getName()); + next.getName(), + retVal.getMessage() != null ? retVal.getMessage() : "Success"); } return retVal; } @@ -401,12 +402,13 @@ public class ValidationSupportChain implements IValidationSupport { if (retVal != null) { if (ourLog.isDebugEnabled()) { ourLog.debug( - "Code {}|{} '{}' in ValueSet {} validated by {}", + "Code {}|{} '{}' in ValueSet {} validated by {} result: {}", theCodeSystem, theCode, theDisplay, theValueSet.getIdElement(), - next.getName()); + next.getName(), + retVal.getMessage() != null ? retVal.getMessage() : "Success"); } return retVal; } @@ -445,4 +447,18 @@ public class ValidationSupportChain implements IValidationSupport { } return null; } + /** + * See VersionSpecificWorkerContextWrapper#validateCode in hapi-fhir-validation. + *

+ * If true, validation for codings will return a positive result if AT LEAST ONE coding is valid. + * If false, validation for codings will return a positive result if there is ALL codings are valid. + *

+ * @return if the application has configured validation to use logical AND, as opposed to logical OR, which is the default + */ + public boolean oneCodingIsSufficient = false; // default value from parent Interface (it's the wrong way round !!!) + + @Override + public boolean isEnabledValidationForCodingsLogicalAnd() { // should be named ....LogicalAny + return oneCodingIsSufficient; + } }