Merge pull request #1291 from hapifhir/do-20230602-refactor-cli-params

Refactor Validator CLI params
This commit is contained in:
Grahame Grieve 2023-06-19 02:29:32 +10:00 committed by GitHub
commit b7425d64b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 1491 additions and 182 deletions

View File

@ -1,16 +1,11 @@
package org.hl7.fhir.validation; package org.hl7.fhir.validation;
import java.io.File;
import java.io.IOException;
import java.net.Authenticator; import java.net.Authenticator;
import java.net.PasswordAuthentication; import java.net.PasswordAuthentication;
import java.net.URISyntaxException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import org.hl7.fhir.convertors.loaders.loaderR5.R4BToR5Loader;
/* /*
Copyright (c) 2011+, HL7, Inc. Copyright (c) 2011+, HL7, Inc.
All rights reserved. All rights reserved.
@ -69,27 +64,18 @@ POSSIBILITY OF SUCH DAMAGE.
*/ */
import org.hl7.fhir.r5.model.ImplementationGuide; import org.apache.commons.text.WordUtils;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.terminologies.JurisdictionUtilities; import org.hl7.fhir.r5.terminologies.JurisdictionUtilities;
import org.hl7.fhir.utilities.FileFormat; import org.hl7.fhir.utilities.FileFormat;
import org.hl7.fhir.utilities.TimeTracker; import org.hl7.fhir.utilities.TimeTracker;
import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.utilities.VersionUtilities;
import org.hl7.fhir.utilities.json.JsonException;
import org.hl7.fhir.utilities.npm.CommonPackages;
import org.hl7.fhir.utilities.settings.FhirSettings; import org.hl7.fhir.utilities.settings.FhirSettings;
import org.hl7.fhir.validation.cli.model.CliContext; import org.hl7.fhir.validation.cli.model.CliContext;
import org.hl7.fhir.validation.cli.services.ComparisonService;
import org.hl7.fhir.validation.cli.services.ValidationService; import org.hl7.fhir.validation.cli.services.ValidationService;
import org.hl7.fhir.validation.cli.tasks.*;
import org.hl7.fhir.validation.cli.utils.Display; import org.hl7.fhir.validation.cli.utils.Display;
import org.hl7.fhir.validation.cli.utils.EngineMode;
import org.hl7.fhir.validation.cli.utils.Params; import org.hl7.fhir.validation.cli.utils.Params;
import org.hl7.fhir.validation.special.R4R5MapTester;
import org.hl7.fhir.validation.special.TxTester;
import org.hl7.fhir.validation.special.TxTester.InternalTxLoader;
import org.hl7.fhir.validation.testexecutor.TestExecutor;
import org.hl7.fhir.validation.testexecutor.TestExecutorParams;
/** /**
* A executable class that will validate one or more FHIR resources against * A executable class that will validate one or more FHIR resources against
@ -120,17 +106,45 @@ public class ValidatorCli {
private static ValidationService validationService = new ValidationService(); private static ValidationService validationService = new ValidationService();
public static void main(String[] args) throws Exception { protected ValidationService myValidationService;
final List<CliTask> cliTasks;
final CliTask defaultCliTask = new ValidateTask();
protected ValidatorCli(ValidationService validationService) {
myValidationService = validationService;
cliTasks = getCliTasks();
}
protected List<CliTask> getCliTasks() {
return List.of(
new CompareTask(),
new CompileTask(),
new ConvertTask(),
new FhirpathTask(),
new InstallTask(),
new LangTransformTask(),
new NarrativeTask(),
new ScanTask(),
new SnapshotTask(),
new SpecialTask(),
new SpreadsheetTask(),
new TestsTask(),
new TxTestsTask(),
new TransformTask(),
new VersionTask(),
defaultCliTask);
}
protected void readParamsAndExecuteTask(CliContext cliContext, String[] args) throws Exception {
TimeTracker tt = new TimeTracker(); TimeTracker tt = new TimeTracker();
TimeTracker.Session tts = tt.start("Loading"); TimeTracker.Session tts = tt.start("Loading");
args = addAdditionalParamsForIpsParam(args); args = addAdditionalParamsForIpsParam(args);
setJavaSystemProxyParamsFromParams(args); setJavaSystemProxyParamsFromParams(args);
Display.displayVersion(); Display.displayVersion(System.out);
Display.displaySystemInfo(); Display.displaySystemInfo(System.out);
CliContext cliContext = Params.loadCliContext(args);
if (cliContext.getFhirSettingsFile() != null) { if (cliContext.getFhirSettingsFile() != null) {
FhirSettings.setExplicitFilePath(cliContext.getFhirSettingsFile()); FhirSettings.setExplicitFilePath(cliContext.getFhirSettingsFile());
@ -139,34 +153,54 @@ public class ValidatorCli {
FileFormat.checkCharsetAndWarnIfNotUTF8(System.out); FileFormat.checkCharsetAndWarnIfNotUTF8(System.out);
if (shouldDisplayHelpToUser(args)) { if (shouldDisplayHelpToUser(args)) {
Display.displayHelpDetails(); String helpTarget = Params.getParam(args, "-" + Params.HELP);
} else if (Params.hasParam(args, Params.COMPARE)) { if (helpTarget != null) {
if (destinationDirectoryValid(Params.getParam(args, Params.DESTINATION))) { cliTasks.stream()
doLeftRightComparison(args, cliContext, tt); .filter(task -> helpTarget.equals(task.getName()))
} .findFirst()
} else if (Params.hasParam(args, Params.TEST) || Params.hasParam(args, Params.TX_TESTS)) { .ifPresent(cliTask -> {
parseTestParamsAndExecute(args); displayHelpForTask(cliTask);
} else if (Params.hasParam(args, Params.SPECIAL)) { });
executeSpecial(args);
} else { } else {
Display.printCliArgumentsAndInfo(args); displayHelpForDefaultTask();
doValidation(tt, tts, cliContext); }return;
}
} }
private static void executeSpecial(String[] args) throws JsonException, IOException { readParamsAndExecuteTask(tt, tts, cliContext, args);
String specialMode = Params.getParam(args, Params.SPECIAL);
if ("r4r5tests".equals(specialMode)) {
final String target = Params.getParam(args, Params.TARGET);
final String source = Params.getParam(args, Params.SOURCE);
final String filter = Params.getParam(args, Params.FILTER);
if (new File(target).exists()) {
new R4R5MapTester().testMaps(target, source, filter);
} }
} else {
System.out.println("Unknown SpecialMode "+specialMode); private void displayHelpForDefaultTask() {
System.out.println();
System.out.println(WordUtils.wrap("This is the help text for default usage of the validator. Help for other modes of operation is available by using the parameter '-help [mode]' for one of the following modes:", 80));
System.out.println();
for (CliTask cliTask : cliTasks) {
if (!cliTask.isHidden()) {
System.out.println(" " + cliTask.getName());
} }
} }
System.out.println();
System.out.println(defaultCliTask.getDisplayName() + " (default usage)");
System.out.println("=".repeat(defaultCliTask.getDisplayName().length()));
System.out.println();
defaultCliTask.printHelp(System.out);
}
private void displayHelpForTask(CliTask cliTask) {
System.out.println();
System.out.println("This is the help text for '" + cliTask.getName() + "'. To display all available help options, use the '-help' or 'help' parameter.");
System.out.println();
System.out.println(cliTask.getDisplayName());
System.out.println("=".repeat(cliTask.getDisplayName().length()));
System.out.println();
cliTask.printHelp(System.out);
}
public static void main(String[] args) throws Exception {
final ValidatorCli validatorCli = new ValidatorCli(validationService);
final CliContext cliContext = Params.loadCliContext(args);
validatorCli.readParamsAndExecuteTask(cliContext, args);
}
private static void setJavaSystemProxyParamsFromParams(String[] args) { private static void setJavaSystemProxyParamsFromParams(String[] args) {
@ -217,31 +251,6 @@ public class ValidatorCli {
} }
} }
protected static void parseTestParamsAndExecute(String[] args) throws IOException, URISyntaxException {
if (Params.hasParam(args, Params.TX_TESTS)) {
final String source = Params.getParam(args, Params.SOURCE);
final String output = Params.getParam(args, Params.OUTPUT);
final String version = Params.getParam(args, Params.VERSION);
final String tx = Params.getParam(args, Params.TERMINOLOGY);
final String filter = Params.getParam(args, Params.FILTER);
boolean ok = new TxTester(new InternalTxLoader(source, output), tx, false).setOutput(output).execute(version, filter);
System.exit(ok ? 1 : 0);
} else {
final String testModuleParam = Params.getParam(args, Params.TEST_MODULES);
final String testClassnameFilter = Params.getParam(args, Params.TEST_NAME_FILTER);
final String testCasesDirectory = Params.getParam(args, Params.TEST);
final String txCacheDirectory = Params.getParam(args, Params.TERMINOLOGY_CACHE);
assert TestExecutorParams.isValidModuleParam(testModuleParam) : "Invalid test module param: " + testModuleParam;
final String[] moduleNamesArg = TestExecutorParams.parseModuleParam(testModuleParam);
assert TestExecutorParams.isValidClassnameFilterParam(testClassnameFilter) : "Invalid regex for test classname filter: " + testClassnameFilter;
new TestExecutor(moduleNamesArg).executeTests(testClassnameFilter, txCacheDirectory, testCasesDirectory);
System.exit(0);
}
}
private static String[] addAdditionalParamsForIpsParam(String[] args) { private static String[] addAdditionalParamsForIpsParam(String[] args) {
// ips$branch --> -version 4.0 -ig hl7.fhir.uv.ips#current$connectathon-2 -profile http://hl7.org/fhir/uv/ips/StructureDefinition/Bundle-uv-ips // ips$branch --> -version 4.0 -ig hl7.fhir.uv.ips#current$connectathon-2 -profile http://hl7.org/fhir/uv/ips/StructureDefinition/Bundle-uv-ips
List<String> res = new ArrayList<>(); List<String> res = new ArrayList<>();
@ -271,43 +280,52 @@ public class ValidatorCli {
return r; return r;
} }
private static boolean destinationDirectoryValid(String dest) {
if (dest == null) {
System.out.println("no -dest parameter provided");
return false;
} else if (!new File(dest).isDirectory()) {
System.out.println("Specified destination (-dest parameter) is not valid: \"" + dest + "\")");
return false;
} else {
System.out.println("Valid destination directory provided: \"" + dest + "\")");
return true;
}
}
private static boolean shouldDisplayHelpToUser(String[] args) {
private boolean shouldDisplayHelpToUser(String[] args) {
return (args.length == 0 return (args.length == 0
|| Params.hasParam(args, Params.HELP) || Params.hasParam(args, Params.HELP)
|| Params.hasParam(args, "-" + Params.HELP)
|| Params.hasParam(args, "?") || Params.hasParam(args, "?")
|| Params.hasParam(args, "-?") || Params.hasParam(args, "-?")
|| Params.hasParam(args, "/?")); || Params.hasParam(args, "/?"));
} }
private static void doLeftRightComparison(String[] args, CliContext cliContext, TimeTracker tt) throws Exception { private void readParamsAndExecuteTask(TimeTracker tt, TimeTracker.Session tts, CliContext cliContext, String[] params) throws Exception {
Display.printCliArgumentsAndInfo(args); Display.printCliParamsAndInfo(params);
final CliTask cliTask = selectCliTask(cliContext, params);
if (cliTask instanceof ValidationEngineTask) {
if (cliContext.getSv() == null) { if (cliContext.getSv() == null) {
cliContext.setSv(validationService.determineVersion(cliContext)); cliContext.setSv(myValidationService.determineVersion(cliContext));
} }
String v = VersionUtilities.getCurrentVersion(cliContext.getSv()); ValidationEngine validationEngine = getValidationEngine(tt, cliContext);
String definitions = VersionUtilities.packageForVersion(v) + "#" + v; tts.end();
ValidationEngine validator = validationService.initializeValidator(cliContext, definitions, tt); ((ValidationEngineTask) cliTask).executeTask(myValidationService, validationEngine, cliContext, params, tt, tts);
validator.loadPackage(CommonPackages.ID_PUBPACK, null); } else if (cliTask instanceof StandaloneTask) {
ComparisonService.doLeftRightComparison(args, Params.getParam(args, Params.DESTINATION), validator); ((StandaloneTask) cliTask).executeTask(cliContext,params,tt,tts);
} }
private static void doValidation(TimeTracker tt, TimeTracker.Session tts, CliContext cliContext) throws Exception { System.out.println("Done. " + tt.report()+". Max Memory = "+Utilities.describeSize(Runtime.getRuntime().maxMemory()));
if (cliContext.getSv() == null) {
cliContext.setSv(validationService.determineVersion(cliContext));
} }
private CliTask selectCliTask(CliContext cliContext, String[] params) {
CliTask cliTask = null;
for(CliTask candidateTask : cliTasks) {
if (candidateTask.shouldExecuteTask(cliContext, params)) {
cliTask = candidateTask;
}
}
if (cliTask == null)
cliTask = defaultCliTask;
return cliTask;
}
private ValidationEngine getValidationEngine(TimeTracker tt, CliContext cliContext) throws Exception {
ValidationEngine validationEngine;
System.out.println(" Locale: "+Locale.getDefault().getDisplayCountry()+"/"+Locale.getDefault().getCountry()); System.out.println(" Locale: "+Locale.getDefault().getDisplayCountry()+"/"+Locale.getDefault().getCountry());
if (cliContext.getJurisdiction() == null) { if (cliContext.getJurisdiction() == null) {
System.out.println(" Jurisdiction: None specified (locale = "+Locale.getDefault().getCountry()+")"); System.out.println(" Jurisdiction: None specified (locale = "+Locale.getDefault().getCountry()+")");
@ -318,57 +336,12 @@ public class ValidatorCli {
System.out.println("Loading"); System.out.println("Loading");
String definitions = "dev".equals(cliContext.getSv()) ? "hl7.fhir.r5.core#current" : VersionUtilities.packageForVersion(cliContext.getSv()) + "#" + VersionUtilities.getCurrentVersion(cliContext.getSv()); String definitions = "dev".equals(cliContext.getSv()) ? "hl7.fhir.r5.core#current" : VersionUtilities.packageForVersion(cliContext.getSv()) + "#" + VersionUtilities.getCurrentVersion(cliContext.getSv());
ValidationEngine validator = validationService.initializeValidator(cliContext, definitions, tt); validationEngine = myValidationService.initializeValidator(cliContext, definitions, tt);
tts.end(); return validationEngine;
switch (cliContext.getMode()) {
case TRANSFORM:
validationService.transform(cliContext, validator);
break;
case COMPILE:
validationService.compile(cliContext, validator);
break;
case NARRATIVE:
validationService.generateNarrative(cliContext, validator);
break;
case SNAPSHOT:
validationService.generateSnapshot(cliContext, validator);
break;
case INSTALL:
validationService.install(cliContext, validator);
break;
case SPREADSHEET:
validationService.generateSpreadsheet(cliContext, validator);
break;
case CONVERT:
validationService.convertSources(cliContext, validator);
break;
case FHIRPATH:
validationService.evaluateFhirpath(cliContext, validator);
break;
case VERSION:
validationService.transformVersion(cliContext, validator);
break;
case LANG_TRANSFORM:
validationService.transformLang(cliContext, validator);
break;
case VALIDATION:
case SCAN:
default:
for (String s : cliContext.getProfiles()) {
if (!validator.getContext().hasResource(StructureDefinition.class, s) && !validator.getContext().hasResource(ImplementationGuide.class, s)) {
System.out.println(" Fetch Profile from " + s);
validator.loadProfile(cliContext.getLocations().getOrDefault(s, s));
} }
}
System.out.println("Validating"); protected void validateScan(CliContext cliContext, ValidationEngine validator) throws Exception {
if (cliContext.getMode() == EngineMode.SCAN) {
Scanner validationScanner = new Scanner(validator.getContext(), validator.getValidator(null), validator.getIgLoader(), validator.getFhirPathEngine()); Scanner validationScanner = new Scanner(validator.getContext(), validator.getValidator(null), validator.getIgLoader(), validator.getFhirPathEngine());
validationScanner.validateScan(cliContext.getOutput(), cliContext.getSources()); validationScanner.validateScan(cliContext.getOutput(), cliContext.getSources());
} else {
validationService.validateSources(cliContext, validator);
}
break;
}
System.out.println("Done. " + tt.report()+". Max Memory = "+Utilities.describeSize(Runtime.getRuntime().maxMemory()));
} }
} }

View File

@ -0,0 +1,21 @@
package org.hl7.fhir.validation.cli.tasks;
import lombok.Getter;
import org.hl7.fhir.utilities.TimeTracker;
import org.hl7.fhir.validation.cli.model.CliContext;
import java.io.IOException;
public abstract class CliTask {
public abstract String getName();
public abstract String getDisplayName();
public abstract boolean isHidden();
public abstract boolean shouldExecuteTask(CliContext cliContext, String[] args);
public abstract void printHelp(java.io.PrintStream out);
}

View File

@ -0,0 +1,70 @@
package org.hl7.fhir.validation.cli.tasks;
import org.hl7.fhir.utilities.TimeTracker;
import org.hl7.fhir.utilities.VersionUtilities;
import org.hl7.fhir.utilities.npm.CommonPackages;
import org.hl7.fhir.validation.ValidationEngine;
import org.hl7.fhir.validation.cli.model.CliContext;
import org.hl7.fhir.validation.cli.services.ComparisonService;
import org.hl7.fhir.validation.cli.services.ValidationService;
import org.hl7.fhir.validation.cli.utils.Display;
import org.hl7.fhir.validation.cli.utils.Params;
import java.io.File;
import java.io.PrintStream;
public class CompareTask extends ValidationEngineTask {
@Override
public String getName() {
return "compare";
}
@Override
public String getDisplayName() {
return "Comparing Profiles";
}
@Override
public boolean isHidden() {
return false;
}
@Override
public boolean shouldExecuteTask(CliContext cliContext, String[] args) {
return Params.hasParam(args, Params.COMPARE);
}
@Override
public void printHelp(PrintStream out) {
Display.displayHelpDetails(out,"help/compare.txt");
}
@Override
public void executeTask(ValidationService validationService, ValidationEngine validationEngine, CliContext cliContext, String[] args, TimeTracker tt, TimeTracker.Session tts) throws Exception {
Display.printCliParamsAndInfo(args);
if (!destinationDirectoryValid(Params.getParam(args, Params.DESTINATION))) {
return;
}
if (cliContext.getSv() == null) {
cliContext.setSv(validationService.determineVersion(cliContext));
}
String v = VersionUtilities.getCurrentVersion(cliContext.getSv());
String definitions = VersionUtilities.packageForVersion(v) + "#" + v;
ValidationEngine validator = validationService.initializeValidator(cliContext, definitions, tt);
validator.loadPackage(CommonPackages.ID_PUBPACK, null);
ComparisonService.doLeftRightComparison(args, Params.getParam(args, Params.DESTINATION), validator);
}
private boolean destinationDirectoryValid(String dest) {
if (dest == null) {
System.out.println("no -dest parameter provided");
return false;
} else if (!new File(dest).isDirectory()) {
System.out.println("Specified destination (-dest parameter) is not valid: \"" + dest + "\")");
return false;
} else {
System.out.println("Valid destination directory provided: \"" + dest + "\")");
return true;
}
}
}

View File

@ -0,0 +1,43 @@
package org.hl7.fhir.validation.cli.tasks;
import org.hl7.fhir.utilities.TimeTracker;
import org.hl7.fhir.validation.ValidationEngine;
import org.hl7.fhir.validation.cli.model.CliContext;
import org.hl7.fhir.validation.cli.services.ValidationService;
import org.hl7.fhir.validation.cli.utils.EngineMode;
import java.io.PrintStream;
public class CompileTask extends ValidationEngineTask {
@Override
public String getName() {
return "compile";
}
@Override
public String getDisplayName() {
return "Compile";
}
@Override
public boolean isHidden() {
return true;
}
@Override
public boolean shouldExecuteTask(CliContext cliContext, String[] args) {
return cliContext.getMode() == EngineMode.COMPILE;
}
@Override
public void printHelp(PrintStream out) {
}
@Override
public void executeTask(ValidationService validationService, ValidationEngine validationEngine, CliContext cliContext, String[] args, TimeTracker tt, TimeTracker.Session tts) throws Exception {
validationService.compile(cliContext, validationEngine);
}
}

View File

@ -0,0 +1,44 @@
package org.hl7.fhir.validation.cli.tasks;
import org.hl7.fhir.utilities.TimeTracker;
import org.hl7.fhir.validation.ValidationEngine;
import org.hl7.fhir.validation.cli.model.CliContext;
import org.hl7.fhir.validation.cli.services.ValidationService;
import org.hl7.fhir.validation.cli.utils.Display;
import org.hl7.fhir.validation.cli.utils.EngineMode;
import java.io.PrintStream;
public class ConvertTask extends ValidationEngineTask {
@Override
public String getName() {
return "convert";
}
@Override
public String getDisplayName() {
return "Conversion";
}
@Override
public boolean isHidden() {
return false;
}
@Override
public boolean shouldExecuteTask(CliContext cliContext, String[] args) {
return cliContext.getMode() == EngineMode.CONVERT;
}
@Override
public void printHelp(PrintStream out) {
Display.displayHelpDetails(out,"help/convert.txt");
}
@Override
public void executeTask(ValidationService validationService, ValidationEngine validationEngine, CliContext cliContext, String[] args, TimeTracker tt, TimeTracker.Session tts) throws Exception {
validationService.convertSources(cliContext, validationEngine);
}
}

View File

@ -0,0 +1,44 @@
package org.hl7.fhir.validation.cli.tasks;
import org.hl7.fhir.utilities.TimeTracker;
import org.hl7.fhir.validation.ValidationEngine;
import org.hl7.fhir.validation.cli.model.CliContext;
import org.hl7.fhir.validation.cli.services.ValidationService;
import org.hl7.fhir.validation.cli.utils.Display;
import org.hl7.fhir.validation.cli.utils.EngineMode;
import java.io.PrintStream;
public class FhirpathTask extends ValidationEngineTask {
@Override
public String getName() {
return "fhirpath";
}
@Override
public String getDisplayName() {
return "FHIRPath";
}
@Override
public boolean isHidden() {
return false;
}
@Override
public boolean shouldExecuteTask(CliContext cliContext, String[] args) {
return cliContext.getMode() == EngineMode.FHIRPATH;
}
@Override
public void printHelp(PrintStream out) {
Display.displayHelpDetails(out,"help/fhirpath.txt");
}
@Override
public void executeTask(ValidationService validationService, ValidationEngine validationEngine, CliContext cliContext, String[] args, TimeTracker tt, TimeTracker.Session tts) throws Exception {
validationService.evaluateFhirpath(cliContext, validationEngine);
}
}

View File

@ -0,0 +1,42 @@
package org.hl7.fhir.validation.cli.tasks;
import org.hl7.fhir.utilities.TimeTracker;
import org.hl7.fhir.validation.ValidationEngine;
import org.hl7.fhir.validation.cli.model.CliContext;
import org.hl7.fhir.validation.cli.services.ValidationService;
import org.hl7.fhir.validation.cli.utils.EngineMode;
import java.io.PrintStream;
public class InstallTask extends ValidationEngineTask {
@Override
public String getName() {
return "install";
}
@Override
public String getDisplayName() {
return "Install";
}
@Override
public boolean isHidden() {
return true;
}
@Override
public boolean shouldExecuteTask(CliContext cliContext, String[] args) {
return cliContext.getMode() == EngineMode.INSTALL;
}
@Override
public void printHelp(PrintStream out) {
}
@Override
public void executeTask(ValidationService validationService, ValidationEngine validationEngine, CliContext cliContext, String[] args, TimeTracker tt, TimeTracker.Session tts) throws Exception {
validationService.install(cliContext, validationEngine);
}
}

View File

@ -0,0 +1,43 @@
package org.hl7.fhir.validation.cli.tasks;
import org.hl7.fhir.utilities.TimeTracker;
import org.hl7.fhir.validation.ValidationEngine;
import org.hl7.fhir.validation.cli.model.CliContext;
import org.hl7.fhir.validation.cli.services.ValidationService;
import org.hl7.fhir.validation.cli.utils.EngineMode;
import java.io.PrintStream;
public class LangTransformTask extends ValidationEngineTask {
@Override
public String getName() {
return "lang-transform";
}
@Override
public String getDisplayName() {
return "Lang Transform";
}
@Override
public boolean isHidden() {
return true;
}
@Override
public boolean shouldExecuteTask(CliContext cliContext, String[] args) {
return cliContext.getMode() == EngineMode.LANG_TRANSFORM;
}
@Override
public void printHelp(PrintStream out) {
}
@Override
public void executeTask(ValidationService validationService, ValidationEngine validationEngine, CliContext cliContext, String[] args, TimeTracker tt, TimeTracker.Session tts) throws Exception {
validationService.transformLang(cliContext, validationEngine);
}
}

View File

@ -0,0 +1,44 @@
package org.hl7.fhir.validation.cli.tasks;
import org.hl7.fhir.utilities.TimeTracker;
import org.hl7.fhir.validation.ValidationEngine;
import org.hl7.fhir.validation.cli.model.CliContext;
import org.hl7.fhir.validation.cli.services.ValidationService;
import org.hl7.fhir.validation.cli.utils.Display;
import org.hl7.fhir.validation.cli.utils.EngineMode;
import java.io.PrintStream;
public class NarrativeTask extends ValidationEngineTask {
@Override
public String getName() {
return "narrative";
}
@Override
public String getDisplayName() {
return "Narratives";
}
@Override
public boolean isHidden() {
return false;
}
@Override
public boolean shouldExecuteTask(CliContext cliContext, String[] args) {
return cliContext.getMode() == EngineMode.NARRATIVE;
}
@Override
public void printHelp(PrintStream out) {
Display.displayHelpDetails(out,"help/narrative.txt");
}
@Override
public void executeTask(ValidationService validationService, ValidationEngine validationEngine, CliContext cliContext, String[] args, TimeTracker tt, TimeTracker.Session tts) throws Exception {
validationService.generateNarrative(cliContext, validationEngine);
}
}

View File

@ -0,0 +1,46 @@
package org.hl7.fhir.validation.cli.tasks;
import org.hl7.fhir.utilities.TimeTracker;
import org.hl7.fhir.validation.Scanner;
import org.hl7.fhir.validation.ValidationEngine;
import org.hl7.fhir.validation.cli.model.CliContext;
import org.hl7.fhir.validation.cli.services.ValidationService;
import org.hl7.fhir.validation.cli.utils.EngineMode;
import java.io.PrintStream;
public class ScanTask extends ValidationEngineTask {
@Override
public String getName() {
return "scan";
}
@Override
public String getDisplayName() {
return "Scan";
}
@Override
public boolean isHidden() {
return true;
}
@Override
public boolean shouldExecuteTask(CliContext cliContext, String[] args) {
return cliContext.getMode() == EngineMode.SCAN;
}
@Override
public void printHelp(PrintStream out) {
}
@Override
public void executeTask(ValidationService validationService, ValidationEngine validationEngine, CliContext cliContext, String[] args, TimeTracker tt, TimeTracker.Session tts) throws Exception {
Scanner validationScanner = new Scanner(validationEngine.getContext(), validationEngine.getValidator(null), validationEngine.getIgLoader(), validationEngine.getFhirPathEngine());
validationScanner.validateScan(cliContext.getOutput(), cliContext.getSources());
}
}

View File

@ -0,0 +1,44 @@
package org.hl7.fhir.validation.cli.tasks;
import org.hl7.fhir.utilities.TimeTracker;
import org.hl7.fhir.validation.ValidationEngine;
import org.hl7.fhir.validation.cli.model.CliContext;
import org.hl7.fhir.validation.cli.services.ValidationService;
import org.hl7.fhir.validation.cli.utils.Display;
import org.hl7.fhir.validation.cli.utils.EngineMode;
import java.io.PrintStream;
public class SnapshotTask extends ValidationEngineTask {
@Override
public String getName() {
return "snapshot";
}
@Override
public String getDisplayName() {
return "Snapshots";
}
@Override
public boolean isHidden() {
return false;
}
@Override
public boolean shouldExecuteTask(CliContext cliContext, String[] args) {
return cliContext.getMode() == EngineMode.SNAPSHOT;
}
@Override
public void printHelp(PrintStream out) {
Display.displayHelpDetails(out,"help/snapshot.txt");
}
@Override
public void executeTask(ValidationService validationService, ValidationEngine validationEngine, CliContext cliContext, String[] args, TimeTracker tt, TimeTracker.Session tts) throws Exception {
validationService.generateSnapshot(cliContext, validationEngine);
}
}

View File

@ -0,0 +1,51 @@
package org.hl7.fhir.validation.cli.tasks;
import org.hl7.fhir.utilities.TimeTracker;
import org.hl7.fhir.validation.cli.model.CliContext;
import org.hl7.fhir.validation.cli.utils.Params;
import org.hl7.fhir.validation.special.R4R5MapTester;
import java.io.File;
import java.io.PrintStream;
public class SpecialTask extends StandaloneTask{
@Override
public String getName() {
return "special";
}
@Override
public String getDisplayName() {
return "Special";
}
@Override
public boolean isHidden() {
return true;
}
@Override
public boolean shouldExecuteTask(CliContext cliContext, String[] args) {
return Params.hasParam(args, Params.SPECIAL);
}
@Override
public void printHelp(PrintStream out) {
}
@Override
public void executeTask(CliContext cliContext, String[] args, TimeTracker tt, TimeTracker.Session tts) throws Exception {
String specialMode = Params.getParam(args, Params.SPECIAL);
if ("r4r5tests".equals(specialMode)) {
final String target = Params.getParam(args, Params.TARGET);
final String source = Params.getParam(args, Params.SOURCE);
final String filter = Params.getParam(args, Params.FILTER);
if (new File(target).exists()) {
new R4R5MapTester().testMaps(target, source, filter);
}
} else {
System.out.println("Unknown SpecialMode "+specialMode);
}
}
}

View File

@ -0,0 +1,43 @@
package org.hl7.fhir.validation.cli.tasks;
import org.hl7.fhir.utilities.TimeTracker;
import org.hl7.fhir.validation.ValidationEngine;
import org.hl7.fhir.validation.cli.model.CliContext;
import org.hl7.fhir.validation.cli.services.ValidationService;
import org.hl7.fhir.validation.cli.utils.EngineMode;
import java.io.PrintStream;
public class SpreadsheetTask extends ValidationEngineTask {
@Override
public String getName() {
return "spreadsheet";
}
@Override
public String getDisplayName() {
return "Spreadsheet";
}
@Override
public boolean isHidden() {
return true;
}
@Override
public boolean shouldExecuteTask(CliContext cliContext, String[] args) {
return cliContext.getMode() == EngineMode.SPREADSHEET;
}
@Override
public void printHelp(PrintStream out) {
}
@Override
public void executeTask(ValidationService validationService, ValidationEngine validationEngine, CliContext cliContext, String[] args, TimeTracker tt, TimeTracker.Session tts) throws Exception {
validationService.generateSpreadsheet(cliContext, validationEngine);
}
}

View File

@ -0,0 +1,14 @@
package org.hl7.fhir.validation.cli.tasks;
import org.hl7.fhir.utilities.TimeTracker;
import org.hl7.fhir.validation.ValidationEngine;
import org.hl7.fhir.validation.cli.model.CliContext;
import org.hl7.fhir.validation.cli.services.ValidationService;
public abstract class StandaloneTask extends CliTask{
public abstract void executeTask(CliContext cliContext, String[] args, TimeTracker tt, TimeTracker.Session tts) throws Exception;
}

View File

@ -0,0 +1,56 @@
package org.hl7.fhir.validation.cli.tasks;
import org.hl7.fhir.utilities.TimeTracker;
import org.hl7.fhir.validation.cli.model.CliContext;
import org.hl7.fhir.validation.cli.utils.Display;
import org.hl7.fhir.validation.cli.utils.Params;
import org.hl7.fhir.validation.special.R4R5MapTester;
import org.hl7.fhir.validation.special.TxTester;
import org.hl7.fhir.validation.testexecutor.TestExecutor;
import org.hl7.fhir.validation.testexecutor.TestExecutorParams;
import java.io.File;
import java.io.PrintStream;
public class TestsTask extends StandaloneTask{
@Override
public String getName() {
return "tests";
}
@Override
public String getDisplayName() {
return "Tests";
}
@Override
public boolean isHidden() {
return false;
}
@Override
public boolean shouldExecuteTask(CliContext cliContext, String[] args) {
return Params.hasParam(args, Params.TEST);
}
@Override
public void printHelp(PrintStream out) {
Display.displayHelpDetails(out,"help/tests.txt");
}
@Override
public void executeTask(CliContext cliContext, String[] args, TimeTracker tt, TimeTracker.Session tts) throws Exception {
final String testModuleParam = Params.getParam(args, Params.TEST_MODULES);
final String testClassnameFilter = Params.getParam(args, Params.TEST_NAME_FILTER);
final String testCasesDirectory = Params.getParam(args, Params.TEST);
final String txCacheDirectory = Params.getParam(args, Params.TERMINOLOGY_CACHE);
assert TestExecutorParams.isValidModuleParam(testModuleParam) : "Invalid test module param: " + testModuleParam;
final String[] moduleNamesArg = TestExecutorParams.parseModuleParam(testModuleParam);
assert TestExecutorParams.isValidClassnameFilterParam(testClassnameFilter) : "Invalid regex for test classname filter: " + testClassnameFilter;
new TestExecutor(moduleNamesArg).executeTests(testClassnameFilter, txCacheDirectory, testCasesDirectory);
System.exit(0);
}
}

View File

@ -0,0 +1,44 @@
package org.hl7.fhir.validation.cli.tasks;
import org.hl7.fhir.utilities.TimeTracker;
import org.hl7.fhir.validation.ValidationEngine;
import org.hl7.fhir.validation.cli.model.CliContext;
import org.hl7.fhir.validation.cli.services.ValidationService;
import org.hl7.fhir.validation.cli.utils.Display;
import org.hl7.fhir.validation.cli.utils.EngineMode;
import java.io.PrintStream;
public class TransformTask extends ValidationEngineTask {
@Override
public String getName() {
return "transform";
}
@Override
public String getDisplayName() {
return "Transforms";
}
@Override
public boolean isHidden() {
return false;
}
@Override
public boolean shouldExecuteTask(CliContext cliContext, String[] args) {
return cliContext.getMode() == EngineMode.TRANSFORM;
}
@Override
public void printHelp(PrintStream out) {
Display.displayHelpDetails(out,"help/transform.txt");
}
@Override
public void executeTask(ValidationService validationService, ValidationEngine validationEngine, CliContext cliContext, String[] args, TimeTracker tt, TimeTracker.Session tts) throws Exception {
validationService.transform(cliContext, validationEngine);
}
}

View File

@ -0,0 +1,48 @@
package org.hl7.fhir.validation.cli.tasks;
import org.hl7.fhir.utilities.TimeTracker;
import org.hl7.fhir.validation.cli.model.CliContext;
import org.hl7.fhir.validation.cli.utils.Params;
import org.hl7.fhir.validation.special.TxTester;
import org.hl7.fhir.validation.testexecutor.TestExecutor;
import org.hl7.fhir.validation.testexecutor.TestExecutorParams;
import java.io.PrintStream;
public class TxTestsTask extends StandaloneTask{
@Override
public String getName() {
return "txTests";
}
@Override
public String getDisplayName() {
return "Terminology Tests";
}
@Override
public boolean isHidden() {
return true;
}
@Override
public boolean shouldExecuteTask(CliContext cliContext, String[] args) {
return Params.hasParam(args, Params.TX_TESTS);
}
@Override
public void printHelp(PrintStream out) {
}
@Override
public void executeTask(CliContext cliContext, String[] args, TimeTracker tt, TimeTracker.Session tts) throws Exception {
final String source = Params.getParam(args, Params.SOURCE);
final String output = Params.getParam(args, Params.OUTPUT);
final String version = Params.getParam(args, Params.VERSION);
final String tx = Params.getParam(args, Params.TERMINOLOGY);
final String filter = Params.getParam(args, Params.FILTER);
boolean ok = new TxTester(new TxTester.InternalTxLoader(source, output), tx, false).setOutput(output).execute(version, filter);
System.exit(ok ? 1 : 0);
}
}

View File

@ -0,0 +1,60 @@
package org.hl7.fhir.validation.cli.tasks;
import org.hl7.fhir.r5.model.Constants;
import org.hl7.fhir.r5.model.ImplementationGuide;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.utilities.TimeTracker;
import org.hl7.fhir.validation.ValidationEngine;
import org.hl7.fhir.validation.cli.model.CliContext;
import org.hl7.fhir.validation.cli.services.ValidationService;
import org.hl7.fhir.validation.cli.utils.Display;
import java.io.PrintStream;
public class ValidateTask extends ValidationEngineTask {
final static String[][] PLACEHOLDERS = {
{ "XML_AND_JSON_FHIR_VERSIONS", "1.0, 1.4, 3.0, 4.0, " + Constants.VERSION_MM },
{ "TURTLE_FHIR_VERSIONS", "3.0, 4.0, " + Constants.VERSION_MM },
};
@Override
public String getName() {
return "validate";
}
@Override
public String getDisplayName() {
return "Validation";
}
@Override
public boolean isHidden() {
return false;
}
@Override
public boolean shouldExecuteTask(CliContext cliContext, String[] args) {
// There is no explicit way to trigger a validation task.
// It is the default task.
return false;
}
@Override
public void printHelp(PrintStream out) {
Display.displayHelpDetails(out,"help/validate.txt", PLACEHOLDERS);
}
@Override
public void executeTask(ValidationService validationService, ValidationEngine validationEngine, CliContext cliContext, String[] args, TimeTracker tt, TimeTracker.Session tts) throws Exception {
for (String s : cliContext.getProfiles()) {
if (!validationEngine.getContext().hasResource(StructureDefinition.class, s) && !validationEngine.getContext().hasResource(ImplementationGuide.class, s)) {
System.out.println(" Fetch Profile from " + s);
validationEngine.loadProfile(cliContext.getLocations().getOrDefault(s, s));
}
}
System.out.println("Validating");
validationService.validateSources(cliContext, validationEngine);
}
}

View File

@ -0,0 +1,14 @@
package org.hl7.fhir.validation.cli.tasks;
import org.hl7.fhir.utilities.TimeTracker;
import org.hl7.fhir.validation.ValidationEngine;
import org.hl7.fhir.validation.cli.model.CliContext;
import org.hl7.fhir.validation.cli.services.ValidationService;
public abstract class ValidationEngineTask extends CliTask{
public abstract void executeTask(ValidationService validationService, ValidationEngine validationEngine, CliContext cliContext, String[] args, org.hl7.fhir.utilities.TimeTracker tt, TimeTracker.Session tts) throws Exception;
}

View File

@ -0,0 +1,44 @@
package org.hl7.fhir.validation.cli.tasks;
import org.hl7.fhir.utilities.TimeTracker;
import org.hl7.fhir.validation.ValidationEngine;
import org.hl7.fhir.validation.cli.model.CliContext;
import org.hl7.fhir.validation.cli.services.ValidationService;
import org.hl7.fhir.validation.cli.utils.Display;
import org.hl7.fhir.validation.cli.utils.EngineMode;
import java.io.PrintStream;
public class VersionTask extends ValidationEngineTask {
@Override
public String getName() {
return "to-version";
}
@Override
public String getDisplayName() {
return "Version Conversion";
}
@Override
public boolean isHidden() {
return false;
}
@Override
public boolean shouldExecuteTask(CliContext cliContext, String[] args) {
return cliContext.getMode() == EngineMode.VERSION;
}
@Override
public void printHelp(PrintStream out) {
Display.displayHelpDetails(out,"help/version.txt");
}
@Override
public void executeTask(ValidationService validationService, ValidationEngine validationEngine, CliContext cliContext, String[] args, TimeTracker tt, TimeTracker.Session tts) throws Exception {
validationService.transformVersion(cliContext, validationEngine);
}
}

View File

@ -2,12 +2,11 @@ package org.hl7.fhir.validation.cli.utils;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.PrintStream;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.hl7.fhir.r5.model.Constants; import org.hl7.fhir.r5.model.Constants;
import org.hl7.fhir.utilities.VersionUtilities;
import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager; import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager;
import org.hl7.fhir.utilities.npm.ToolsVersion;
/** /**
* Class for displaying output to the cli user. * Class for displaying output to the cli user.
@ -21,7 +20,7 @@ public class Display {
return Long.toString(maxMemory / (1024 * 1024)); return Long.toString(maxMemory / (1024 * 1024));
} }
public static void printCliArgumentsAndInfo(String[] args) throws IOException { public static void printCliParamsAndInfo(String[] args) throws IOException {
System.out.println(" Paths: Current = " + System.getProperty("user.dir") + ", Package Cache = " + new FilesystemPackageCacheManager(org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager.FilesystemPackageCacheMode.USER).getFolder()); System.out.println(" Paths: Current = " + System.getProperty("user.dir") + ", Package Cache = " + new FilesystemPackageCacheManager(org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager.FilesystemPackageCacheMode.USER).getFolder());
System.out.print(" Params:"); System.out.print(" Params:");
for (String s : args) { for (String s : args) {
@ -37,29 +36,30 @@ public class Display {
return CURLY_START + string + CURLY_END; return CURLY_START + string + CURLY_END;
} }
final static String[][] PLACEHOLDERS = {
{ getMoustacheString("XML_AND_JSON_FHIR_VERSIONS"), "1.0, 1.4, 3.0, 4.0," + Constants.VERSION_MM },
{ getMoustacheString("TURTLE_FHIR_VERSIONS"), "3.0, 4.0, " + Constants.VERSION_MM },
};
final static String replacePlaceholders(final String input, final String[][] placeholders) { final static String replacePlaceholders(final String input, final String[][] placeholders) {
String output = input; String output = input;
for (String[] placeholder : placeholders) { for (String[] placeholder : placeholders) {
output = output.replaceAll(placeholder[0], placeholder[1]); output = output.replaceAll(getMoustacheString(placeholder[0]), placeholder[1]);
} }
return output; return output;
} }
public static void displayHelpDetails(PrintStream out, String file) {
displayHelpDetails(out, file, new String[][]{});
}
/** /**
* Loads the help details from resources/help.txt, and displays them on the command line to the user. * Loads the help details from a file, and displays them on the out stream.
* @param file
*/ */
public static void displayHelpDetails() { public static void displayHelpDetails(PrintStream out, String file, final String[][] placeholders) {
ClassLoader classLoader = Display.class.getClassLoader(); ClassLoader classLoader = Display.class.getClassLoader();
InputStream help = classLoader.getResourceAsStream("help.txt"); InputStream help = classLoader.getResourceAsStream(file);
try { try {
String data = IOUtils.toString(help, "UTF-8"); String data = IOUtils.toString(help, "UTF-8");
System.out.println(replacePlaceholders(data, PLACEHOLDERS)); out.println(replacePlaceholders(data, placeholders));
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -70,8 +70,8 @@ public class Display {
/** /**
* Prints out system info to the command line. * Prints out system info to the command line.
*/ */
public static void displaySystemInfo() { public static void displaySystemInfo(PrintStream out) {
System.out.println(" Java: " + System.getProperty("java.version") out.println(" Java: " + System.getProperty("java.version")
+ " from " + System.getProperty("java.home") + " from " + System.getProperty("java.home")
+ " on " + System.getProperty("os.arch") + " on " + System.getProperty("os.arch")
+ " (" + System.getProperty("sun.arch.data.model") + "bit). " + " (" + System.getProperty("sun.arch.data.model") + "bit). "
@ -81,7 +81,7 @@ public class Display {
/** /**
* Prints current version of the validator. * Prints current version of the validator.
*/ */
public static void displayVersion() { public static void displayVersion(PrintStream out) {
System.out.println("FHIR Validation tool " + VersionUtil.getVersionString()); out.println("FHIR Validation tool " + VersionUtil.getVersionString());
} }
} }

View File

@ -30,6 +30,7 @@ public class TestExecutorParams {
} }
public static boolean isValidClassnameFilterParam(String param) { public static boolean isValidClassnameFilterParam(String param) {
if (param == null) return true;
try { try {
Pattern.compile(param); Pattern.compile(param);
return true; return true;

View File

@ -0,0 +1,13 @@
The validator can compare profiles. To compare profiles, use the following parameters:
java -jar validator_cli.jar -compare -dest /home/user/work/ig-comparison -version 4.0
-ig hl7.fhir.us.carin-bb#1.1.0 -ig hl7.fhir.us.davinci-crd#1.0.0
-left http://hl7.org/fhir/us/carin-bb/StructureDefinition/C4BB-Patient -right http://hl7.org/fhir/us/davinci-crd/StructureDefinition/profile-patient
Parameters:
-compare: tell the validator to run the comparison logic
-dest: folder in which to produce the output. This must exist, and the validator will overwrite existing content if it needs to. The output isn't simple - see below
-version Maj.Min - the version to use. You can leave this out and let the validator infer this, but it's there so that you can compare profiles across versions. E.g. if you specify -version 4.0, the profiles will both be treated as R4 profiles, even if they aren't
-ig - a repeating parameter that specifies the packages to load, that contain the profiles you want to compare
-left and -right - the two profiles to compare. There's no functional difference between left and right, except that the comparison will keep to left and right consistently

View File

@ -0,0 +1,13 @@
You can use the validator to convert a resource or logical model. To do this,
you must provide a specific parameter:
-convert
-convert requires the parameters -source and one of {-output, -outputSuffix}.
-ig may be used to provide a logical model.
If the -source maps to one or more resources, e.g. when using a wildcard,
use -outputSuffix <suffix>, to obtain multiple result files with a
`<sourcefilename>.<suffix>` filename.
Example: `-source *.xml -convert -outputSuffix convert.json` outputs:
`source1.xml.convert.json`, `source2.xml.convert.json`, etc. .

View File

@ -0,0 +1,9 @@
You can use the validator to evaluate a FHIRPath expression on a resource or
logical model. To do this, you must provide a specific parameter:
-fhirpath [FHIRPath]
* [FHIRPath] the FHIRPath expression to evaluate
-fhirpath requires the parameters -source. ig may be used to provide a logical
model

View File

@ -0,0 +1,7 @@
You can use the validator to generate narrative for a resource. To do this, you
must provide a specific parameter:
-narrative
-narrative requires the parameters -defn, -txserver, -source, and -output. ig
and profile may be used

View File

@ -0,0 +1,16 @@
You can use the validator to generate a snapshot for a profile. To do this, you
must provide a specific parameter:
-snapshot
-snapshot requires the parameters -defn, -txserver, -source, and
one of {-output, -outputSuffix}.
-ig may be used to provide necessary base profiles.
The -output/-outputSuffic filetype (xml, json) may imply a conversion.
If the -source maps to one or more profiles, e.g. when using a wildcard,
use -outputSuffix <suffix>, to obtain multiple result files with a
`<sourcefilename>.<suffix>` filename.
Example: `-source *.xml -snapshot -outputSuffix snapshot.json` outputs:
`source1.xml.snapshot.json`, `source2.xml.snapshot.json`, etc. .

View File

@ -0,0 +1,24 @@
The validator can be run in test mode, which executes all JUnit tests for the
FHIR core library against a set of test cases in a local directory. To do this,
you must provide a specific parameter with a directory value:
-test [directory]
The directory must follow the same conventions as the reference test cases found
at https://github.com/FHIR/fhir-test-cases. This mode must also be executed with
the Java property java.locale.providers set to COMPAT as below:
java -Djava.locale.providers=COMPAT -jar validator_cli.jar -tests
./my/path/to/fhir-test-cases
This parameter is compatible with -txCache, -test-modules, and
-test-classname-filter parameters.
The following test-specific parameters can be used to limit which tests are run:
-test-modules [module-names] A comma delimited list of Java module names for
which to run JUnit tests. By default, all modules are used to run tests.
Example: -test-modules org.hl7.fhir.dstu2,org.hl7.fhir.dstu2016may
-test-classname-filter [regex] A regex filter applied to test Java class names
for selecting which JUnit tests to run. By default, all tests are run.
Example: -test-classname-filter .*ShexGeneratorTests

View File

@ -0,0 +1,11 @@
You can use the validator to execute a transformation as described by a
structure map. To do this, you must provide some additional parameters:
-transform [map]
* [map] the URI of the map that the transform starts with
Any other dependency maps have to be loaded through an -ig reference
-transform uses the parameters -defn, -txserver, -ig (at least one with the map
files), and -output

View File

@ -0,0 +1,114 @@
Usage: java -jar [validator].jar (parameters)
The FHIR validation tool validates a FHIR resource or bundle.
The validation tool compares a resource against the base definitions and any
profiles declared in the resource (Resource.meta.profile) or specified on the
command line
The FHIR validation tool validates a FHIR resource or bundle. Schema and
schematron checking is performed, then some additional checks are performed.
* XML & Json (FHIR versions {{XML_AND_JSON_FHIR_VERSIONS}})
* Turtle (FHIR versions {{TURTLE_FHIR_VERSIONS}})
If requested, instances will also be verified against the appropriate schema W3C
XML Schema, JSON schema or ShEx, as appropriate
The following parameters are supported:
[source]: a file, url, directory or pattern for resources to validate.
At least one source must be declared. If there is more than one source or
if the source is other than a single file or url and the output parameter is
used, results will be provided as a Bundle.
Patterns are limited to a directory followed by a filename with an
embedded asterisk. E.g. foo*-examples.xml or someresource.*, etc.
-version [ver]: The FHIR version to use.
This can only appear once.
valid values {{FHIR_MAJOR_VERSIONS}} or {{FHIR_MINOR_VERSIONS}}
Default value is {{FHIR_CURRENT_VERSION}}
-ig [package|file|folder|url]: an IG or profile definition to load.
Can be the URL of an implementation guide or a package ([id]-[ver]) for a
built implementation guide or a local folder that contains a set of
conformance resources.
If you would like to load the latest unreleased version of the
implementation guide or package, please define the version as '#current'.
If no version is provided, the latest version in the package cache will
be used, or if no such cached package is available, the PackageCacheManager
will load the latest from the the online package repo.
If you want the implementation guide to be loaded for a specific version
of FHIR, you can prefix the IG with the appropriate version in square
brackets ([[fhirVer]][id]-[igVer]).
No default value. This parameter can appear any number of times
-tx [url]: the [base] url of a FHIR terminology service
Default value is http://tx.fhir.org. This parameter can appear once
To run without terminology value, specific n/a as the URL
-txLog [file]: Produce a log of the terminology server operations in [file]
Default value is not to produce a log
-profile [url]: the canonical URL to validate against (same as if it was
specified in Resource.meta.profile).
If no profile is specified, the resource is validated against the base
specification. This parameter can appear any number of times.
Note: the profile (and it's dependencies) have to be made available
through one of the -ig parameters. Note that package dependencies will
automatically be resolved
-showReferenceMessages
Includes validation messages resulting from validating target resources
against profiles defined on a reference. This increases the volume of
validation messages, but may allow easier debugging. If not specified,
then only a high-level message indicating that the referenced item wasn't
valid against the listed profile(s) will be provided.
-questionnaire mode: what to do when validating QuestionnaireResponse resources
* none (default): just ignore the questionnaire reference
* required: check that the QuestionnaireResponse has a questionnaire and
validate against it
* check: if the QuestionnaireResponse has a questionnaire, validate
against it
The questionnaire must be loaded using the -ig parameter
which specifies the location of a questionnaire. If provided, then the
validator will validate any QuestionnaireResponse that claims to match the
Questionnaire against it no default value. This parameter can appear any
number of times
-output [file]: a filename for the results (OperationOutcome)
Default: results are sent to the std out.
-outputSuffix [string]: used in -convert and -snapshot to deal with
one or more result files (where -output can only have one)
-debug
Produce additional information about the loading/validation process
-recurse
Look in subfolders when -ig refers to a folder
-locale
Specifies the locale/language of the validation result messages (eg.:
de-DE
-sct
Specify the edition of SNOMED CT to use. Valid Choices:
intl | us | uk | au | nl | ca | se | dk | es
tx.fhir.org only supports a subset. To add to this list or tx.fhir.org ask
on https://chat.fhir.org/#narrow/stream/179202-terminology
-native: use schema for validation as well
* XML: w3c schema+schematron
* JSON: json.schema
* RDF: SHEX
Default: false
-language: [lang] The language to use when validating coding displays - same
value as for xml:lang
Not used if the resource specifies language
Default: no specified language
-extension: Controls how extensions are validated by the validator. The value
for this parameter is a URL for a domain from which extensions will be
allowed. By default, unknown extensions are prohibited, but can be allowed
by using the value 'any' (e.g. -extension any). This parameter can repeat
any number of times.
-hintAboutNonMustSupport: If present, raise hints if the instance contains data
elements that are not marked as mustSupport=true. Useful to identify
elements included that may be ignored by recipients
-assumeValidRestReferences: If present, assume that URLs that reference
resources follow the RESTful URI pattern and it is safe to infer the type
from the URL
-security-checks: If present, check that string content doesn't include any html
-like tags that might create problems downstream (though all external input
must always be santized by escaping for either html or sql)
The validator also supports the param -proxy=[address]:[port] for if you use a
proxy
Parameters can appear in any order

View File

@ -0,0 +1,12 @@
The validator can convert between versions of FHIR (r2, r3, and r4). To use the validator to convert versions, provide the following parameters:
java -jar validator_cli.jar c:\temp\observation.xml -version 3.0 -to-version 4.0 -output c:\temp\observation4.json
The key parameter is "-to-version" which causes the validator to invoke the version conversion routine.
Technical notes:
the validator can use either it's own internal routines, or the structure maps found at https://github.com/FHIR/packages/tree/master/interversion.
By default, the internal routines will be used for resources with a canonical URL (e.g. code system etc) and the structure maps will be used otherwise
If the internal routines fail, the structure maps will be used anyway
you can use the parameter -do-native to get the validator to try the internal routines first for any resource, and the parameter -no-native to tell it not to try them at all
Issues with the structure maps should be discussed on the chat.fhir.org implementers channel, or submitted as PRs against the github repo above

View File

@ -0,0 +1,302 @@
package org.hl7.fhir.validation;
import org.hl7.fhir.utilities.TimeTracker;
import org.hl7.fhir.validation.cli.model.CliContext;
import org.hl7.fhir.validation.cli.services.ValidationService;
import org.hl7.fhir.validation.cli.tasks.*;
import org.hl7.fhir.validation.cli.utils.Params;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
public class ValidatorCliTests {
@Mock
ValidationService validationService;
@Mock
ValidationEngine validationEngine;
@Spy
ConvertTask convertTask;
@Spy
CompareTask compareTask;
@Spy
CompileTask compileTask;
@Spy
FhirpathTask fhirpathTask;
@Spy
InstallTask installTask;
@Spy
LangTransformTask langTransformTask;
@Spy
NarrativeTask narrativeTask;
@Spy
SnapshotTask snapshotTask;
@Spy
SpreadsheetTask spreadsheetTask;
@Spy
TestsTask testsTask = new TestsTask() {
@Override
public void executeTask(CliContext cliContext, String[] args, TimeTracker tt, TimeTracker.Session tts) {}
};
@Spy
TxTestsTask txTestsTask = new TxTestsTask() {
@Override
public void executeTask(CliContext cliContext, String[] args, TimeTracker tt, TimeTracker.Session tts) {}
};
@Spy
TransformTask transformTask;
@Spy
VersionTask versionTask;
@Spy
ValidateTask validateTask;
@Spy
ScanTask scanTask = new ScanTask() {
@Override
public void executeTask(ValidationService validationService, ValidationEngine validationEngine, CliContext cliContext, String[] args, TimeTracker tt, TimeTracker.Session tts) {}
};
@Spy
SpecialTask specialTask = new SpecialTask() {
@Override
public void executeTask(CliContext cliContext, String[] args, TimeTracker tt, TimeTracker.Session tts) {}
};
public ValidatorCli mockValidatorCli() {
ValidatorCli validatorCli = spy(new ValidatorCli(validationService){
protected void validateScan(CliContext cliContext, ValidationEngine validator) {
// DO NOTHING;
}
protected List<CliTask> getCliTasks() {
return List.of(
compareTask,
compileTask,
convertTask,
fhirpathTask,
installTask,
langTransformTask,
narrativeTask,
scanTask,
snapshotTask,
specialTask,
spreadsheetTask,
testsTask,
txTestsTask,
transformTask,
versionTask,
//validate is the default
validateTask
);
}
});
return validatorCli;
}
public ValidatorCli mockValidatorCliWithService(CliContext cliContext) throws Exception {
when(validationService.determineVersion(Mockito.same(cliContext))).thenReturn("5.0.1");
when(validationService.initializeValidator(Mockito.same(cliContext), anyString(), any(org.hl7.fhir.utilities.TimeTracker.class))).thenReturn(validationEngine);
return mockValidatorCli();
}
@Test
public void testCorrectTasksInValidatorCli() {
ValidatorCli realCli = new ValidatorCli(mock(ValidationService.class));
ValidatorCli mockCli = mockValidatorCli();
List<CliTask> realTasks = realCli.getCliTasks();
List<CliTask> mockTasks = mockCli.getCliTasks();
assertEquals(mockTasks.size(), realTasks.size());
for (int i = 0; i < realTasks.size(); i++) {
assertEquals(mockTasks.get(i).getName(), realTasks.get(i).getName(), "Differing task at index " + i);
}
}
private void assertContainsValidationTask(List<CliTask> tasks, String name) {
for (CliTask task : tasks) {
if (task.getName().equals(name)) {
return;
}
}
fail("Cannot find " + name + " in task list");
}
@Test
public void transformTest() throws Exception {
final String[] args = new String[]{"-transform", "dummyFile.map", "dummySource.json"};
CliContext cliContext = Params.loadCliContext(args);
ValidatorCli cli = mockValidatorCliWithService(cliContext);
cli.readParamsAndExecuteTask(cliContext, args);
Mockito.verify(validationService).determineVersion(same(cliContext));
Mockito.verify(validationService).transform(same(cliContext), same(validationEngine));
}
@Test
public void narrativeTest() throws Exception {
final String[] args = new String[]{"-narrative"};
CliContext cliContext = Params.loadCliContext(args);
ValidatorCli cli = mockValidatorCliWithService(cliContext);
cli.readParamsAndExecuteTask(cliContext, args);
Mockito.verify(validationService).determineVersion(same(cliContext));
Mockito.verify(validationService).generateNarrative(same(cliContext), same(validationEngine));
}
@Test
public void compileTest() throws Exception {
final String[] args = new String[]{"-compile", "dummyMap.map"};
CliContext cliContext = Params.loadCliContext(args);
ValidatorCli cli = mockValidatorCliWithService(cliContext);
cli.readParamsAndExecuteTask(cliContext, args);
Mockito.verify(validationService).determineVersion(same(cliContext));
Mockito.verify(validationService).compile(same(cliContext), same(validationEngine));
}
@Test
public void convertTest() throws Exception {
final String[] args = new String[]{"-convert"};
CliContext cliContext = Params.loadCliContext(args);
ValidatorCli cli = mockValidatorCliWithService(cliContext);
cli.readParamsAndExecuteTask(cliContext, args);
Mockito.verify(validationService).determineVersion(same(cliContext));
Mockito.verify(validationService).convertSources(cliContext,validationEngine);
}
@Test
public void snapshotTest() throws Exception {
final String[] args = new String[]{"-snapshot"};
CliContext cliContext = Params.loadCliContext(args);
ValidatorCli cli = mockValidatorCliWithService(cliContext);
cli.readParamsAndExecuteTask(cliContext, args);
Mockito.verify(validationService).determineVersion(same(cliContext));
Mockito.verify(validationService).generateSnapshot(same(cliContext), same(validationEngine));
}
@Test
public void installTest() throws Exception {
final String[] args = new String[]{"-install"};
CliContext cliContext = Params.loadCliContext(args);
ValidatorCli cli = mockValidatorCliWithService(cliContext);
cli.readParamsAndExecuteTask(cliContext, args);
Mockito.verify(validationService).determineVersion(same(cliContext));
Mockito.verify(validationService).install(same(cliContext), same(validationEngine));
}
@Test
public void spreadsheetTest() throws Exception {
final String[] args = new String[]{"-spreadsheet"};
CliContext cliContext = Params.loadCliContext(args);
ValidatorCli cli = mockValidatorCliWithService(cliContext);
cli.readParamsAndExecuteTask(cliContext, args);
Mockito.verify(validationService).determineVersion(same(cliContext));
Mockito.verify(validationService).generateSpreadsheet(same(cliContext), same(validationEngine));
}
@Test
public void fhirpathTest() throws Exception {
final String[] args = new String[]{"-fhirpath", "dummyExpression"};
CliContext cliContext = Params.loadCliContext(args);
ValidatorCli cli = mockValidatorCliWithService(cliContext);
cli.readParamsAndExecuteTask(cliContext, args);
Mockito.verify(validationService).determineVersion(same(cliContext));
Mockito.verify(validationService).evaluateFhirpath(same(cliContext), same(validationEngine));
}
@Test
public void versionTest() throws Exception {
final String[] args = new String[]{"-to-version", "1.2.3"};
CliContext cliContext = Params.loadCliContext(args);
ValidatorCli cli = mockValidatorCliWithService(cliContext);
cli.readParamsAndExecuteTask(cliContext, args);
Mockito.verify(validationService).determineVersion(same(cliContext));
Mockito.verify(validationService).transformVersion(same(cliContext), same(validationEngine));
}
@Test
public void langTransformTest() throws Exception {
final String[] args = new String[]{"-lang-transform", "dummyLang"};
CliContext cliContext = Params.loadCliContext(args);
ValidatorCli cli = mockValidatorCliWithService(cliContext);
cli.readParamsAndExecuteTask(cliContext, args);
Mockito.verify(validationService).determineVersion(same(cliContext));
Mockito.verify(validationService).transformLang(same(cliContext), same(validationEngine));
}
@Test
public void defaultTest() throws Exception {
final String[] args = new String[]{"dummyFile.json"};
CliContext cliContext = Params.loadCliContext(args);
ValidatorCli cli = mockValidatorCliWithService(cliContext);
cli.readParamsAndExecuteTask(cliContext, args);
Mockito.verify(validationService).determineVersion(same(cliContext));
Mockito.verify(validationService).validateSources(same(cliContext), same(validationEngine));
}
@Test
public void scanTest() throws Exception {
final String[] args = new String[]{"-scan"};
CliContext cliContext = Params.loadCliContext(args);
ValidatorCli cli = mockValidatorCliWithService(cliContext);
cli.readParamsAndExecuteTask(cliContext, args);
Mockito.verify(validationService).determineVersion(same(cliContext));
Mockito.verify(scanTask).executeTask(same(validationService), same(validationEngine), same(cliContext), eq(args), any(TimeTracker.class), any(TimeTracker.Session.class));
}
@Test
public void specialTest() throws Exception {
final String[] args = new String[]{"-special"};
CliContext cliContext = Params.loadCliContext(args);
ValidatorCli cli = mockValidatorCli();
cli.readParamsAndExecuteTask(cliContext, args);
Mockito.verify(specialTask).executeTask(same(cliContext), eq(args), any(TimeTracker.class), any(TimeTracker.Session.class));
}
@Test
public void compareTest() throws Exception {
final String[] args = new String[]{"-compare"};
CliContext cliContext = Params.loadCliContext(args);
ValidatorCli cli = mockValidatorCliWithService(cliContext);
cli.readParamsAndExecuteTask(cliContext, args);
Mockito.verify(validationService).determineVersion(same(cliContext));
Mockito.verify(compareTask).executeTask(same(validationService), same(validationEngine), same(cliContext), eq(args), any(TimeTracker.class), any(TimeTracker.Session.class));
}
@Test
public void txTestsTest() throws Exception {
final String[] args = new String[]{"-txTests"};
CliContext cliContext = Params.loadCliContext(args);
ValidatorCli cli = mockValidatorCli();
cli.readParamsAndExecuteTask(cliContext, args);
Mockito.verify(txTestsTask).executeTask(same(cliContext), eq(args), any(TimeTracker.class), any(TimeTracker.Session.class));
}
@Test
public void testsTest() throws Exception {
final String[] args = new String[]{"-tests"};
CliContext cliContext = Params.loadCliContext(args);
ValidatorCli cli = mockValidatorCli();
cli.readParamsAndExecuteTask(cliContext, args);
Mockito.verify(testsTask).executeTask(same(cliContext), eq(args), any(TimeTracker.class), any(TimeTracker.Session.class));
}
}

View File

@ -1,5 +1,7 @@
package org.hl7.fhir.validation.cli.utils; package org.hl7.fhir.validation.cli.utils;
import org.hl7.fhir.r5.model.Constants;
import org.hl7.fhir.validation.cli.tasks.ValidateTask;
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -10,32 +12,29 @@ import static org.junit.jupiter.api.Assertions.*;
public class DisplayTests { public class DisplayTests {
final static String[][] PLACEHOLDERS = {
{ "XML_AND_JSON_FHIR_VERSIONS", "1.0, 1.4, 3.0, 4.0, " + Constants.VERSION_MM },
{ "TURTLE_FHIR_VERSIONS", "3.0, 4.0, " + Constants.VERSION_MM },
};
@Test @Test
@DisplayName("Check for placeholder replacement in help output") @DisplayName("Check for placeholder replacement in help output")
public void displayHelpDetails() { public void displayHelpDetails() {
final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
final ByteArrayOutputStream errContent = new ByteArrayOutputStream(); final ByteArrayOutputStream errContent = new ByteArrayOutputStream();
final PrintStream originalOut = System.out; PrintStream out = new PrintStream(outContent);
final PrintStream originalErr = System.err; PrintStream err = new PrintStream(errContent);
System.setOut(new PrintStream(outContent)); Display.displayHelpDetails(out, "help/validate.txt", PLACEHOLDERS);
System.setErr(new PrintStream(errContent));
try {
Display.displayHelpDetails();
String output = outContent.toString(); String output = outContent.toString();
for (String[] placeHolder: Display.PLACEHOLDERS) { for (String[] placeHolder: PLACEHOLDERS) {
assertTrue(output.contains(placeHolder[1]), placeHolder[1] + " is not contained in output:\n" + output); assertTrue(output.contains(placeHolder[1]), placeHolder[1] + " is not contained in output:\n" + output);
assertFalse(output.contains(placeHolder[0]), placeHolder[0] + " found in output:\n" + output); assertFalse(output.contains(placeHolder[0]), placeHolder[0] + " found in output:\n" + output);
} }
}
finally {
System.setOut(originalOut);
System.setErr(originalErr);
}
} }
@Test @Test
@ -44,8 +43,8 @@ public class DisplayTests {
final String myTestString = "The {{DUMMY_A}} jumps over the {{DUMMY_B}}."; final String myTestString = "The {{DUMMY_A}} jumps over the {{DUMMY_B}}.";
final String[][] placeHolders = { final String[][] placeHolders = {
{ "\\{\\{DUMMY_A\\}\\}", "quick brown fox"}, { "DUMMY_A", "quick brown fox"},
{ "\\{\\{DUMMY_B\\}\\}", "lazy dog"}, { "DUMMY_B", "lazy dog"},
}; };
final String expectedOutput = "The quick brown fox jumps over the lazy dog."; final String expectedOutput = "The quick brown fox jumps over the lazy dog.";