diff --git a/hapi-fhir-cli/pom.xml b/hapi-fhir-cli/pom.xml index e9006179fb1..c823c82bbe5 100644 --- a/hapi-fhir-cli/pom.xml +++ b/hapi-fhir-cli/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir - 1.2-SNAPSHOT + 1.2 ../pom.xml @@ -22,33 +22,33 @@ ca.uhn.hapi.fhir hapi-fhir-base - 1.2-SNAPSHOT + 1.2 ca.uhn.hapi.fhir hapi-fhir-jpaserver-example - 1.2-SNAPSHOT + 1.2 war ca.uhn.hapi.fhir hapi-fhir-structures-dstu - 1.2-SNAPSHOT + 1.2 ca.uhn.hapi.fhir hapi-fhir-structures-dstu2 - 1.2-SNAPSHOT + 1.2 ca.uhn.hapi.fhir hapi-fhir-structures-hl7org-dstu2 - 1.2-SNAPSHOT + 1.2 ca.uhn.hapi.fhir - hapi-fhir-validation-resources - 1.2-SNAPSHOT + hapi-fhir-validation-resources-dstu2 + 1.2 @@ -143,7 +143,7 @@ ca.uhn.hapi.fhir hapi-fhir-jpaserver-example - 1.2-SNAPSHOT + 1.2 war true target/classes diff --git a/hapi-fhir-cli/src/main/java/ca/uhn/fhir/cli/App.java b/hapi-fhir-cli/src/main/java/ca/uhn/fhir/cli/App.java index d215b269445..a514fe3e854 100644 --- a/hapi-fhir-cli/src/main/java/ca/uhn/fhir/cli/App.java +++ b/hapi-fhir-cli/src/main/java/ca/uhn/fhir/cli/App.java @@ -28,10 +28,13 @@ public class App { private static List ourCommands; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(App.class); + public static final String LINESEP = System.getProperty("line.separator"); + static { ourCommands = new ArrayList(); ourCommands.add(new RunServerCommand()); ourCommands.add(new ExampleDataUploader()); + ourCommands.add(new ValidateCommand()); Collections.sort(ourCommands); } @@ -79,16 +82,22 @@ public class App { System.out.println(" hapi-fhir-cli {command} [options]"); System.out.println(); System.out.println("Commands:"); + + int longestCommandLength = 0; for (BaseCommand next : ourCommands) { - String left = " " + next.getCommandName() + " - "; - String[] rightParts = WordUtils.wrap(next.getCommandDescription(), 80 - left.length()).split("\\n"); + longestCommandLength = Math.max(longestCommandLength, next.getCommandName().length()); + } + + for (BaseCommand next : ourCommands) { + String left = " " + StringUtils.rightPad(next.getCommandName(), longestCommandLength); + String[] rightParts = WordUtils.wrap(next.getCommandDescription(), 80 - (left.length() + 3)).split("\\n"); for (int i = 1; i < rightParts.length; i++) { - rightParts[i] = StringUtils.leftPad("", left.length()) + rightParts[i]; + rightParts[i] = StringUtils.leftPad("", left.length() + 3) + rightParts[i]; } - System.out.println(left + StringUtils.join(rightParts, '\n')); + System.out.println(ansi().bold().fg(Color.GREEN) + left + ansi().boldOff().fg(Color.WHITE) + " - " + ansi().bold() + StringUtils.join(rightParts, LINESEP)); } System.out.println(); - System.out.println("See what options are available:"); + System.out.println(ansi().boldOff().fg(Color.WHITE) + "See what options are available:"); System.out.println(" hapi-fhir-cli help {command}"); System.out.println(); } @@ -143,12 +152,14 @@ public class App { Options options = command.getOptions(); DefaultParser parser = new DefaultParser(); CommandLine parsedOptions; + + logAppHeader(); + loggingConfigOn(); + try { String[] args = Arrays.asList(theArgs).subList(1, theArgs.length).toArray(new String[theArgs.length - 1]); parsedOptions = parser.parse(options, args, true); - logAppHeader(); - loggingConfigOn(); // Actually execute the command command.run(parsedOptions); diff --git a/hapi-fhir-cli/src/main/java/ca/uhn/fhir/cli/BaseCommand.java b/hapi-fhir-cli/src/main/java/ca/uhn/fhir/cli/BaseCommand.java index b110b4085a2..0ed9f849a9b 100644 --- a/hapi-fhir-cli/src/main/java/ca/uhn/fhir/cli/BaseCommand.java +++ b/hapi-fhir-cli/src/main/java/ca/uhn/fhir/cli/BaseCommand.java @@ -9,6 +9,8 @@ import ca.uhn.fhir.rest.client.IGenericClient; public abstract class BaseCommand implements Comparable { + private FhirContext myFhirCtx; + public BaseCommand() { super(); } @@ -32,4 +34,11 @@ public abstract class BaseCommand implements Comparable { public abstract void run(CommandLine theCommandLine) throws ParseException, Exception; + public FhirContext getFhirCtx() { + if (myFhirCtx == null) { + myFhirCtx = FhirContext.forDstu2(); + } + return myFhirCtx; + } + } \ No newline at end of file diff --git a/hapi-fhir-cli/src/main/java/ca/uhn/fhir/cli/ValidateCommand.java b/hapi-fhir-cli/src/main/java/ca/uhn/fhir/cli/ValidateCommand.java new file mode 100644 index 00000000000..7bc49ca11d4 --- /dev/null +++ b/hapi-fhir-cli/src/main/java/ca/uhn/fhir/cli/ValidateCommand.java @@ -0,0 +1,116 @@ +package ca.uhn.fhir.cli; + +import static org.apache.commons.lang3.StringUtils.defaultString; +import static org.apache.commons.lang3.StringUtils.isBlank; +import static org.apache.commons.lang3.StringUtils.isNotBlank; + +import static org.fusesource.jansi.Ansi.*; + +import java.io.FileInputStream; +import java.io.InputStreamReader; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.OptionGroup; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; +import org.apache.commons.io.IOUtils; +import org.fusesource.jansi.Ansi; + +import com.phloc.commons.io.file.FileUtils; + +import ca.uhn.fhir.rest.method.MethodUtil; +import ca.uhn.fhir.rest.server.EncodingEnum; +import ca.uhn.fhir.validation.FhirInstanceValidator; +import ca.uhn.fhir.validation.FhirValidator; +import ca.uhn.fhir.validation.SingleValidationMessage; +import ca.uhn.fhir.validation.ValidationResult; + +public class ValidateCommand extends BaseCommand { + + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValidateCommand.class); + + @Override + public String getCommandDescription() { + return "Validate a resource using the FHIR validation tools"; + } + + @Override + public String getCommandName() { + return "validate"; + } + + @Override + public Options getOptions() { + Options retVal = new Options(); + + OptionGroup source = new OptionGroup(); + source.addOption(new Option("f", "file", true, "The name of the file to validate")); + source.addOption(new Option("d", "data", true, "The text to validate")); + retVal.addOptionGroup(source); + + retVal.addOption("x", "xsd", false, "Validate using Schemas"); + retVal.addOption("s", "sch", false, "Validate using Schematrons"); + retVal.addOption("p", "profile", false, "Validate using Profiles (StructureDefinition / ValueSet)"); + + retVal.addOption("e", "encoding", false, "File encoding (default is UTF-8)"); + + return retVal; + } + + @Override + public void run(CommandLine theCommandLine) throws ParseException, Exception { + String fileName = theCommandLine.getOptionValue("f"); + String contents = theCommandLine.getOptionValue("c"); + if (isNotBlank(fileName) && isNotBlank(contents)) { + throw new ParseException("Can not supply both a file (-f) and data (-d)"); + } + if (isBlank(fileName) && isBlank(contents)) { + throw new ParseException("Must supply either a file (-f) or data (-d)"); + } + + if (isNotBlank(fileName)) { + String encoding = theCommandLine.getOptionValue("e", "UTF-8"); + ourLog.info("Reading file '{}' using encoding {}", fileName, encoding); + + contents = IOUtils.toString(new InputStreamReader(new FileInputStream(fileName), encoding)); + ourLog.info("Fully read - Size is {}", FileUtils.getFileSizeDisplay(contents.length())); + } + + EncodingEnum enc = MethodUtil.detectEncodingNoDefault(defaultString(contents)); + if (enc == null) { + throw new ParseException("Could not detect encoding (json/xml) of contents"); + } + + FhirValidator val = getFhirCtx().newValidator(); + if (theCommandLine.hasOption("p")) { + val.registerValidatorModule(new FhirInstanceValidator()); + } + + val.setValidateAgainstStandardSchema(theCommandLine.hasOption("x")); + val.setValidateAgainstStandardSchematron(theCommandLine.hasOption("s")); + + ValidationResult results = val.validateWithResult(contents); + + StringBuilder b = new StringBuilder("Validation results:" + ansi().boldOff()); + int count = 0; + for (SingleValidationMessage next : results.getMessages()) { + count++; + b.append(App.LINESEP); + b.append("Issue ").append(count).append(": "); + b.append(next.getSeverity()).append(" - ").append(next.getLocationString()); + b.append(App.LINESEP); + b.append(" ").append(next.getMessage()); + } + + if (count > 0) { + ourLog.info(b.toString()); + } + + if (results.isSuccessful()) { + ourLog.info("Validation successful!"); + } else { + ourLog.warn("Validation FAILED"); + } + } +} diff --git a/hapi-fhir-dist/src/assembly/hapi-fhir-android-distribution.xml b/hapi-fhir-dist/src/assembly/hapi-fhir-android-distribution.xml index 3253067d6c3..fb45ba4e4a7 100644 --- a/hapi-fhir-dist/src/assembly/hapi-fhir-android-distribution.xml +++ b/hapi-fhir-dist/src/assembly/hapi-fhir-android-distribution.xml @@ -5,7 +5,7 @@ zip - + tar.bz2 false diff --git a/hapi-fhir-dist/src/assembly/hapi-fhir-jpaserver-example.xml b/hapi-fhir-dist/src/assembly/hapi-fhir-jpaserver-example.xml index 6268d658fad..aac691b3fd3 100644 --- a/hapi-fhir-dist/src/assembly/hapi-fhir-jpaserver-example.xml +++ b/hapi-fhir-dist/src/assembly/hapi-fhir-jpaserver-example.xml @@ -5,7 +5,7 @@ zip - + tar.bz2 false diff --git a/hapi-fhir-dist/src/assembly/hapi-fhir-standard-distribution.xml b/hapi-fhir-dist/src/assembly/hapi-fhir-standard-distribution.xml index 62148b76655..65b4836e7b5 100644 --- a/hapi-fhir-dist/src/assembly/hapi-fhir-standard-distribution.xml +++ b/hapi-fhir-dist/src/assembly/hapi-fhir-standard-distribution.xml @@ -1,11 +1,11 @@ - hapi-fhir-standard-distribution + standard-distribution zip - + tar.bz2 false diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaValidationSupportDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaValidationSupportDstu2.java index 33f6765ff14..7bd6d564c67 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaValidationSupportDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaValidationSupportDstu2.java @@ -1,5 +1,25 @@ package ca.uhn.fhir.jpa.dao; +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2015 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + import org.hl7.fhir.instance.model.ValueSet; import org.hl7.fhir.instance.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.instance.model.ValueSet.ValueSetExpansionComponent; diff --git a/pom.xml b/pom.xml index 90bc5573ee4..6fae60ba666 100644 --- a/pom.xml +++ b/pom.xml @@ -1287,6 +1287,7 @@ hapi-fhir-testpage-overlay hapi-fhir-jpaserver-uhnfhirtest hapi-fhir-android + hapi-fhir-cli hapi-fhir-dist examples