Work on CLI tool
This commit is contained in:
parent
780fc871cb
commit
d0bac3d419
|
@ -9,6 +9,8 @@ import javax.servlet.ServletException;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.commons.io.filefilter.WildcardFileFilter;
|
import org.apache.commons.io.filefilter.WildcardFileFilter;
|
||||||
import org.hl7.fhir.instance.model.ValueSet;
|
import org.hl7.fhir.instance.model.ValueSet;
|
||||||
|
import org.hl7.fhir.instance.model.ValueSet.ConceptSetComponent;
|
||||||
|
import org.hl7.fhir.instance.model.ValueSet.ValueSetExpansionComponent;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
@ -21,6 +23,8 @@ import ca.uhn.fhir.parser.IParser;
|
||||||
import ca.uhn.fhir.parser.StrictErrorHandler;
|
import ca.uhn.fhir.parser.StrictErrorHandler;
|
||||||
import ca.uhn.fhir.rest.client.IGenericClient;
|
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
|
import ca.uhn.fhir.validation.DefaultProfileValidationSupport;
|
||||||
|
import ca.uhn.fhir.validation.ValidationSupportChain;
|
||||||
import ca.uhn.fhir.validation.FhirInstanceValidator;
|
import ca.uhn.fhir.validation.FhirInstanceValidator;
|
||||||
import ca.uhn.fhir.validation.FhirValidator;
|
import ca.uhn.fhir.validation.FhirValidator;
|
||||||
import ca.uhn.fhir.validation.IValidationSupport;
|
import ca.uhn.fhir.validation.IValidationSupport;
|
||||||
|
@ -172,7 +176,7 @@ public class ValidatorExamples {
|
||||||
IValidationSupport valSupport = new IValidationSupport() {
|
IValidationSupport valSupport = new IValidationSupport() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public org.hl7.fhir.instance.utils.IWorkerContext.ValidationResult validateCode(String theCodeSystem, String theCode, String theDisplay) {
|
public CodeValidationResult validateCode(String theCodeSystem, String theCode, String theDisplay) {
|
||||||
// TODO: Implement
|
// TODO: Implement
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -194,8 +198,23 @@ public class ValidatorExamples {
|
||||||
// TODO: Implement
|
// TODO: Implement
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ValueSetExpansionComponent expandValueSet(ConceptSetComponent theInclude) {
|
||||||
|
// TODO: Implement
|
||||||
|
return null;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
instanceValidator.setValidationSupport(valSupport);
|
|
||||||
|
/*
|
||||||
|
* ValidationSupportChain strings multiple instances of IValidationSupport together. The
|
||||||
|
* code below is useful because it means that when the validator wants to load a
|
||||||
|
* StructureDefinition or a ValueSet, it will first use DefaultProfileValidationSupport,
|
||||||
|
* which loads the default HL7 versions. Any StructureDefinitions which are not found in
|
||||||
|
* the built-in set are delegated to your custom implementation.
|
||||||
|
*/
|
||||||
|
ValidationSupportChain support = new ValidationSupportChain(new DefaultProfileValidationSupport(), valSupport);
|
||||||
|
instanceValidator.setValidationSupport(support);
|
||||||
|
|
||||||
// END SNIPPET: instanceValidatorCustom
|
// END SNIPPET: instanceValidatorCustom
|
||||||
}
|
}
|
||||||
|
|
|
@ -1101,7 +1101,9 @@ public class JsonParser extends BaseParser implements IParser {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// _id is incorrect, but some early examples in the FHIR spec used it
|
// _id is incorrect, but some early examples in the FHIR spec used it
|
||||||
elementId = theObject.getString(nextName);
|
if (theObject.get(nextName).getValueType() == ValueType.STRING) {
|
||||||
|
elementId = theObject.getString(nextName);
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
} else if ("extension".equals(nextName)) {
|
} else if ("extension".equals(nextName)) {
|
||||||
JsonArray array = theObject.getJsonArray(nextName);
|
JsonArray array = theObject.getJsonArray(nextName);
|
||||||
|
|
|
@ -1076,6 +1076,8 @@ class ParserState<T> {
|
||||||
} else if ("fullUrl".equals(theLocalPart)) {
|
} else if ("fullUrl".equals(theLocalPart)) {
|
||||||
myFullUrl = new IdDt();
|
myFullUrl = new IdDt();
|
||||||
push(new PrimitiveState(getPreResourceState(), myFullUrl));
|
push(new PrimitiveState(getPreResourceState(), myFullUrl));
|
||||||
|
} else if ("fhir_comments".equals(theLocalPart) && myJsonMode) {
|
||||||
|
push(new SwallowChildrenWholeState(getPreResourceState()));
|
||||||
} else {
|
} else {
|
||||||
throw new DataFormatException("Unexpected element in entry: " + theLocalPart);
|
throw new DataFormatException("Unexpected element in entry: " + theLocalPart);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<!-- Note: HAPI projects use the "hapi-fhir" POM as their base to provide easy management. You do not need to use this in your own projects, so the "parent" tag and it's contents below may be removed
|
<!-- Note: HAPI projects use the "hapi-fhir" POM as their base to provide easy management. You do not need to use this in your own projects, so the "parent" tag
|
||||||
if you are using this file as a basis for your own project. -->
|
and it's contents below may be removed if you are using this file as a basis for your own project. -->
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir</artifactId>
|
<artifactId>hapi-fhir</artifactId>
|
||||||
|
@ -30,6 +30,11 @@
|
||||||
<version>1.2-SNAPSHOT</version>
|
<version>1.2-SNAPSHOT</version>
|
||||||
<type>war</type>
|
<type>war</type>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
|
<artifactId>hapi-fhir-structures-dstu</artifactId>
|
||||||
|
<version>1.2-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir-structures-dstu2</artifactId>
|
<artifactId>hapi-fhir-structures-dstu2</artifactId>
|
||||||
|
@ -91,7 +96,34 @@
|
||||||
<groupId>org.eclipse.jetty</groupId>
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
<artifactId>jetty-webapp</artifactId>
|
<artifactId>jetty-webapp</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>jcl-over-slf4j</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.thymeleaf</groupId>
|
||||||
|
<artifactId>thymeleaf</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.ebaysf.web</groupId>
|
||||||
|
<artifactId>cors-filter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.phloc</groupId>
|
||||||
|
<artifactId>phloc-schematron</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.phloc</groupId>
|
||||||
|
<artifactId>phloc-commons</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.fusesource.jansi</groupId>
|
||||||
|
<artifactId>jansi</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
@ -163,6 +195,35 @@
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
|
<pluginManagement>
|
||||||
|
<plugins>
|
||||||
|
<!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself. -->
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.eclipse.m2e</groupId>
|
||||||
|
<artifactId>lifecycle-mapping</artifactId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
<configuration>
|
||||||
|
<lifecycleMappingMetadata>
|
||||||
|
<pluginExecutions>
|
||||||
|
<pluginExecution>
|
||||||
|
<pluginExecutionFilter>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-dependency-plugin</artifactId>
|
||||||
|
<versionRange>[2.10,)</versionRange>
|
||||||
|
<goals>
|
||||||
|
<goal>copy</goal>
|
||||||
|
</goals>
|
||||||
|
</pluginExecutionFilter>
|
||||||
|
<action>
|
||||||
|
<ignore></ignore>
|
||||||
|
</action>
|
||||||
|
</pluginExecution>
|
||||||
|
</pluginExecutions>
|
||||||
|
</lifecycleMappingMetadata>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</pluginManagement>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package ca.uhn.fhir.cli;
|
package ca.uhn.fhir.cli;
|
||||||
|
|
||||||
|
import java.io.PrintWriter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -7,26 +8,130 @@ import java.util.List;
|
||||||
|
|
||||||
import org.apache.commons.cli.CommandLine;
|
import org.apache.commons.cli.CommandLine;
|
||||||
import org.apache.commons.cli.DefaultParser;
|
import org.apache.commons.cli.DefaultParser;
|
||||||
|
import org.apache.commons.cli.HelpFormatter;
|
||||||
import org.apache.commons.cli.Options;
|
import org.apache.commons.cli.Options;
|
||||||
import org.apache.commons.cli.ParseException;
|
import org.apache.commons.cli.ParseException;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.commons.lang3.text.WordUtils;
|
||||||
|
import org.fusesource.jansi.AnsiConsole;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.util.VersionUtil;
|
||||||
|
import ch.qos.logback.classic.LoggerContext;
|
||||||
|
import ch.qos.logback.classic.joran.JoranConfigurator;
|
||||||
|
import ch.qos.logback.core.joran.spi.JoranException;
|
||||||
|
import static org.fusesource.jansi.Ansi.*;
|
||||||
|
import static org.fusesource.jansi.Ansi.Color.*;
|
||||||
|
import static org.fusesource.jansi.Ansi.*;
|
||||||
|
import static org.fusesource.jansi.Ansi.Color.*;
|
||||||
public class App {
|
public class App {
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(App.class);
|
|
||||||
private static List<BaseCommand> ourCommands;
|
private static List<BaseCommand> ourCommands;
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(App.class);
|
||||||
|
|
||||||
static {
|
static {
|
||||||
ourCommands = new ArrayList<BaseCommand>();
|
ourCommands = new ArrayList<BaseCommand>();
|
||||||
ourCommands.add(new RunServerCommand());
|
ourCommands.add(new RunServerCommand());
|
||||||
|
ourCommands.add(new ExampleDataUploader());
|
||||||
|
|
||||||
Collections.sort(ourCommands);
|
Collections.sort(ourCommands);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void logCommandUsage(BaseCommand theCommand) {
|
||||||
|
logAppHeader();
|
||||||
|
|
||||||
|
System.out.println("Usage:");
|
||||||
|
System.out.println(" hapi-fhir-cli " + theCommand.getCommandName() + " [options]");
|
||||||
|
System.out.println();
|
||||||
|
System.out.println("Options:");
|
||||||
|
|
||||||
|
HelpFormatter fmt = new HelpFormatter();
|
||||||
|
PrintWriter pw = new PrintWriter(System.out);
|
||||||
|
fmt.printOptions(pw, 80, theCommand.getOptions(), 2, 2);
|
||||||
|
pw.flush();
|
||||||
|
pw.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void loggingConfigOff() {
|
||||||
|
try {
|
||||||
|
JoranConfigurator configurator = new JoranConfigurator();
|
||||||
|
configurator.setContext((LoggerContext) LoggerFactory.getILoggerFactory());
|
||||||
|
((LoggerContext) LoggerFactory.getILoggerFactory()).reset();
|
||||||
|
configurator.doConfigure(App.class.getResourceAsStream("/logback-cli-off.xml"));
|
||||||
|
} catch (JoranException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void loggingConfigOn() {
|
||||||
|
try {
|
||||||
|
JoranConfigurator configurator = new JoranConfigurator();
|
||||||
|
configurator.setContext((LoggerContext) LoggerFactory.getILoggerFactory());
|
||||||
|
((LoggerContext) LoggerFactory.getILoggerFactory()).reset();
|
||||||
|
configurator.doConfigure(App.class.getResourceAsStream("/logback-cli-on.xml"));
|
||||||
|
} catch (JoranException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void logUsage() {
|
||||||
|
logAppHeader();
|
||||||
|
System.out.println("Usage:");
|
||||||
|
System.out.println(" hapi-fhir-cli {command} [options]");
|
||||||
|
System.out.println();
|
||||||
|
System.out.println("Commands:");
|
||||||
|
for (BaseCommand next : ourCommands) {
|
||||||
|
String left = " " + next.getCommandName() + " - ";
|
||||||
|
String[] rightParts = WordUtils.wrap(next.getCommandDescription(), 80 - left.length()).split("\\n");
|
||||||
|
for (int i = 1; i < rightParts.length; i++) {
|
||||||
|
rightParts[i] = StringUtils.leftPad("", left.length()) + rightParts[i];
|
||||||
|
}
|
||||||
|
System.out.println(left + StringUtils.join(rightParts, '\n'));
|
||||||
|
}
|
||||||
|
System.out.println();
|
||||||
|
System.out.println("See what options are available:");
|
||||||
|
System.out.println(" hapi-fhir-cli help {command}");
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void logAppHeader() {
|
||||||
|
System.out.flush();
|
||||||
|
System.out.println("------------------------------------------------------------");
|
||||||
|
System.out.println("\ud83d\udd25 " + ansi().bold() + "HAPI FHIR" + ansi().boldOff() + " " + VersionUtil.getVersion() + " - Command Line Tool");
|
||||||
|
System.out.println("------------------------------------------------------------");
|
||||||
|
}
|
||||||
|
|
||||||
public static void main(String[] theArgs) {
|
public static void main(String[] theArgs) {
|
||||||
|
loggingConfigOff();
|
||||||
|
AnsiConsole.systemInstall();
|
||||||
|
|
||||||
|
// log version while the logging is off
|
||||||
|
VersionUtil.getVersion();
|
||||||
|
|
||||||
if (theArgs.length == 0) {
|
if (theArgs.length == 0) {
|
||||||
logUsage();
|
logUsage();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (theArgs[0].equals("help")) {
|
||||||
|
if (theArgs.length < 2) {
|
||||||
|
logUsage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
BaseCommand command = null;
|
||||||
|
for (BaseCommand nextCommand : ourCommands) {
|
||||||
|
if (nextCommand.getCommandName().equals(theArgs[1])) {
|
||||||
|
command = nextCommand;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (command == null) {
|
||||||
|
System.err.println("Unknown command: " + theArgs[1]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
logCommandUsage(command);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
BaseCommand command = null;
|
BaseCommand command = null;
|
||||||
for (BaseCommand nextCommand : ourCommands) {
|
for (BaseCommand nextCommand : ourCommands) {
|
||||||
if (nextCommand.getCommandName().equals(theArgs[0])) {
|
if (nextCommand.getCommandName().equals(theArgs[0])) {
|
||||||
|
@ -41,18 +146,24 @@ public class App {
|
||||||
try {
|
try {
|
||||||
String[] args = Arrays.asList(theArgs).subList(1, theArgs.length).toArray(new String[theArgs.length - 1]);
|
String[] args = Arrays.asList(theArgs).subList(1, theArgs.length).toArray(new String[theArgs.length - 1]);
|
||||||
parsedOptions = parser.parse(options, args, true);
|
parsedOptions = parser.parse(options, args, true);
|
||||||
|
|
||||||
|
logAppHeader();
|
||||||
|
loggingConfigOn();
|
||||||
|
|
||||||
|
// Actually execute the command
|
||||||
command.run(parsedOptions);
|
command.run(parsedOptions);
|
||||||
|
|
||||||
} catch (ParseException e) {
|
} catch (ParseException e) {
|
||||||
ourLog.error("Invalid command options for command: " + command.getCommandName());
|
ourLog.error("Invalid command options for command: " + command.getCommandName());
|
||||||
ourLog.error(e.getMessage());
|
ourLog.error(e.getMessage());
|
||||||
ourLog.error("Aborting!");
|
ourLog.error("Aborting!");
|
||||||
return;
|
return;
|
||||||
|
} catch (CommandFailureException e) {
|
||||||
|
ourLog.error(e.getMessage());
|
||||||
|
} catch (Exception e) {
|
||||||
|
ourLog.error("Error during execution: ", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void logUsage() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,20 +13,23 @@ public abstract class BaseCommand implements Comparable<BaseCommand> {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected IGenericClient newClient(FhirContext ctx) {
|
|
||||||
IGenericClient fhirClient = ctx.newRestfulGenericClient("http://fhirtest.uhn.ca/baseDstu2");
|
|
||||||
return fhirClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract Options getOptions();
|
|
||||||
|
|
||||||
public abstract String getCommandName();
|
|
||||||
|
|
||||||
public abstract void run(CommandLine theCommandLine) throws ParseException;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(BaseCommand theO) {
|
public int compareTo(BaseCommand theO) {
|
||||||
return getCommandName().compareTo(theO.getCommandName());
|
return getCommandName().compareTo(theO.getCommandName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract String getCommandDescription();
|
||||||
|
|
||||||
|
public abstract String getCommandName();
|
||||||
|
|
||||||
|
public abstract Options getOptions();
|
||||||
|
|
||||||
|
protected IGenericClient newClient(FhirContext ctx, String theBaseUrl) {
|
||||||
|
ctx.getRestfulClientFactory().setSocketTimeout(10 * 60 * 1000);
|
||||||
|
IGenericClient fhirClient = ctx.newRestfulGenericClient(theBaseUrl);
|
||||||
|
return fhirClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void run(CommandLine theCommandLine) throws ParseException, Exception;
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package ca.uhn.fhir.cli;
|
||||||
|
|
||||||
|
public class CommandFailureException extends Exception {
|
||||||
|
|
||||||
|
public CommandFailureException(String theMessage) {
|
||||||
|
super(theMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
}
|
|
@ -1,17 +1,22 @@
|
||||||
package ca.uhn.fhir.cli;
|
package ca.uhn.fhir.cli;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.util.HashMap;
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipInputStream;
|
import java.util.zip.ZipInputStream;
|
||||||
|
|
||||||
|
import org.apache.commons.cli.CommandLine;
|
||||||
|
import org.apache.commons.cli.Option;
|
||||||
|
import org.apache.commons.cli.Options;
|
||||||
|
import org.apache.commons.cli.ParseException;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.http.client.ClientProtocolException;
|
|
||||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
import org.apache.http.client.methods.HttpGet;
|
import org.apache.http.client.methods.HttpGet;
|
||||||
import org.apache.http.impl.client.CloseableHttpClient;
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
@ -23,32 +28,72 @@ import ca.uhn.fhir.model.api.IResource;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
|
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Bundle.EntryRequest;
|
import ca.uhn.fhir.model.dstu2.resource.Bundle.EntryRequest;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.DataElement;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.SearchParameter;
|
import ca.uhn.fhir.model.dstu2.resource.SearchParameter;
|
||||||
import ca.uhn.fhir.model.dstu2.valueset.HTTPVerbEnum;
|
import ca.uhn.fhir.model.dstu2.valueset.HTTPVerbEnum;
|
||||||
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
import ca.uhn.fhir.parser.DataFormatException;
|
import ca.uhn.fhir.parser.DataFormatException;
|
||||||
import ca.uhn.fhir.rest.client.IGenericClient;
|
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||||
import ca.uhn.fhir.util.ResourceReferenceInfo;
|
import ca.uhn.fhir.util.ResourceReferenceInfo;
|
||||||
|
|
||||||
public class ExampleDataUploader extends BaseCommand {
|
public class ExampleDataUploader extends BaseCommand {
|
||||||
|
|
||||||
|
private static final String SPEC_DEFAULT_VERSION = "2015Sep";
|
||||||
|
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExampleDataUploader.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExampleDataUploader.class);
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
@Override
|
||||||
new ExampleDataUploader().execute();
|
public String getCommandDescription() {
|
||||||
|
return "Downloads the resource example pack from the HL7.org FHIR specification website, and uploads all of the example resources to a given server.";
|
||||||
}
|
}
|
||||||
|
|
||||||
private void execute() throws IOException, ClientProtocolException, UnsupportedEncodingException {
|
@Override
|
||||||
ourLog.info("Starting...");
|
public String getCommandName() {
|
||||||
|
return "upload-examples";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Options getOptions() {
|
||||||
|
Options options = new Options();
|
||||||
|
Option opt;
|
||||||
|
|
||||||
|
opt = new Option("f", "fhirversion", true, "Spec version to upload (default is '" + SPEC_DEFAULT_VERSION + "')");
|
||||||
|
opt.setRequired(false);
|
||||||
|
options.addOption(opt);
|
||||||
|
|
||||||
|
opt = new Option("t", "target", true, "Base URL for the target server");
|
||||||
|
opt.setRequired(true);
|
||||||
|
options.addOption(opt);
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(CommandLine theCommandLine) throws Exception {
|
||||||
|
String specVersion = theCommandLine.getOptionValue("f", SPEC_DEFAULT_VERSION);
|
||||||
|
String targetServer = theCommandLine.getOptionValue("t");
|
||||||
|
if (isBlank(targetServer)) {
|
||||||
|
throw new ParseException("No target server (-t) specified");
|
||||||
|
} else if (targetServer.startsWith("http") == false) {
|
||||||
|
throw new ParseException("Invalid target server specified, must begin with 'http'");
|
||||||
|
}
|
||||||
|
|
||||||
FhirContext ctx = FhirContext.forDstu2();
|
FhirContext ctx = FhirContext.forDstu2();
|
||||||
|
String specUrl = "http://hl7.org/fhir/" + specVersion + "/examples-json.zip";
|
||||||
|
|
||||||
HttpGet get = new HttpGet("http://hl7.org/fhir/2015May/examples-json.zip");
|
ourLog.info("HTTP fetching: {}", specUrl);
|
||||||
|
|
||||||
|
HttpGet get = new HttpGet(specUrl);
|
||||||
CloseableHttpClient client = HttpClientBuilder.create().build();
|
CloseableHttpClient client = HttpClientBuilder.create().build();
|
||||||
CloseableHttpResponse result = client.execute(get);
|
CloseableHttpResponse result = client.execute(get);
|
||||||
|
|
||||||
|
if (result.getStatusLine().getStatusCode() != 200) {
|
||||||
|
throw new CommandFailureException("Got HTTP " + result.getStatusLine().getStatusCode() + " response code loading " + specUrl);
|
||||||
|
}
|
||||||
|
|
||||||
byte[] bytes = IOUtils.toByteArray(result.getEntity().getContent());
|
byte[] bytes = IOUtils.toByteArray(result.getEntity().getContent());
|
||||||
IOUtils.closeQuietly(result.getEntity().getContent());
|
IOUtils.closeQuietly(result.getEntity().getContent());
|
||||||
|
|
||||||
ourLog.info("Loaded examples ({} bytes)", bytes.length);
|
ourLog.info("Successfully Loaded example pack ({} bytes)", bytes.length);
|
||||||
|
|
||||||
ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(bytes));
|
ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(bytes));
|
||||||
byte[] buffer = new byte[2048];
|
byte[] buffer = new byte[2048];
|
||||||
|
@ -69,6 +114,10 @@ public class ExampleDataUploader extends BaseCommand {
|
||||||
byte[] exampleBytes = bos.toByteArray();
|
byte[] exampleBytes = bos.toByteArray();
|
||||||
String exampleString = new String(exampleBytes, "UTF-8");
|
String exampleString = new String(exampleBytes, "UTF-8");
|
||||||
|
|
||||||
|
if (ourLog.isTraceEnabled()) {
|
||||||
|
ourLog.trace("Next example: " + exampleString);
|
||||||
|
}
|
||||||
|
|
||||||
IBaseResource parsed;
|
IBaseResource parsed;
|
||||||
try {
|
try {
|
||||||
parsed = ctx.newJsonParser().parseResource(exampleString);
|
parsed = ctx.newJsonParser().parseResource(exampleString);
|
||||||
|
@ -101,16 +150,60 @@ public class ExampleDataUploader extends BaseCommand {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<String> ids = new HashSet<String>();
|
Map<String, Integer> ids = new HashMap<String, Integer>();
|
||||||
|
Set<String> fullIds = new HashSet<String>();
|
||||||
|
for (int i = 0; i < bundle.getEntry().size(); i++) {
|
||||||
|
Entry next = bundle.getEntry().get(i);
|
||||||
|
|
||||||
|
// DataElement have giant IDs that seem invalid, need to investigate this..
|
||||||
|
if ("DataElement".equals(next.getResource().getResourceName()) || "OperationOutcome".equals(next.getResource().getResourceName())
|
||||||
|
|| "OperationDefinition".equals(next.getResource().getResourceName())) {
|
||||||
|
ourLog.info("Skipping " + next.getResource().getResourceName() + " example");
|
||||||
|
bundle.getEntry().remove(i);
|
||||||
|
i--;
|
||||||
|
} else {
|
||||||
|
IdDt resourceId = next.getResource().getId();
|
||||||
|
if (!fullIds.add(resourceId.toUnqualifiedVersionless().getValue())) {
|
||||||
|
ourLog.info("Discarding duplicate resource: " + resourceId.getValue());
|
||||||
|
bundle.getEntry().remove(i);
|
||||||
|
i--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String idPart = resourceId.getIdPart();
|
||||||
|
if (idPart != null) {
|
||||||
|
if (!ids.containsKey(idPart)) {
|
||||||
|
ids.put(idPart, 1);
|
||||||
|
} else {
|
||||||
|
ids.put(idPart, ids.get(idPart) + 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ourLog.info("Discarding resource with not explicit ID");
|
||||||
|
bundle.getEntry().remove(i);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<String> qualIds = new HashSet<String>();
|
||||||
|
Map<String, String> renames = new HashMap<String,String>();
|
||||||
for (int i = 0; i < bundle.getEntry().size(); i++) {
|
for (int i = 0; i < bundle.getEntry().size(); i++) {
|
||||||
Entry next = bundle.getEntry().get(i);
|
Entry next = bundle.getEntry().get(i);
|
||||||
if (next.getResource().getId().getIdPart() != null) {
|
if (next.getResource().getId().getIdPart() != null) {
|
||||||
String nextId = next.getResource().getResourceName() + '/' + next.getResource().getId().getIdPart();
|
String idPart = next.getResource().getId().getIdPart();
|
||||||
if (!ids.add(nextId)) {
|
String originalId = next.getResource().getResourceName() + '/' + idPart;
|
||||||
|
if (ids.get(idPart) > 1 || next.getResource().getId().isIdPartValidLong()) {
|
||||||
|
idPart = next.getResource().getResourceName() + idPart;
|
||||||
|
}
|
||||||
|
String nextId = next.getResource().getResourceName() + '/' + idPart;
|
||||||
|
if (!qualIds.add(nextId)) {
|
||||||
ourLog.info("Discarding duplicate resource with ID: " + nextId);
|
ourLog.info("Discarding duplicate resource with ID: " + nextId);
|
||||||
bundle.getEntry().remove(i);
|
bundle.getEntry().remove(i);
|
||||||
i--;
|
i--;
|
||||||
}
|
}
|
||||||
|
next.getRequest().setMethod(HTTPVerbEnum.PUT);
|
||||||
|
next.getRequest().setUrl(nextId);
|
||||||
|
renames.put(originalId, nextId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,14 +212,20 @@ public class ExampleDataUploader extends BaseCommand {
|
||||||
List<ResourceReferenceInfo> refs = ctx.newTerser().getAllResourceReferences(next.getResource());
|
List<ResourceReferenceInfo> refs = ctx.newTerser().getAllResourceReferences(next.getResource());
|
||||||
for (ResourceReferenceInfo nextRef : refs) {
|
for (ResourceReferenceInfo nextRef : refs) {
|
||||||
// if (nextRef.getResourceReference().getReferenceElement().isAbsolute()) {
|
// if (nextRef.getResourceReference().getReferenceElement().isAbsolute()) {
|
||||||
// ourLog.info("Discarding absolute reference: {}", nextRef.getResourceReference().getReferenceElement().getValue());
|
// ourLog.info("Discarding absolute reference: {}",
|
||||||
|
// nextRef.getResourceReference().getReferenceElement().getValue());
|
||||||
// nextRef.getResourceReference().getReferenceElement().setValue(null);
|
// nextRef.getResourceReference().getReferenceElement().setValue(null);
|
||||||
// }
|
// }
|
||||||
nextRef.getResourceReference().getReferenceElement().setValue(nextRef.getResourceReference().getReferenceElement().toUnqualifiedVersionless().getValue());
|
nextRef.getResourceReference().getReferenceElement().setValue(nextRef.getResourceReference().getReferenceElement().toUnqualifiedVersionless().getValue());
|
||||||
String value = nextRef.getResourceReference().getReferenceElement().toUnqualifiedVersionless().getValue();
|
String value = nextRef.getResourceReference().getReferenceElement().toUnqualifiedVersionless().getValue();
|
||||||
if (!ids.contains(value) && !nextRef.getResourceReference().getReferenceElement().isLocal()) {
|
if (!qualIds.contains(value) && !nextRef.getResourceReference().getReferenceElement().isLocal()) {
|
||||||
|
if (renames.containsKey(value)) {
|
||||||
|
nextRef.getResourceReference().setReference(renames.get(value));
|
||||||
|
goodRefs++;
|
||||||
|
} else {
|
||||||
ourLog.info("Discarding unknown reference: {}", value);
|
ourLog.info("Discarding unknown reference: {}", value);
|
||||||
nextRef.getResourceReference().getReferenceElement().setValue(null);
|
nextRef.getResourceReference().getReferenceElement().setValue(null);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
goodRefs++;
|
goodRefs++;
|
||||||
}
|
}
|
||||||
|
@ -134,8 +233,10 @@ public class ExampleDataUploader extends BaseCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
// for (Entry next : bundle.getEntry()) {
|
// for (Entry next : bundle.getEntry()) {
|
||||||
// if (next.getResource().getId().hasIdPart() && Character.isLetter(next.getResource().getId().getIdPart().charAt(0))) {
|
// if (next.getResource().getId().hasIdPart() &&
|
||||||
// next.getTransaction().setUrl(next.getResource().getResourceName() + '/' + next.getResource().getId().getIdPart());
|
// Character.isLetter(next.getResource().getId().getIdPart().charAt(0))) {
|
||||||
|
// next.getTransaction().setUrl(next.getResource().getResourceName() + '/' +
|
||||||
|
// next.getResource().getId().getIdPart());
|
||||||
// next.getTransaction().setMethod(HTTPVerbEnum.PUT);
|
// next.getTransaction().setMethod(HTTPVerbEnum.PUT);
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
@ -146,8 +247,16 @@ public class ExampleDataUploader extends BaseCommand {
|
||||||
ourLog.info("Final bundle: {} entries", bundle.getEntry().size());
|
ourLog.info("Final bundle: {} entries", bundle.getEntry().size());
|
||||||
ourLog.info("Final bundle: {} chars", encoded.length());
|
ourLog.info("Final bundle: {} chars", encoded.length());
|
||||||
|
|
||||||
IGenericClient fhirClient = newClient(ctx);
|
ourLog.info("Uploading bundle to server: " + targetServer);
|
||||||
|
|
||||||
|
IGenericClient fhirClient = newClient(ctx, targetServer);
|
||||||
|
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
;
|
||||||
fhirClient.transaction().withBundle(bundle).execute();
|
fhirClient.transaction().withBundle(bundle).execute();
|
||||||
|
long delay = System.currentTimeMillis() - start;
|
||||||
|
|
||||||
|
ourLog.info("Finished uploading bundle to server (took {} ms)", delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,6 +66,7 @@ public class RunServerCommand extends BaseCommand {
|
||||||
WebAppContext root = new WebAppContext();
|
WebAppContext root = new WebAppContext();
|
||||||
root.setAllowDuplicateFragmentNames(true);
|
root.setAllowDuplicateFragmentNames(true);
|
||||||
root.setWar(tempWarFile.getAbsolutePath());
|
root.setWar(tempWarFile.getAbsolutePath());
|
||||||
|
root.setParentLoaderPriority(true);
|
||||||
root.setContextPath("/");
|
root.setContextPath("/");
|
||||||
|
|
||||||
myServer = new Server(myPort);
|
myServer = new Server(myPort);
|
||||||
|
@ -99,4 +100,9 @@ public class RunServerCommand extends BaseCommand {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCommandDescription() {
|
||||||
|
return "Start a FHIR server which can be used for testing";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,9 @@ package ca.uhn.fhir.cli;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
|
|
||||||
|
import org.apache.commons.cli.CommandLine;
|
||||||
|
import org.apache.commons.cli.Options;
|
||||||
|
import org.apache.commons.cli.ParseException;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.http.client.ClientProtocolException;
|
import org.apache.http.client.ClientProtocolException;
|
||||||
import org.hl7.fhir.instance.model.Bundle;
|
import org.hl7.fhir.instance.model.Bundle;
|
||||||
|
@ -29,7 +32,7 @@ public class ValidationDataUploader extends BaseCommand {
|
||||||
|
|
||||||
FhirContext ctx = FhirContext.forDstu2Hl7Org();
|
FhirContext ctx = FhirContext.forDstu2Hl7Org();
|
||||||
|
|
||||||
IGenericClient client = newClient(ctx);
|
IGenericClient client = newClient(ctx,"");
|
||||||
|
|
||||||
int total;
|
int total;
|
||||||
int count;
|
int count;
|
||||||
|
@ -104,4 +107,28 @@ public class ValidationDataUploader extends BaseCommand {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCommandDescription() {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCommandName() {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Options getOptions() {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(CommandLine theCommandLine) throws ParseException {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
<configuration>
|
||||||
|
|
||||||
|
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<encoder>
|
||||||
|
<pattern>%d{HH:mm:ss} %logger{36} - %msg%n
|
||||||
|
</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<root level="error">
|
||||||
|
<appender-ref ref="STDOUT" />
|
||||||
|
</root>
|
||||||
|
|
||||||
|
</configuration>
|
|
@ -0,0 +1,46 @@
|
||||||
|
<configuration>
|
||||||
|
|
||||||
|
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<useJansi>true</useJansi>
|
||||||
|
<encoder>
|
||||||
|
<pattern>%boldGreen(%d{HH:mm:ss}) %white(%-5level) %logger{36} - %boldWhite(%msg%n)
|
||||||
|
</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<logger name="java.sql" additivity="false" level="warn">
|
||||||
|
<appender-ref ref="STDOUT" />
|
||||||
|
</logger>
|
||||||
|
<logger name="ca.uhn.fhir.jpa" additivity="false" level="info">
|
||||||
|
<appender-ref ref="STDOUT" />
|
||||||
|
</logger>
|
||||||
|
<logger name="ca.uhn.fhir.rest" additivity="false" level="warn">
|
||||||
|
<appender-ref ref="STDOUT" />
|
||||||
|
</logger>
|
||||||
|
<logger name="org.eclipse" additivity="false" level="warn">
|
||||||
|
<appender-ref ref="STDOUT" />
|
||||||
|
</logger>
|
||||||
|
<logger name="org.springframework" additivity="false" level="warn">
|
||||||
|
<appender-ref ref="STDOUT" />
|
||||||
|
</logger>
|
||||||
|
<logger name="org.apache" additivity="false" level="info">
|
||||||
|
<appender-ref ref="STDOUT" />
|
||||||
|
</logger>
|
||||||
|
<logger name="org.thymeleaf" additivity="false" level="warn">
|
||||||
|
<appender-ref ref="STDOUT" />
|
||||||
|
</logger>
|
||||||
|
<logger name="org.hibernate" additivity="false" level="warn">
|
||||||
|
<appender-ref ref="STDOUT" />
|
||||||
|
</logger>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<logger name="ca.uhn.fhir.rest.client" additivity="false" level="trace">
|
||||||
|
<appender-ref ref="STDOUT" />
|
||||||
|
</logger>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<root level="info">
|
||||||
|
<appender-ref ref="STDOUT" />
|
||||||
|
</root>
|
||||||
|
|
||||||
|
</configuration>
|
|
@ -1,30 +0,0 @@
|
||||||
<configuration>
|
|
||||||
|
|
||||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
|
||||||
<encoder>
|
|
||||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%file:%line] - %msg%n
|
|
||||||
</pattern>
|
|
||||||
</encoder>
|
|
||||||
</appender>
|
|
||||||
|
|
||||||
<logger name="org.eclipse" additivity="false" level="info">
|
|
||||||
<appender-ref ref="STDOUT" />
|
|
||||||
</logger>
|
|
||||||
<logger name="org.apache" additivity="false" level="info">
|
|
||||||
<appender-ref ref="STDOUT" />
|
|
||||||
</logger>
|
|
||||||
<logger name="org.thymeleaf" additivity="false" level="warn">
|
|
||||||
<appender-ref ref="STDOUT" />
|
|
||||||
</logger>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
<logger name="ca.uhn.fhir.rest.client" additivity="false" level="trace">
|
|
||||||
<appender-ref ref="STDOUT" />
|
|
||||||
</logger>
|
|
||||||
-->
|
|
||||||
|
|
||||||
<root level="info">
|
|
||||||
<appender-ref ref="STDOUT" />
|
|
||||||
</root>
|
|
||||||
|
|
||||||
</configuration>
|
|
|
@ -0,0 +1,132 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you 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.
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# HAPI-FHIR-CLI Startup Script
|
||||||
|
#
|
||||||
|
|
||||||
|
# OS specific support. $var _must_ be set to either true or false.
|
||||||
|
cygwin=false;
|
||||||
|
darwin=false;
|
||||||
|
mingw=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN*) cygwin=true ;;
|
||||||
|
MINGW*) mingw=true;;
|
||||||
|
Darwin*) darwin=true
|
||||||
|
#
|
||||||
|
# Look for the Apple JDKs first to preserve the existing behaviour, and then look
|
||||||
|
# for the new JDKs provided by Oracle.
|
||||||
|
#
|
||||||
|
if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then
|
||||||
|
#
|
||||||
|
# Apple JDKs
|
||||||
|
#
|
||||||
|
export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then
|
||||||
|
#
|
||||||
|
# Apple JDKs
|
||||||
|
#
|
||||||
|
export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then
|
||||||
|
#
|
||||||
|
# Oracle JDKs
|
||||||
|
#
|
||||||
|
export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then
|
||||||
|
#
|
||||||
|
# Apple JDKs
|
||||||
|
#
|
||||||
|
export JAVA_HOME=`/usr/libexec/java_home`
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -z "$JAVA_HOME" ] ; then
|
||||||
|
if [ -r /etc/gentoo-release ] ; then
|
||||||
|
JAVA_HOME=`java-config --jre-home`
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin, ensure paths are in UNIX format before anything is touched
|
||||||
|
if $cygwin ; then
|
||||||
|
[ -n "$JAVA_HOME" ] &&
|
||||||
|
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Migwn, ensure paths are in UNIX format before anything is touched
|
||||||
|
if $mingw ; then
|
||||||
|
[ -n "$JAVA_HOME" ] &&
|
||||||
|
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$JAVA_HOME" ]; then
|
||||||
|
javaExecutable="`which javac`"
|
||||||
|
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
|
||||||
|
# readlink(1) is not available as standard on Solaris 10.
|
||||||
|
readLink=`which readlink`
|
||||||
|
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
|
||||||
|
if $darwin ; then
|
||||||
|
javaHome="`dirname \"$javaExecutable\"`"
|
||||||
|
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
|
||||||
|
else
|
||||||
|
javaExecutable="`readlink -f \"$javaExecutable\"`"
|
||||||
|
fi
|
||||||
|
javaHome="`dirname \"$javaExecutable\"`"
|
||||||
|
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
|
||||||
|
JAVA_HOME="$javaHome"
|
||||||
|
export JAVA_HOME
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$JAVACMD" ] ; then
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="`which java`"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
echo "Error: JAVA_HOME is not defined correctly." >&2
|
||||||
|
echo " We cannot execute $JAVACMD" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$JAVA_HOME" ] ; then
|
||||||
|
echo "Warning: JAVA_HOME environment variable is not set."
|
||||||
|
fi
|
||||||
|
|
||||||
|
SCRIPTDIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
|
||||||
|
|
||||||
|
exec "$JAVACMD" \
|
||||||
|
$CLI_OPTS \
|
||||||
|
-jar $SCRIPTDIR/hapi-fhir-cli.jar "$@"
|
|
@ -0,0 +1,135 @@
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
@REM or more contributor license agreements. See the NOTICE file
|
||||||
|
@REM distributed with this work for additional information
|
||||||
|
@REM regarding copyright ownership. The ASF licenses this file
|
||||||
|
@REM to you under the Apache License, Version 2.0 (the
|
||||||
|
@REM "License"); you may not use this file except in compliance
|
||||||
|
@REM with the License. You may obtain a copy of the License at
|
||||||
|
@REM
|
||||||
|
@REM http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@REM
|
||||||
|
@REM Unless required by applicable law or agreed to in writing,
|
||||||
|
@REM software distributed under the License is distributed on an
|
||||||
|
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
@REM KIND, either express or implied. See the License for the
|
||||||
|
@REM specific language governing permissions and limitations
|
||||||
|
@REM under the License.
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
@REM HAPI-FHIR CLI Start Up Batch script
|
||||||
|
@REM
|
||||||
|
@REM Required ENV vars:
|
||||||
|
@REM JAVA_HOME - location of a JDK home dir
|
||||||
|
@REM
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@echo off
|
||||||
|
|
||||||
|
@REM set %HOME% to equivalent of $HOME
|
||||||
|
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
|
||||||
|
|
||||||
|
@setlocal
|
||||||
|
|
||||||
|
set ERROR_CODE=0
|
||||||
|
|
||||||
|
@REM To isolate internal variables from possible post scripts, we use another setlocal
|
||||||
|
@setlocal
|
||||||
|
|
||||||
|
@REM ==== START VALIDATION ====
|
||||||
|
if not "%JAVA_HOME%" == "" goto OkJHome
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo Error: JAVA_HOME not found in your environment. >&2
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||||
|
echo location of your Java installation. >&2
|
||||||
|
echo.
|
||||||
|
goto error
|
||||||
|
|
||||||
|
:OkJHome
|
||||||
|
if exist "%JAVA_HOME%\bin\java.exe" goto chkMHome
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo Error: JAVA_HOME is set to an invalid directory. >&2
|
||||||
|
echo JAVA_HOME = "%JAVA_HOME%" >&2
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||||
|
echo location of your Java installation. >&2
|
||||||
|
echo.
|
||||||
|
goto error
|
||||||
|
|
||||||
|
:chkMHome
|
||||||
|
if not "%CLI_HOME%"=="" goto valMHome
|
||||||
|
|
||||||
|
SET "CLI_HOME=%~dp0."
|
||||||
|
if not "%CLI_HOME%"=="" goto valMHome
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo Error: CLI_HOME not found in your environment. >&2
|
||||||
|
echo Please set the CLI_HOME variable in your environment to match the >&2
|
||||||
|
echo location of the Maven installation. >&2
|
||||||
|
echo.
|
||||||
|
goto error
|
||||||
|
|
||||||
|
:valMHome
|
||||||
|
|
||||||
|
:stripMHome
|
||||||
|
if not "_%CLI_HOME:~-1%"=="_\" goto checkMCmd
|
||||||
|
set "CLI_HOME=%CLI_HOME:~0,-1%"
|
||||||
|
goto stripMHome
|
||||||
|
|
||||||
|
:checkMCmd
|
||||||
|
if exist "%CLI_HOME%\hapi-fhir-cli.jar" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo Error: CLI_HOME is set to an invalid directory. >&2
|
||||||
|
echo CLI_HOME = "%CLI_HOME%" >&2
|
||||||
|
echo Please set the CLI_HOME variable in your environment to match the >&2
|
||||||
|
echo location of the Maven installation >&2
|
||||||
|
echo.
|
||||||
|
goto error
|
||||||
|
@REM ==== END VALIDATION ====
|
||||||
|
|
||||||
|
:init
|
||||||
|
|
||||||
|
set CLI_CMD_LINE_ARGS=%*
|
||||||
|
|
||||||
|
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
|
||||||
|
@REM Fallback to current working directory if not found.
|
||||||
|
|
||||||
|
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
|
||||||
|
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
|
||||||
|
|
||||||
|
set EXEC_DIR=%CD%
|
||||||
|
set WDIR=%EXEC_DIR%
|
||||||
|
:findBaseDir
|
||||||
|
IF EXIST "%WDIR%"\.mvn goto baseDirFound
|
||||||
|
cd ..
|
||||||
|
IF "%WDIR%"=="%CD%" goto baseDirNotFound
|
||||||
|
set WDIR=%CD%
|
||||||
|
goto findBaseDir
|
||||||
|
|
||||||
|
:baseDirFound
|
||||||
|
set MAVEN_PROJECTBASEDIR=%WDIR%
|
||||||
|
cd "%EXEC_DIR%"
|
||||||
|
goto endDetectBaseDir
|
||||||
|
|
||||||
|
:baseDirNotFound
|
||||||
|
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
|
||||||
|
cd "%EXEC_DIR%"
|
||||||
|
|
||||||
|
:endDetectBaseDir
|
||||||
|
|
||||||
|
SET CLI_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
|
||||||
|
|
||||||
|
%CLI_JAVA_EXE% %JVM_CONFIG_CLI_PROPS% -jar %CLI_HOME%\hapi-fhir-cli.jar %CLI_CMD_LINE_ARGS%
|
||||||
|
if ERRORLEVEL 1 goto error
|
||||||
|
goto end
|
||||||
|
|
||||||
|
:error
|
||||||
|
set ERROR_CODE=1
|
||||||
|
|
||||||
|
:end
|
||||||
|
@endlocal & set ERROR_CODE=%ERROR_CODE%
|
||||||
|
|
||||||
|
exit /B %ERROR_CODE%
|
|
@ -275,6 +275,11 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
||||||
// TODO: process verbs in the correct order
|
// TODO: process verbs in the correct order
|
||||||
|
|
||||||
for (int i = 0; i < theRequest.getEntry().size(); i++) {
|
for (int i = 0; i < theRequest.getEntry().size(); i++) {
|
||||||
|
|
||||||
|
if (i % 100 == 0) {
|
||||||
|
ourLog.info("Processed {} entries out of {}", i, theRequest.getEntry().size());
|
||||||
|
}
|
||||||
|
|
||||||
Entry nextEntry = theRequest.getEntry().get(i);
|
Entry nextEntry = theRequest.getEntry().get(i);
|
||||||
IResource res = nextEntry.getResource();
|
IResource res = nextEntry.getResource();
|
||||||
IdDt nextResourceId = null;
|
IdDt nextResourceId = null;
|
||||||
|
@ -313,7 +318,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
||||||
case POST: {
|
case POST: {
|
||||||
// CREATE
|
// CREATE
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
IFhirResourceDao resourceDao = getDao(res.getClass());
|
IFhirResourceDao resourceDao = getDaoOrThrowException(res.getClass());
|
||||||
res.setId((String) null);
|
res.setId((String) null);
|
||||||
DaoMethodOutcome outcome;
|
DaoMethodOutcome outcome;
|
||||||
Entry newEntry = response.addEntry();
|
Entry newEntry = response.addEntry();
|
||||||
|
@ -338,7 +343,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
||||||
case PUT: {
|
case PUT: {
|
||||||
// UPDATE
|
// UPDATE
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
IFhirResourceDao resourceDao = getDao(res.getClass());
|
IFhirResourceDao resourceDao = getDaoOrThrowException(res.getClass());
|
||||||
|
|
||||||
DaoMethodOutcome outcome;
|
DaoMethodOutcome outcome;
|
||||||
Entry newEntry = response.addEntry();
|
Entry newEntry = response.addEntry();
|
||||||
|
@ -490,6 +495,14 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IFhirResourceDao<?> getDaoOrThrowException(Class<? extends IResource> theClass) {
|
||||||
|
IFhirResourceDao<? extends IResource> retVal = getDao(theClass);
|
||||||
|
if (retVal == null) {
|
||||||
|
throw new InvalidRequestException("Unable to process request, this server does not know how to handle resources of type " + getContext().getResourceDefinition(theClass).getName());
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
private static void handleTransactionCreateOrUpdateOutcome(Map<IdDt, IdDt> idSubstitutions, Map<IdDt, DaoMethodOutcome> idToPersistedOutcome, IdDt nextResourceId, DaoMethodOutcome outcome,
|
private static void handleTransactionCreateOrUpdateOutcome(Map<IdDt, IdDt> idSubstitutions, Map<IdDt, DaoMethodOutcome> idToPersistedOutcome, IdDt nextResourceId, DaoMethodOutcome outcome,
|
||||||
Entry newEntry, String theResourceType, IResource theRes) {
|
Entry newEntry, String theResourceType, IResource theRes) {
|
||||||
IdDt newId = (IdDt) outcome.getId().toUnqualifiedVersionless();
|
IdDt newId = (IdDt) outcome.getId().toUnqualifiedVersionless();
|
||||||
|
|
|
@ -587,12 +587,10 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ourLog.info("Adding param: {}, {}", resourceName, nextValue.getValue());
|
ourLog.trace("Adding param: {}, {}", resourceName, nextValue.getValue());
|
||||||
|
|
||||||
ResourceIndexedSearchParamUri nextEntity = new ResourceIndexedSearchParamUri(resourceName, nextValue.getValue());
|
ResourceIndexedSearchParamUri nextEntity = new ResourceIndexedSearchParamUri(resourceName, nextValue.getValue());
|
||||||
|
|
||||||
ourLog.info("Added : {}", nextEntity);
|
|
||||||
|
|
||||||
nextEntity.setResource(theEntity);
|
nextEntity.setResource(theEntity);
|
||||||
retVal.add(nextEntity);
|
retVal.add(nextEntity);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -474,6 +474,17 @@ public class JsonParserDstu2Test {
|
||||||
assertThat(ourCtx.newJsonParser().setOmitResourceId(true).encodeResourceToString(p), not(containsString("123")));
|
assertThat(ourCtx.newJsonParser().setOmitResourceId(true).encodeResourceToString(p), not(containsString("123")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseAndEncodeBundleResourceWithComments() throws Exception {
|
||||||
|
String content = IOUtils.toString(JsonParserDstu2Test.class.getResourceAsStream("/bundle-transaction2.json"));
|
||||||
|
|
||||||
|
ourCtx.newJsonParser().parseBundle(content);
|
||||||
|
|
||||||
|
ca.uhn.fhir.model.dstu2.resource.Bundle parsed = ourCtx.newJsonParser().parseResource(ca.uhn.fhir.model.dstu2.resource.Bundle.class, content);
|
||||||
|
|
||||||
|
// TODO: preserve comments
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testParseAndEncodeBundle() throws Exception {
|
public void testParseAndEncodeBundle() throws Exception {
|
||||||
String content = IOUtils.toString(JsonParserDstu2Test.class.getResourceAsStream("/bundle-example.json"));
|
String content = IOUtils.toString(JsonParserDstu2Test.class.getResourceAsStream("/bundle-example.json"));
|
||||||
|
|
|
@ -0,0 +1,348 @@
|
||||||
|
{
|
||||||
|
"resourceType": "Bundle",
|
||||||
|
"id": "bundle-transaction",
|
||||||
|
"meta": {
|
||||||
|
"fhir_comments": [
|
||||||
|
" this example bundle is a transaction ",
|
||||||
|
" when the transaction was constructed "
|
||||||
|
],
|
||||||
|
"lastUpdated": "2014-08-18T01:43:30Z"
|
||||||
|
},
|
||||||
|
"type": "transaction",
|
||||||
|
"entry": [
|
||||||
|
{
|
||||||
|
"fhir_comments": [
|
||||||
|
" now, each entry is an action to take in the transaction "
|
||||||
|
],
|
||||||
|
"fullUrl": "urn:uuid:61ebe359-bfdc-4613-8bf2-c5e300945f0a",
|
||||||
|
"resource": {
|
||||||
|
"resourceType": "Patient",
|
||||||
|
"text": {
|
||||||
|
"fhir_comments": [
|
||||||
|
" no id for create operations ",
|
||||||
|
" and no metadata on this resource, though it would be valid "
|
||||||
|
],
|
||||||
|
"status": "generated",
|
||||||
|
"div": "<div>Some narrative</div>"
|
||||||
|
},
|
||||||
|
"active": true,
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"use": "official",
|
||||||
|
"family": [
|
||||||
|
"Chalmers"
|
||||||
|
],
|
||||||
|
"given": [
|
||||||
|
"Peter",
|
||||||
|
"James"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"gender": "male",
|
||||||
|
"birthDate": "1974-12-25"
|
||||||
|
},
|
||||||
|
"request": {
|
||||||
|
"fhir_comments": [
|
||||||
|
" now, details about the action to take with the resource "
|
||||||
|
],
|
||||||
|
"method": "POST",
|
||||||
|
"_method": {
|
||||||
|
"fhir_comments": [
|
||||||
|
" POST to [base]/Patient - that's a create "
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"url": "Patient",
|
||||||
|
"_url": {
|
||||||
|
"fhir_comments": [
|
||||||
|
" actually, in a transaction, you don't specify the [base], \n so [base]/Patient becomes just 'Patient': "
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fullUrl": "urn:uuid:88f151c0-a954-468a-88bd-5ae15c08e059",
|
||||||
|
"resource": {
|
||||||
|
"resourceType": "Patient",
|
||||||
|
"text": {
|
||||||
|
"fhir_comments": [
|
||||||
|
" no id for create operations ",
|
||||||
|
" and no metadata on this resource, though it would be valid "
|
||||||
|
],
|
||||||
|
"status": "generated",
|
||||||
|
"div": "<div>Some narrative</div>"
|
||||||
|
},
|
||||||
|
"active": true,
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"use": "official",
|
||||||
|
"family": [
|
||||||
|
"Chalmers"
|
||||||
|
],
|
||||||
|
"given": [
|
||||||
|
"Peter",
|
||||||
|
"James"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"gender": "male",
|
||||||
|
"birthDate": "1974-12-25"
|
||||||
|
},
|
||||||
|
"request": {
|
||||||
|
"fhir_comments": [
|
||||||
|
" transaction details "
|
||||||
|
],
|
||||||
|
"method": "POST",
|
||||||
|
"_method": {
|
||||||
|
"fhir_comments": [
|
||||||
|
" POST to [base]/Patient - that's a create "
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"url": "Patient",
|
||||||
|
"_url": {
|
||||||
|
"fhir_comments": [
|
||||||
|
" actually, in a transaction, you don't specify the [base], \n so [base]/Patient becomes just 'Patient': "
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ifNoneExist": "identifier=234234",
|
||||||
|
"_ifNoneExist": {
|
||||||
|
"fhir_comments": [
|
||||||
|
" the conditional header: only add this resource if \n there isn't already one for this patient. If there is one,\n the content of this resource will be ignored "
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fullUrl": "http://example.org/fhir/Patient/123",
|
||||||
|
"resource": {
|
||||||
|
"resourceType": "Patient",
|
||||||
|
"id": "123",
|
||||||
|
"_id": {
|
||||||
|
"fhir_comments": [
|
||||||
|
" the id here and in the url have to match "
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"text": {
|
||||||
|
"fhir_comments": [
|
||||||
|
" and no metadata on this resource, though it would be valid "
|
||||||
|
],
|
||||||
|
"status": "generated",
|
||||||
|
"div": "<div>Some narrative</div>"
|
||||||
|
},
|
||||||
|
"active": true,
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"use": "official",
|
||||||
|
"family": [
|
||||||
|
"Chalmers"
|
||||||
|
],
|
||||||
|
"given": [
|
||||||
|
"Peter",
|
||||||
|
"James"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"gender": "male",
|
||||||
|
"birthDate": "1974-12-25"
|
||||||
|
},
|
||||||
|
"request": {
|
||||||
|
"fhir_comments": [
|
||||||
|
" transaction details "
|
||||||
|
],
|
||||||
|
"method": "PUT",
|
||||||
|
"_method": {
|
||||||
|
"fhir_comments": [
|
||||||
|
" PUT to [base]/Patient/[id] - that's an update "
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"url": "Patient/123",
|
||||||
|
"_url": {
|
||||||
|
"fhir_comments": [
|
||||||
|
" actually, in a transaction, you don't specify the [base], \n so [base]/Patient becomes just 'Patient': "
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fullUrl": "urn:uuid:74891afc-ed52-42a2-bcd7-f13d9b60f096",
|
||||||
|
"resource": {
|
||||||
|
"resourceType": "Patient",
|
||||||
|
"text": {
|
||||||
|
"fhir_comments": [
|
||||||
|
" no id for conditional update operations ",
|
||||||
|
" and no metadata on this resource, though it would be valid "
|
||||||
|
],
|
||||||
|
"status": "generated",
|
||||||
|
"div": "<div>Some narrative</div>"
|
||||||
|
},
|
||||||
|
"active": true,
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"use": "official",
|
||||||
|
"family": [
|
||||||
|
"Chalmers"
|
||||||
|
],
|
||||||
|
"given": [
|
||||||
|
"Peter",
|
||||||
|
"James"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"gender": "male",
|
||||||
|
"birthDate": "1974-12-25"
|
||||||
|
},
|
||||||
|
"request": {
|
||||||
|
"fhir_comments": [
|
||||||
|
" transaction details "
|
||||||
|
],
|
||||||
|
"method": "PUT",
|
||||||
|
"_method": {
|
||||||
|
"fhir_comments": [
|
||||||
|
" PUT to [base]/Patient?search_params - that's a conditional update "
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"url": "Patient?identifier=234234",
|
||||||
|
"_url": {
|
||||||
|
"fhir_comments": [
|
||||||
|
" actually, in a transaction, you don't specify the [base], \n so [base]/Patient?params becomes just 'Patient?params': "
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fhir_comments": [
|
||||||
|
" a different kind of conditional update: version dependent "
|
||||||
|
],
|
||||||
|
"fullUrl": "http://example.org/fhir/Patient/123a",
|
||||||
|
"resource": {
|
||||||
|
"resourceType": "Patient",
|
||||||
|
"id": "123a",
|
||||||
|
"text": {
|
||||||
|
"status": "generated",
|
||||||
|
"div": "<div>Some narrative</div>"
|
||||||
|
},
|
||||||
|
"active": true,
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"use": "official",
|
||||||
|
"family": [
|
||||||
|
"Chalmers"
|
||||||
|
],
|
||||||
|
"given": [
|
||||||
|
"Peter",
|
||||||
|
"James"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"gender": "male",
|
||||||
|
"birthDate": "1974-12-25"
|
||||||
|
},
|
||||||
|
"request": {
|
||||||
|
"method": "PUT",
|
||||||
|
"url": "Patient/123a",
|
||||||
|
"ifMatch": "W/\"2\"",
|
||||||
|
"_ifMatch": {
|
||||||
|
"fhir_comments": [
|
||||||
|
" this will only succeed if the source version is correct: "
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"request": {
|
||||||
|
"fhir_comments": [
|
||||||
|
" a simple delete operation ",
|
||||||
|
" no resource in this case ",
|
||||||
|
" transaction details "
|
||||||
|
],
|
||||||
|
"method": "DELETE",
|
||||||
|
"_method": {
|
||||||
|
"fhir_comments": [
|
||||||
|
" DELETE to [base]/Patient/[id]- that's a delete operation "
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"url": "Patient/234",
|
||||||
|
"_url": {
|
||||||
|
"fhir_comments": [
|
||||||
|
" actually, in a transaction, you don't specify the [base], \n so [base]/Patient/234 becomes just 'Patient/234': ",
|
||||||
|
" btw, couldn't re-use Patient/123 for the delete, since \n the transaction couldn't do two different operations on the\n same resource "
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"request": {
|
||||||
|
"fhir_comments": [
|
||||||
|
" a conditional delete operation ",
|
||||||
|
" no resource in this case ",
|
||||||
|
" transaction details "
|
||||||
|
],
|
||||||
|
"method": "DELETE",
|
||||||
|
"_method": {
|
||||||
|
"fhir_comments": [
|
||||||
|
" DELETE to [base]/Patient?params- that's a conditional delete operation "
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"url": "Patient?identifier=123456",
|
||||||
|
"_url": {
|
||||||
|
"fhir_comments": [
|
||||||
|
" actually, in a transaction, you don't specify the [base], \n so [base]/Patient?params becomes just 'Patient?params': "
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fullUrl": "urn:uuid:79378cb8-8f58-48e8-a5e8-60ac2755b674",
|
||||||
|
"resource": {
|
||||||
|
"resourceType": "Parameters",
|
||||||
|
"parameter": [
|
||||||
|
{
|
||||||
|
"name": "coding",
|
||||||
|
"valueCoding": {
|
||||||
|
"system": "http://loinc.org",
|
||||||
|
"code": "1963-8"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"request": {
|
||||||
|
"method": "POST",
|
||||||
|
"_method": {
|
||||||
|
"fhir_comments": [
|
||||||
|
" POST to [base]/ValueSet/$lookup - invoking a lookup operation (see Terminology Service) "
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"url": "ValueSet/$lookup"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"request": {
|
||||||
|
"fhir_comments": [
|
||||||
|
" can also do read-only operations. \n \n Note that these do not 'fail' - see discussion on transaction \n atomicity for further information \n "
|
||||||
|
],
|
||||||
|
"method": "GET",
|
||||||
|
"_method": {
|
||||||
|
"fhir_comments": [
|
||||||
|
" GET from [base]/Patient?params - searching for a patient "
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"url": "Patient?name=peter"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"request": {
|
||||||
|
"fhir_comments": [
|
||||||
|
" and an example conditional read: "
|
||||||
|
],
|
||||||
|
"method": "GET",
|
||||||
|
"url": "Patient/12334",
|
||||||
|
"ifNoneMatch": "W/\"4\"",
|
||||||
|
"_ifNoneMatch": {
|
||||||
|
"fhir_comments": [
|
||||||
|
" in practice, you'd only specify one of these "
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ifModifiedSince": "2015-08-31T08:14:33+10:00"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -11,6 +11,8 @@ import org.hl7.fhir.instance.model.Bundle.BundleEntryComponent;
|
||||||
import org.hl7.fhir.instance.model.IdType;
|
import org.hl7.fhir.instance.model.IdType;
|
||||||
import org.hl7.fhir.instance.model.OperationOutcome.IssueSeverity;
|
import org.hl7.fhir.instance.model.OperationOutcome.IssueSeverity;
|
||||||
import org.hl7.fhir.instance.model.ValueSet;
|
import org.hl7.fhir.instance.model.ValueSet;
|
||||||
|
import org.hl7.fhir.instance.model.ValueSet.ConceptSetComponent;
|
||||||
|
import org.hl7.fhir.instance.model.ValueSet.ValueSetExpansionComponent;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
@ -68,8 +70,8 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public org.hl7.fhir.instance.utils.IWorkerContext.ValidationResult validateCode(String theCodeSystem, String theCode, String theDisplay) {
|
public CodeValidationResult validateCode(String theCodeSystem, String theCode, String theDisplay) {
|
||||||
return new org.hl7.fhir.instance.utils.IWorkerContext.ValidationResult(IssueSeverity.INFORMATION, "Unknown code: " + theCodeSystem + " / " + theCode);
|
return new CodeValidationResult(IssueSeverity.INFORMATION, "Unknown code: " + theCodeSystem + " / " + theCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -77,4 +79,9 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ValueSetExpansionComponent expandValueSet(ConceptSetComponent theInclude) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,7 @@ import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
import ca.uhn.fhir.validation.IValidationSupport.CodeValidationResult;
|
||||||
|
|
||||||
public class FhirInstanceValidator extends BaseValidatorBridge implements IValidatorModule {
|
public class FhirInstanceValidator extends BaseValidatorBridge implements IValidatorModule {
|
||||||
|
|
||||||
|
@ -262,27 +263,13 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
||||||
private final class HapiWorkerContext implements IWorkerContext, ValueSetExpanderFactory, ValueSetExpander {
|
private final class HapiWorkerContext implements IWorkerContext, ValueSetExpanderFactory, ValueSetExpander {
|
||||||
private final FhirContext myCtx;
|
private final FhirContext myCtx;
|
||||||
|
|
||||||
// public StructureDefinition getProfile(String theId) {
|
|
||||||
// StructureDefinition retVal = super.getProfile(theId);
|
|
||||||
// if (retVal == null) {
|
|
||||||
// if (theId.startsWith("http://hl7.org/fhir/StructureDefinition/")) {
|
|
||||||
// retVal = loadProfileOrReturnNull(null, getHl7OrgDstu2Ctx(myCtx),
|
|
||||||
// theId.substring("http://hl7.org/fhir/StructureDefinition/".length()));
|
|
||||||
// if (retVal != null) {
|
|
||||||
// seeProfile(theId, retVal);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return retVal;
|
|
||||||
// }
|
|
||||||
|
|
||||||
private HapiWorkerContext(FhirContext theCtx) {
|
private HapiWorkerContext(FhirContext theCtx) {
|
||||||
myCtx = theCtx;
|
myCtx = theCtx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValueSetExpansionComponent expandVS(ConceptSetComponent theInc) {
|
public ValueSetExpansionComponent expandVS(ConceptSetComponent theInc) {
|
||||||
throw new UnsupportedOperationException();
|
return myValidationSupport.expandValueSet(theInc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -354,7 +341,11 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidationResult validateCode(String theSystem, String theCode, String theDisplay) {
|
public ValidationResult validateCode(String theSystem, String theCode, String theDisplay) {
|
||||||
return myValidationSupport.validateCode(theSystem, theCode, theDisplay);
|
CodeValidationResult result = myValidationSupport.validateCode(theSystem, theCode, theDisplay);
|
||||||
|
if (result == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new ValidationResult(result.getSeverity(), result.getMessage(), result.asConceptDefinition());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -25,121 +25,148 @@ import ca.uhn.fhir.util.ResourceReferenceInfo;
|
||||||
|
|
||||||
public class FhirQuestionnaireResponseValidator extends BaseValidatorBridge {
|
public class FhirQuestionnaireResponseValidator extends BaseValidatorBridge {
|
||||||
|
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirQuestionnaireResponseValidator.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory
|
||||||
private IResourceLoader myResourceLoader;
|
.getLogger(FhirQuestionnaireResponseValidator.class);
|
||||||
|
private IResourceLoader myResourceLoader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the class which will be used to load linked resources from the <code>QuestionnaireResponse</code>. Specifically, if the <code>QuestionnaireResponse</code> refers to an external (non-contained)
|
* Set the class which will be used to load linked resources from the
|
||||||
* <code>Questionnaire</code>, or to any external (non-contained) <code>ValueSet</code>, the resource loader will be used to fetch those resources during the validation.
|
* <code>QuestionnaireResponse</code>. Specifically, if the
|
||||||
*
|
* <code>QuestionnaireResponse</code> refers to an external (non-contained)
|
||||||
* @param theResourceLoader
|
* <code>Questionnaire</code>, or to any external (non-contained)
|
||||||
* The resourceloader to use. May be <code>null</code> if no resource loader should be used (in which case any <code>QuestionaireResponse</code> with external references will fail to
|
* <code>ValueSet</code>, the resource loader will be used to fetch those
|
||||||
* validate.)
|
* resources during the validation.
|
||||||
*/
|
*
|
||||||
public void setResourceLoader(IResourceLoader theResourceLoader) {
|
* @param theResourceLoader
|
||||||
myResourceLoader = theResourceLoader;
|
* The resourceloader to use. May be <code>null</code> if no resource
|
||||||
}
|
* loader should be used (in which case any
|
||||||
|
* <code>QuestionaireResponse</code> with external references will
|
||||||
|
* fail to validate.)
|
||||||
|
*/
|
||||||
|
public void setResourceLoader(IResourceLoader theResourceLoader) {
|
||||||
|
myResourceLoader = theResourceLoader;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<ValidationMessage> validate(IValidationContext<?> theCtx) {
|
protected List<ValidationMessage> validate(IValidationContext<?> theCtx) {
|
||||||
Object resource = theCtx.getResource();
|
Object resource = theCtx.getResource();
|
||||||
if (!(theCtx.getResource() instanceof IBaseResource)) {
|
if (!(theCtx.getResource() instanceof IBaseResource)) {
|
||||||
ourLog.debug("Not validating object of type {}", theCtx.getResource().getClass());
|
ourLog.debug("Not validating object of type {}", theCtx.getResource().getClass());
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resource instanceof QuestionnaireResponse) {
|
if (resource instanceof QuestionnaireResponse) {
|
||||||
return doValidate(theCtx, (QuestionnaireResponse) resource);
|
return doValidate(theCtx, (QuestionnaireResponse) resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
RuntimeResourceDefinition def = theCtx.getFhirContext().getResourceDefinition((IBaseResource) resource);
|
RuntimeResourceDefinition def = theCtx.getFhirContext().getResourceDefinition((IBaseResource) resource);
|
||||||
if ("QuestionnaireResponse".equals(def.getName()) == false) {
|
if ("QuestionnaireResponse".equals(def.getName()) == false) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we have a non-RI structure, convert it
|
* If we have a non-RI structure, convert it
|
||||||
*/
|
*/
|
||||||
|
|
||||||
IParser p = theCtx.getFhirContext().newJsonParser();
|
IParser p = theCtx.getFhirContext().newJsonParser();
|
||||||
String string = p.encodeResourceToString((IBaseResource) resource);
|
String string = p.encodeResourceToString((IBaseResource) resource);
|
||||||
QuestionnaireResponse qa = p.parseResource(QuestionnaireResponse.class, string);
|
QuestionnaireResponse qa = p.parseResource(QuestionnaireResponse.class, string);
|
||||||
|
|
||||||
return doValidate(theCtx, qa);
|
return doValidate(theCtx, qa);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<ValidationMessage> doValidate(IValidationContext<?> theValCtx, QuestionnaireResponse theResource) {
|
private List<ValidationMessage> doValidate(IValidationContext<?> theValCtx, QuestionnaireResponse theResource) {
|
||||||
|
|
||||||
WorkerContext workerCtx = new WorkerContext();
|
WorkerContext workerCtx = new WorkerContext();
|
||||||
ArrayList<ValidationMessage> retVal = new ArrayList<ValidationMessage>();
|
ArrayList<ValidationMessage> retVal = new ArrayList<ValidationMessage>();
|
||||||
|
|
||||||
if (!loadReferences(theResource, workerCtx, theValCtx, retVal)) {
|
if (!loadReferences(theResource, workerCtx, theValCtx, retVal)) {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
QuestionnaireResponseValidator val = new QuestionnaireResponseValidator(workerCtx);
|
QuestionnaireResponseValidator val = new QuestionnaireResponseValidator(workerCtx);
|
||||||
|
|
||||||
val.validate(retVal, theResource);
|
val.validate(retVal, theResource);
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean loadReferences(IBaseResource theResource, WorkerContext theWorkerCtx, IValidationContext<?> theValCtx, ArrayList<ValidationMessage> theMessages) {
|
private boolean loadReferences(IBaseResource theResource, WorkerContext theWorkerCtx, IValidationContext<?> theValCtx,
|
||||||
List<ResourceReferenceInfo> refs = theValCtx.getFhirContext().newTerser().getAllResourceReferences(theResource);
|
ArrayList<ValidationMessage> theMessages) {
|
||||||
|
List<ResourceReferenceInfo> refs = theValCtx.getFhirContext().newTerser().getAllResourceReferences(theResource);
|
||||||
|
|
||||||
List<IBaseResource> newResources = new ArrayList<IBaseResource>();
|
List<IBaseResource> newResources = new ArrayList<IBaseResource>();
|
||||||
|
|
||||||
for (ResourceReferenceInfo nextRefInfo : refs) {
|
for (ResourceReferenceInfo nextRefInfo : refs) {
|
||||||
IIdType nextRef = nextRefInfo.getResourceReference().getReferenceElement();
|
IIdType nextRef = nextRefInfo.getResourceReference().getReferenceElement();
|
||||||
String resourceType = nextRef.getResourceType();
|
String resourceType = nextRef.getResourceType();
|
||||||
if (isBlank(resourceType)) {
|
if (nextRef.isLocal()) {
|
||||||
theMessages.add(new ValidationMessage(Source.QuestionnaireResponseValidator, org.hl7.fhir.instance.model.OperationOutcome.IssueType.INVALID, "Invalid reference '" + nextRef.getValue() + "' - Does not identify resource type", IssueSeverity.FATAL));
|
IBaseResource resource = nextRefInfo.getResourceReference().getResource();
|
||||||
} else if ("ValueSet".equals(resourceType)) {
|
if (resource instanceof ValueSet) {
|
||||||
if (!theWorkerCtx.getValueSets().containsKey(nextRef.getValue())) {
|
theWorkerCtx.getValueSets().put(nextRef.getValue(), (ValueSet) resource);
|
||||||
ValueSet resource = tryToLoad(ValueSet.class, nextRef, theMessages);
|
newResources.add(resource);
|
||||||
if (resource == null) {
|
} else if (resource instanceof Questionnaire) {
|
||||||
return false;
|
theWorkerCtx.getQuestionnaires().put(nextRef.getValue(), (Questionnaire) resource);
|
||||||
}
|
newResources.add(resource);
|
||||||
theWorkerCtx.getValueSets().put(nextRef.getValue(), resource);
|
} else if (resource == null) {
|
||||||
newResources.add(resource);
|
theMessages.add(new ValidationMessage(Source.QuestionnaireResponseValidator,
|
||||||
}
|
org.hl7.fhir.instance.model.OperationOutcome.IssueType.INVALID,
|
||||||
} else if ("Questionnaire".equals(resourceType)) {
|
"Invalid reference '" + nextRef.getValue() + "' - No contained resource with this ID found", IssueSeverity.FATAL));
|
||||||
if (!theWorkerCtx.getQuestionnaires().containsKey(nextRef.getValue())) {
|
}
|
||||||
Questionnaire resource = tryToLoad(Questionnaire.class, nextRef, theMessages);
|
} else if (isBlank(resourceType)) {
|
||||||
if (resource == null) {
|
theMessages.add(new ValidationMessage(Source.QuestionnaireResponseValidator,
|
||||||
return false;
|
org.hl7.fhir.instance.model.OperationOutcome.IssueType.INVALID,
|
||||||
}
|
"Invalid reference '" + nextRef.getValue() + "' - Does not identify resource type", IssueSeverity.FATAL));
|
||||||
theWorkerCtx.getQuestionnaires().put(nextRef.getValue(), resource);
|
} else if ("ValueSet".equals(resourceType)) {
|
||||||
newResources.add(resource);
|
if (!theWorkerCtx.getValueSets().containsKey(nextRef.getValue())) {
|
||||||
}
|
ValueSet resource = tryToLoad(ValueSet.class, nextRef, theMessages);
|
||||||
}
|
if (resource == null) {
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
|
theWorkerCtx.getValueSets().put(nextRef.getValue(), resource);
|
||||||
|
newResources.add(resource);
|
||||||
|
}
|
||||||
|
} else if ("Questionnaire".equals(resourceType)) {
|
||||||
|
if (!theWorkerCtx.getQuestionnaires().containsKey(nextRef.getValue())) {
|
||||||
|
Questionnaire resource = tryToLoad(Questionnaire.class, nextRef, theMessages);
|
||||||
|
if (resource == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
theWorkerCtx.getQuestionnaires().put(nextRef.getValue(), resource);
|
||||||
|
newResources.add(resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (IBaseResource nextAddedResource : newResources) {
|
for (IBaseResource nextAddedResource : newResources) {
|
||||||
boolean outcome = loadReferences(nextAddedResource, theWorkerCtx, theValCtx, theMessages);
|
boolean outcome = loadReferences(nextAddedResource, theWorkerCtx, theValCtx, theMessages);
|
||||||
if (!outcome) {
|
if (!outcome) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T extends IBaseResource> T tryToLoad(Class<T> theType, IIdType theReference, List<ValidationMessage> theMessages) {
|
private <T extends IBaseResource> T tryToLoad(Class<T> theType, IIdType theReference,
|
||||||
if (myResourceLoader == null) {
|
List<ValidationMessage> theMessages) {
|
||||||
theMessages.add(new ValidationMessage().setLevel(IssueSeverity.FATAL).setMessage("No resource loader present, could not load " + theReference));
|
if (myResourceLoader == null) {
|
||||||
return null;
|
theMessages.add(new ValidationMessage().setLevel(IssueSeverity.FATAL)
|
||||||
}
|
.setMessage("No resource loader present, could not load " + theReference));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
T retVal = myResourceLoader.load(theType, theReference);
|
T retVal = myResourceLoader.load(theType, theReference);
|
||||||
if (retVal == null) {
|
if (retVal == null) {
|
||||||
throw new IllegalStateException("ResourceLoader returned null. This is a bug with the resourceloader. Reference was: " + theReference);
|
throw new IllegalStateException(
|
||||||
}
|
"ResourceLoader returned null. This is a bug with the resourceloader. Reference was: " + theReference);
|
||||||
return retVal;
|
}
|
||||||
} catch (ResourceNotFoundException e) {
|
return retVal;
|
||||||
theMessages.add(new ValidationMessage().setLevel(IssueSeverity.FATAL).setMessage("Reference could not be found: " + theReference));
|
} catch (ResourceNotFoundException e) {
|
||||||
return null;
|
theMessages.add(new ValidationMessage().setLevel(IssueSeverity.FATAL)
|
||||||
}
|
.setMessage("Reference could not be found: " + theReference));
|
||||||
}
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
package ca.uhn.fhir.validation;
|
package ca.uhn.fhir.validation;
|
||||||
|
|
||||||
import org.hl7.fhir.instance.model.ValueSet;
|
import org.hl7.fhir.instance.model.ValueSet;
|
||||||
|
import org.hl7.fhir.instance.model.OperationOutcome.IssueSeverity;
|
||||||
|
import org.hl7.fhir.instance.model.ValueSet.ConceptDefinitionComponent;
|
||||||
|
import org.hl7.fhir.instance.model.ValueSet.ConceptSetComponent;
|
||||||
|
import org.hl7.fhir.instance.model.ValueSet.ValueSetExpansionComponent;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
@ -8,15 +12,22 @@ import ca.uhn.fhir.context.FhirContext;
|
||||||
public interface IValidationSupport {
|
public interface IValidationSupport {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns <code>true</code> if codes in the given code system can be
|
* Expands the given portion of a ValueSet
|
||||||
* validated
|
*
|
||||||
|
* @param theInclude
|
||||||
|
* The portion to include
|
||||||
|
* @return The expansion
|
||||||
|
*/
|
||||||
|
ValueSetExpansionComponent expandValueSet(ConceptSetComponent theInclude);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch a code system by ID
|
||||||
*
|
*
|
||||||
* @param theSystem
|
* @param theSystem
|
||||||
* The URI for the code system, e.g. <code>"http://loinc.org"</code>
|
* The code system
|
||||||
* @return Returns <code>true</code> if codes in the given code system can be
|
* @return The valueset (must not be null, but can be an empty ValueSet)
|
||||||
* validated
|
|
||||||
*/
|
*/
|
||||||
boolean isCodeSystemSupported(String theSystem);
|
ValueSet fetchCodeSystem(String theSystem);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a resource needed by the validation (a StructureDefinition, or a
|
* Loads a resource needed by the validation (a StructureDefinition, or a
|
||||||
|
@ -33,6 +44,17 @@ public interface IValidationSupport {
|
||||||
*/
|
*/
|
||||||
<T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri);
|
<T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns <code>true</code> if codes in the given code system can be expanded
|
||||||
|
* or validated
|
||||||
|
*
|
||||||
|
* @param theSystem
|
||||||
|
* The URI for the code system, e.g. <code>"http://loinc.org"</code>
|
||||||
|
* @return Returns <code>true</code> if codes in the given code system can be
|
||||||
|
* validated
|
||||||
|
*/
|
||||||
|
boolean isCodeSystemSupported(String theSystem);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates that the given code exists and if possible returns a display
|
* Validates that the given code exists and if possible returns a display
|
||||||
* name. This method is called to check codes which are found in "example"
|
* name. This method is called to check codes which are found in "example"
|
||||||
|
@ -46,16 +68,48 @@ public interface IValidationSupport {
|
||||||
* The display name, if it should also be validated
|
* The display name, if it should also be validated
|
||||||
* @return Returns a validation result object
|
* @return Returns a validation result object
|
||||||
*/
|
*/
|
||||||
org.hl7.fhir.instance.utils.IWorkerContext.ValidationResult validateCode(String theCodeSystem, String theCode,
|
CodeValidationResult validateCode(String theCodeSystem, String theCode, String theDisplay);
|
||||||
String theDisplay);
|
|
||||||
|
|
||||||
/**
|
public class CodeValidationResult {
|
||||||
* Fetch a code system by ID
|
private ConceptDefinitionComponent definition;
|
||||||
*
|
private String message;
|
||||||
* @param theSystem
|
private IssueSeverity severity;
|
||||||
* The code system
|
|
||||||
* @return The valueset (must not be null, but can be an empty ValueSet)
|
public CodeValidationResult(ConceptDefinitionComponent definition) {
|
||||||
*/
|
this.definition = definition;
|
||||||
ValueSet fetchCodeSystem(String theSystem);
|
}
|
||||||
|
|
||||||
|
public CodeValidationResult(IssueSeverity severity, String message) {
|
||||||
|
this.severity = severity;
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CodeValidationResult(IssueSeverity severity, String message, ConceptDefinitionComponent definition) {
|
||||||
|
this.severity = severity;
|
||||||
|
this.message = message;
|
||||||
|
this.definition = definition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConceptDefinitionComponent asConceptDefinition() {
|
||||||
|
return definition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplay() {
|
||||||
|
return definition == null ? "??" : definition.getDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IssueSeverity getSeverity() {
|
||||||
|
return severity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOk() {
|
||||||
|
return definition != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
package ca.uhn.fhir.validation;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hl7.fhir.instance.model.ValueSet;
|
||||||
|
import org.hl7.fhir.instance.model.ValueSet.ConceptSetComponent;
|
||||||
|
import org.hl7.fhir.instance.model.ValueSet.ValueSetExpansionComponent;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
|
||||||
|
public class ValidationSupportChain implements IValidationSupport {
|
||||||
|
|
||||||
|
private List<IValidationSupport> myChain;
|
||||||
|
|
||||||
|
public ValidationSupportChain(IValidationSupport... theValidationSupportModules) {
|
||||||
|
myChain = Arrays.asList(theValidationSupportModules);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ValueSetExpansionComponent expandValueSet(ConceptSetComponent theInclude) {
|
||||||
|
for (IValidationSupport next : myChain) {
|
||||||
|
if (next.isCodeSystemSupported(theInclude.getSystem())) {
|
||||||
|
return next.expandValueSet(theInclude);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return myChain.get(0).expandValueSet(theInclude);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ValueSet fetchCodeSystem(String theSystem) {
|
||||||
|
for (IValidationSupport next : myChain) {
|
||||||
|
ValueSet retVal = next.fetchCodeSystem(theSystem);
|
||||||
|
if (retVal != null) {
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
|
||||||
|
for (IValidationSupport next : myChain) {
|
||||||
|
T retVal = next.fetchResource(theContext, theClass, theUri);
|
||||||
|
if (retVal != null) {
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCodeSystemSupported(String theSystem) {
|
||||||
|
for (IValidationSupport next : myChain) {
|
||||||
|
if (next.isCodeSystemSupported(theSystem)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CodeValidationResult validateCode(String theCodeSystem, String theCode, String theDisplay) {
|
||||||
|
for (IValidationSupport next : myChain) {
|
||||||
|
if (next.isCodeSystemSupported(theCodeSystem)) {
|
||||||
|
return next.validateCode(theCodeSystem, theCode, theDisplay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return myChain.get(0).validateCode(theCodeSystem, theCode, theDisplay);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -54,7 +54,7 @@ import org.hl7.fhir.instance.utils.ToolingExtensions;
|
||||||
import org.hl7.fhir.utilities.Utilities;
|
import org.hl7.fhir.utilities.Utilities;
|
||||||
|
|
||||||
public class ValueSetExpanderSimple implements ValueSetExpander {
|
public class ValueSetExpanderSimple implements ValueSetExpander {
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValueSetExpanderSimple.class);
|
||||||
private IWorkerContext context;
|
private IWorkerContext context;
|
||||||
private List<ValueSetExpansionContainsComponent> codes = new ArrayList<ValueSet.ValueSetExpansionContainsComponent>();
|
private List<ValueSetExpansionContainsComponent> codes = new ArrayList<ValueSet.ValueSetExpansionContainsComponent>();
|
||||||
private Map<String, ValueSetExpansionContainsComponent> map = new HashMap<String, ValueSet.ValueSetExpansionContainsComponent>();
|
private Map<String, ValueSetExpansionContainsComponent> map = new HashMap<String, ValueSet.ValueSetExpansionContainsComponent>();
|
||||||
|
@ -88,6 +88,7 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
|
||||||
}
|
}
|
||||||
return new ValueSetExpansionOutcome(focus, null);
|
return new ValueSetExpansionOutcome(focus, null);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
ourLog.error("Failure during expansion", e);
|
||||||
// well, we couldn't expand, so we'll return an interface to a checker that can check membership of the set
|
// well, we couldn't expand, so we'll return an interface to a checker that can check membership of the set
|
||||||
// that might fail too, but it might not, later.
|
// that might fail too, but it might not, later.
|
||||||
return new ValueSetExpansionOutcome(new ValueSetCheckerSimple(source, factory, context), e.getMessage());
|
return new ValueSetExpansionOutcome(new ValueSetCheckerSimple(source, factory, context), e.getMessage());
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package org.hl7.fhir.instance.validation;
|
package org.hl7.fhir.instance.validation;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
@ -282,7 +284,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||||
rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, isAbsolute(system), "Coding.system must be an absolute reference, not a local reference");
|
rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, isAbsolute(system), "Coding.system must be an absolute reference, not a local reference");
|
||||||
|
|
||||||
if (system != null && code != null) {
|
if (system != null && code != null) {
|
||||||
if (checkCode(errors, element, path, code, system, display))
|
if (checkCode(errors, element, path, code, system, display)) {
|
||||||
if (context != null && context.getBinding() != null) {
|
if (context != null && context.getBinding() != null) {
|
||||||
ElementDefinitionBindingComponent binding = context.getBinding();
|
ElementDefinitionBindingComponent binding = context.getBinding();
|
||||||
if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, binding != null, "Binding for " + path + " missing")) {
|
if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, binding != null, "Binding for " + path + " missing")) {
|
||||||
|
@ -318,6 +320,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1746,44 +1749,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||||
// 5. inspect each child for validity
|
// 5. inspect each child for validity
|
||||||
for (ElementInfo ei : children) {
|
for (ElementInfo ei : children) {
|
||||||
if (ei.definition != null) {
|
if (ei.definition != null) {
|
||||||
String type = null;
|
String type = determineType(ei.definition, ei.name, ei.line(), ei.col(), errors, stack, profile);
|
||||||
ElementDefinition typeDefn = null;
|
ElementDefinition typeDefn = null;
|
||||||
if (ei.definition.getType().size() == 1 && !ei.definition.getType().get(0).getCode().equals("*") && !ei.definition.getType().get(0).getCode().equals("Element")
|
if (isBlank(type) && ei.definition.getNameReference() != null) {
|
||||||
&& !ei.definition.getType().get(0).getCode().equals("BackboneElement"))
|
|
||||||
type = ei.definition.getType().get(0).getCode();
|
|
||||||
else if (ei.definition.getType().size() == 1 && ei.definition.getType().get(0).getCode().equals("*")) {
|
|
||||||
String prefix = tail(ei.definition.getPath());
|
|
||||||
assert prefix.endsWith("[x]");
|
|
||||||
type = ei.name.substring(prefix.length() - 3);
|
|
||||||
if (isPrimitiveType(type))
|
|
||||||
type = Utilities.uncapitalize(type);
|
|
||||||
} else if (ei.definition.getType().size() > 1) {
|
|
||||||
|
|
||||||
String prefix = tail(ei.definition.getPath());
|
|
||||||
assert typesAreAllReference(ei.definition.getType()) || prefix.endsWith("[x]") : prefix;
|
|
||||||
|
|
||||||
prefix = prefix.substring(0, prefix.length() - 3);
|
|
||||||
for (TypeRefComponent t : ei.definition.getType())
|
|
||||||
if ((prefix + Utilities.capitalize(t.getCode())).equals(ei.name))
|
|
||||||
type = t.getCode();
|
|
||||||
if (type == null) {
|
|
||||||
TypeRefComponent trc = ei.definition.getType().get(0);
|
|
||||||
if (trc.getCode().equals("Reference"))
|
|
||||||
type = "Reference";
|
|
||||||
else
|
|
||||||
rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), stack.getLiteralPath(), false,
|
|
||||||
"The element " + ei.name + " is illegal. Valid types at this point are " + describeTypes(ei.definition.getType()));
|
|
||||||
}
|
|
||||||
} else if (ei.definition.getNameReference() != null) {
|
|
||||||
typeDefn = resolveNameReference(profile.getSnapshot(), ei.definition.getNameReference());
|
typeDefn = resolveNameReference(profile.getSnapshot(), ei.definition.getNameReference());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type != null) {
|
|
||||||
if (type.startsWith("@")) {
|
|
||||||
ei.definition = findElement(profile, type.substring(1));
|
|
||||||
type = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
NodeStack localStack = stack.push(ei.element, ei.count, ei.definition, type == null ? typeDefn : resolveType(type));
|
NodeStack localStack = stack.push(ei.element, ei.count, ei.definition, type == null ? typeDefn : resolveType(type));
|
||||||
String localStackLiterapPath = localStack.getLiteralPath();
|
String localStackLiterapPath = localStack.getLiteralPath();
|
||||||
String eiPath = ei.path;
|
String eiPath = ei.path;
|
||||||
|
@ -1793,21 +1763,36 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||||
if (isPrimitiveType(type))
|
if (isPrimitiveType(type))
|
||||||
checkPrimitive(errors, ei.path, type, ei.definition, ei.element);
|
checkPrimitive(errors, ei.path, type, ei.definition, ei.element);
|
||||||
else {
|
else {
|
||||||
if (type.equals("Identifier"))
|
if (type.equals("Identifier")) {
|
||||||
checkIdentifier(errors, ei.path, ei.element, ei.definition);
|
checkIdentifier(errors, ei.path, ei.element, ei.definition);
|
||||||
else if (type.equals("Coding"))
|
} else if (type.equals("Coding")) {
|
||||||
checkCoding(errors, ei.path, ei.element, profile, ei.definition);
|
/*
|
||||||
else if (type.equals("CodeableConcept"))
|
* We don't check coding children within a CodeableConcept, because
|
||||||
|
* checkCodeableConcept() already verifies the codings and the binding
|
||||||
|
* doesn't actually make it into checkCoding anyhow.
|
||||||
|
*/
|
||||||
|
boolean skip = false;
|
||||||
|
if (localStack.getParent() != null && localStack.getParent() != null) {
|
||||||
|
String parentType = determineType(localStack.getParent().getDefinition(), "", -1, -1, errors, stack, profile);
|
||||||
|
if ("CodeableConcept".equals(parentType)) {
|
||||||
|
skip = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!skip) {
|
||||||
|
checkCoding(errors, ei.path, ei.element, profile, ei.definition);
|
||||||
|
}
|
||||||
|
} else if (type.equals("CodeableConcept")) {
|
||||||
checkCodeableConcept(errors, ei.path, ei.element, profile, ei.definition);
|
checkCodeableConcept(errors, ei.path, ei.element, profile, ei.definition);
|
||||||
else if (type.equals("Reference"))
|
} else if (type.equals("Reference")) {
|
||||||
checkReference(errors, ei.path, ei.element, profile, ei.definition, actualType, localStack);
|
checkReference(errors, ei.path, ei.element, profile, ei.definition, actualType, localStack);
|
||||||
|
}
|
||||||
if (type.equals("Extension"))
|
|
||||||
|
if (type.equals("Extension")) {
|
||||||
checkExtension(errors, ei.path, ei.element, ei.definition, profile, localStack);
|
checkExtension(errors, ei.path, ei.element, ei.definition, profile, localStack);
|
||||||
else if (type.equals("Resource"))
|
} else if (type.equals("Resource")) {
|
||||||
validateContains(errors, ei.path, ei.definition, definition, ei.element, localStack, !isBundleEntry(ei.path)); // if
|
validateContains(errors, ei.path, ei.definition, definition, ei.element, localStack, !isBundleEntry(ei.path)); // if
|
||||||
// (str.matches(".*([.,/])work\\1$"))
|
// (str.matches(".*([.,/])work\\1$"))
|
||||||
else {
|
} else {
|
||||||
StructureDefinition p = getProfileForType(type);
|
StructureDefinition p = getProfileForType(type);
|
||||||
if (rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.path, p != null, "Unknown type " + type)) {
|
if (rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.path, p != null, "Unknown type " + type)) {
|
||||||
validateElement(errors, p, p.getSnapshot().getElement().get(0), profile, ei.definition, ei.element, type, localStack);
|
validateElement(errors, p, p.getSnapshot().getElement().get(0), profile, ei.definition, ei.element, type, localStack);
|
||||||
|
@ -1822,6 +1807,50 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String determineType(ElementDefinition definition, String name, int line, int col, List<ValidationMessage> theErrors, NodeStack theStack, StructureDefinition theProfile) {
|
||||||
|
String retVal = null;
|
||||||
|
if (definition.getType().size() == 1 && !definition.getType().get(0).getCode().equals("*") && !definition.getType().get(0).getCode().equals("Element")
|
||||||
|
&& !definition.getType().get(0).getCode().equals("BackboneElement")) {
|
||||||
|
retVal = definition.getType().get(0).getCode();
|
||||||
|
} else if (definition.getType().size() == 1 && definition.getType().get(0).getCode().equals("*")) {
|
||||||
|
String prefix = tail(definition.getPath());
|
||||||
|
assert prefix.endsWith("[x]");
|
||||||
|
retVal = name.substring(prefix.length() - 3);
|
||||||
|
if (isPrimitiveType(retVal)) {
|
||||||
|
retVal = Utilities.uncapitalize(retVal);
|
||||||
|
}
|
||||||
|
} else if (definition.getType().size() > 1) {
|
||||||
|
|
||||||
|
String prefix = tail(definition.getPath());
|
||||||
|
assert typesAreAllReference(definition.getType()) || prefix.endsWith("[x]") : prefix;
|
||||||
|
|
||||||
|
prefix = prefix.substring(0, prefix.length() - 3);
|
||||||
|
for (TypeRefComponent t : definition.getType()) {
|
||||||
|
if ((prefix + Utilities.capitalize(t.getCode())).equals(name)) {
|
||||||
|
retVal = t.getCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (retVal == null) {
|
||||||
|
TypeRefComponent trc = definition.getType().get(0);
|
||||||
|
if (trc.getCode().equals("Reference")) {
|
||||||
|
retVal = "Reference";
|
||||||
|
} else {
|
||||||
|
rule(theErrors, IssueType.STRUCTURE, line, col, theStack.getLiteralPath(), false,
|
||||||
|
"The element " + name + " is illegal. Valid types at this point are " + describeTypes(definition.getType()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (retVal != null) {
|
||||||
|
if (retVal.startsWith("@")) {
|
||||||
|
definition = findElement(theProfile, retVal.substring(1));
|
||||||
|
retVal = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
private void validateMessage(List<ValidationMessage> errors, WrapperElement bundle) {
|
private void validateMessage(List<ValidationMessage> errors, WrapperElement bundle) {
|
||||||
// TODO Auto-generated method stub
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
@ -2378,6 +2407,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||||
return logicalPaths == null ? new ArrayList<String>() : logicalPaths;
|
return logicalPaths == null ? new ArrayList<String>() : logicalPaths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public NodeStack getParent() {
|
||||||
|
return this.parent;
|
||||||
|
}
|
||||||
|
|
||||||
private ElementDefinition getType() {
|
private ElementDefinition getType() {
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,9 @@ import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.hl7.fhir.instance.model.CodeType;
|
import org.hl7.fhir.instance.model.CodeType;
|
||||||
|
@ -18,14 +20,16 @@ import org.hl7.fhir.instance.model.Observation.ObservationStatus;
|
||||||
import org.hl7.fhir.instance.model.StringType;
|
import org.hl7.fhir.instance.model.StringType;
|
||||||
import org.hl7.fhir.instance.model.ValueSet;
|
import org.hl7.fhir.instance.model.ValueSet;
|
||||||
import org.hl7.fhir.instance.model.ValueSet.ConceptDefinitionComponent;
|
import org.hl7.fhir.instance.model.ValueSet.ConceptDefinitionComponent;
|
||||||
|
import org.hl7.fhir.instance.model.ValueSet.ConceptSetComponent;
|
||||||
|
import org.hl7.fhir.instance.model.ValueSet.ValueSetExpansionComponent;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mockito.invocation.InvocationOnMock;
|
import org.mockito.invocation.InvocationOnMock;
|
||||||
import org.mockito.stubbing.Answer;
|
import org.mockito.stubbing.Answer;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.validation.IValidationSupport.CodeValidationResult;
|
||||||
|
|
||||||
public class FhirInstanceValidatorTest {
|
public class FhirInstanceValidatorTest {
|
||||||
|
|
||||||
|
@ -37,6 +41,7 @@ public class FhirInstanceValidatorTest {
|
||||||
|
|
||||||
private FhirValidator myVal;
|
private FhirValidator myVal;
|
||||||
private ArrayList<String> myValidConcepts;
|
private ArrayList<String> myValidConcepts;
|
||||||
|
private Map<String, ValueSetExpansionComponent> mySupportedCodeSystemsForExpansion;
|
||||||
|
|
||||||
private void addValidConcept(String theSystem, String theCode) {
|
private void addValidConcept(String theSystem, String theCode) {
|
||||||
myValidConcepts.add(theSystem + "___" + theCode);
|
myValidConcepts.add(theSystem + "___" + theCode);
|
||||||
|
@ -52,39 +57,57 @@ public class FhirInstanceValidatorTest {
|
||||||
myInstanceVal = new FhirInstanceValidator();
|
myInstanceVal = new FhirInstanceValidator();
|
||||||
myVal.registerValidatorModule(myInstanceVal);
|
myVal.registerValidatorModule(myInstanceVal);
|
||||||
|
|
||||||
|
mySupportedCodeSystemsForExpansion = new HashMap<String, ValueSet.ValueSetExpansionComponent>();
|
||||||
|
|
||||||
myValidConcepts = new ArrayList<String>();
|
myValidConcepts = new ArrayList<String>();
|
||||||
|
|
||||||
myMockSupport = mock(IValidationSupport.class);
|
myMockSupport = mock(IValidationSupport.class);
|
||||||
|
when(myMockSupport.expandValueSet(any(ConceptSetComponent.class))).thenAnswer(new Answer<ValueSetExpansionComponent>() {
|
||||||
|
@Override
|
||||||
|
public ValueSetExpansionComponent answer(InvocationOnMock theInvocation) throws Throwable {
|
||||||
|
ConceptSetComponent arg = (ConceptSetComponent)theInvocation.getArguments()[0];
|
||||||
|
ValueSetExpansionComponent retVal = mySupportedCodeSystemsForExpansion.get(arg.getSystem());
|
||||||
|
if (retVal == null) {
|
||||||
|
retVal = myDefaultValidationSupport.expandValueSet(arg);
|
||||||
|
}
|
||||||
|
ourLog.info("expandValueSet({}) : {}", new Object[] { theInvocation.getArguments()[0], retVal });
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
});
|
||||||
when(myMockSupport.isCodeSystemSupported(any(String.class))).thenAnswer(new Answer<Boolean>() {
|
when(myMockSupport.isCodeSystemSupported(any(String.class))).thenAnswer(new Answer<Boolean>() {
|
||||||
@Override
|
@Override
|
||||||
public Boolean answer(InvocationOnMock theInvocation) throws Throwable {
|
public Boolean answer(InvocationOnMock theInvocation) throws Throwable {
|
||||||
boolean retVal = myDefaultValidationSupport.isCodeSystemSupported((String) theInvocation.getArguments()[0]);
|
boolean retVal = mySupportedCodeSystemsForExpansion.containsKey(theInvocation.getArguments()[0]);
|
||||||
ourLog.info("isCodeSystemSupported({}) : {}", new Object[] { theInvocation.getArguments()[0], retVal });
|
ourLog.info("isCodeSystemSupported({}) : {}", new Object[] { theInvocation.getArguments()[0], retVal });
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
when(myMockSupport.fetchResource(any(FhirContext.class), any(Class.class), any(String.class))).thenAnswer(new Answer<IBaseResource>() {
|
when(myMockSupport.fetchResource(any(FhirContext.class), any(Class.class), any(String.class)))
|
||||||
@Override
|
.thenAnswer(new Answer<IBaseResource>() {
|
||||||
public IBaseResource answer(InvocationOnMock theInvocation) throws Throwable {
|
|
||||||
IBaseResource retVal = myDefaultValidationSupport.fetchResource((FhirContext) theInvocation.getArguments()[0],
|
|
||||||
(Class<IBaseResource>) theInvocation.getArguments()[1], (String) theInvocation.getArguments()[2]);
|
|
||||||
ourLog.info("fetchResource({}, {}) : {}", new Object[] { theInvocation.getArguments()[1], theInvocation.getArguments()[2], retVal });
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
when(myMockSupport.validateCode(any(String.class), any(String.class), any(String.class)))
|
|
||||||
.thenAnswer(new Answer<org.hl7.fhir.instance.utils.IWorkerContext.ValidationResult>() {
|
|
||||||
@Override
|
@Override
|
||||||
public org.hl7.fhir.instance.utils.IWorkerContext.ValidationResult answer(InvocationOnMock theInvocation) throws Throwable {
|
public IBaseResource answer(InvocationOnMock theInvocation) throws Throwable {
|
||||||
|
IBaseResource retVal = myDefaultValidationSupport.fetchResource(
|
||||||
|
(FhirContext) theInvocation.getArguments()[0], (Class<IBaseResource>) theInvocation.getArguments()[1],
|
||||||
|
(String) theInvocation.getArguments()[2]);
|
||||||
|
ourLog.info("fetchResource({}, {}) : {}",
|
||||||
|
new Object[] { theInvocation.getArguments()[1], theInvocation.getArguments()[2], retVal });
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
when(myMockSupport.validateCode(any(String.class), any(String.class), any(String.class)))
|
||||||
|
.thenAnswer(new Answer<CodeValidationResult>() {
|
||||||
|
@Override
|
||||||
|
public CodeValidationResult answer(InvocationOnMock theInvocation) throws Throwable {
|
||||||
String system = (String) theInvocation.getArguments()[0];
|
String system = (String) theInvocation.getArguments()[0];
|
||||||
String code = (String) theInvocation.getArguments()[1];
|
String code = (String) theInvocation.getArguments()[1];
|
||||||
org.hl7.fhir.instance.utils.IWorkerContext.ValidationResult retVal;
|
CodeValidationResult retVal;
|
||||||
if (myValidConcepts.contains(system + "___" + code)) {
|
if (myValidConcepts.contains(system + "___" + code)) {
|
||||||
retVal = new org.hl7.fhir.instance.utils.IWorkerContext.ValidationResult(new ConceptDefinitionComponent(new CodeType(code)));
|
retVal = new CodeValidationResult(new ConceptDefinitionComponent(new CodeType(code)));
|
||||||
} else {
|
} else {
|
||||||
retVal = myDefaultValidationSupport.validateCode(system, code, (String) theInvocation.getArguments()[2]);
|
retVal = myDefaultValidationSupport.validateCode(system, code, (String) theInvocation.getArguments()[2]);
|
||||||
}
|
}
|
||||||
ourLog.info("validateCode({}, {}, {}) : {}", new Object[] { system, code, (String) theInvocation.getArguments()[2], retVal });
|
ourLog.info("validateCode({}, {}, {}) : {}",
|
||||||
|
new Object[] { system, code, (String) theInvocation.getArguments()[2], retVal });
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -104,7 +127,8 @@ public class FhirInstanceValidatorTest {
|
||||||
|
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (SingleValidationMessage next : theOutput.getMessages()) {
|
for (SingleValidationMessage next : theOutput.getMessages()) {
|
||||||
ourLog.info("Result {}: {} - {} - {}", new Object[] { index, next.getSeverity(), next.getLocationString(), next.getMessage() });
|
ourLog.info("Result {}: {} - {} - {}",
|
||||||
|
new Object[] { index, next.getSeverity(), next.getLocationString(), next.getMessage() });
|
||||||
index++;
|
index++;
|
||||||
|
|
||||||
if (next.getSeverity() != ResultSeverityEnum.INFORMATION) {
|
if (next.getSeverity() != ResultSeverityEnum.INFORMATION) {
|
||||||
|
@ -115,6 +139,21 @@ public class FhirInstanceValidatorTest {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<SingleValidationMessage> logResultsAndReturnAll(ValidationResult theOutput) {
|
||||||
|
List<SingleValidationMessage> retVal = new ArrayList<SingleValidationMessage>();
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
for (SingleValidationMessage next : theOutput.getMessages()) {
|
||||||
|
ourLog.info("Result {}: {} - {} - {}",
|
||||||
|
new Object[] { index, next.getSeverity(), next.getLocationString(), next.getMessage() });
|
||||||
|
index++;
|
||||||
|
|
||||||
|
retVal.add(next);
|
||||||
|
}
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testValidateRawJsonResource() {
|
public void testValidateRawJsonResource() {
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
|
@ -152,7 +191,8 @@ public class FhirInstanceValidatorTest {
|
||||||
@Test
|
@Test
|
||||||
public void testValidateRawXmlResourceBadAttributes() {
|
public void testValidateRawXmlResourceBadAttributes() {
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
String input = "<Patient xmlns=\"http://hl7.org/fhir\">" + "<id value=\"123\"/>" + "<foo value=\"222\"/>" + "</Patient>";
|
String input = "<Patient xmlns=\"http://hl7.org/fhir\">" + "<id value=\"123\"/>" + "<foo value=\"222\"/>"
|
||||||
|
+ "</Patient>";
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
|
|
||||||
ValidationResult output = myVal.validateWithResult(input);
|
ValidationResult output = myVal.validateWithResult(input);
|
||||||
|
@ -173,7 +213,8 @@ public class FhirInstanceValidatorTest {
|
||||||
|
|
||||||
ValidationResult output = myVal.validateWithResult(input);
|
ValidationResult output = myVal.validateWithResult(input);
|
||||||
assertThat(output.getMessages().size(), greaterThan(0));
|
assertThat(output.getMessages().size(), greaterThan(0));
|
||||||
assertEquals("Element '/f:Observation.status': minimum required = 1, but only found 0", output.getMessages().get(0).getMessage());
|
assertEquals("Element '/f:Observation.status': minimum required = 1, but only found 0",
|
||||||
|
output.getMessages().get(0).getMessage());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,9 +222,9 @@ public class FhirInstanceValidatorTest {
|
||||||
* See #216
|
* See #216
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
@Ignore
|
|
||||||
public void testValidateRawXmlInvalidChoiceName() throws Exception {
|
public void testValidateRawXmlInvalidChoiceName() throws Exception {
|
||||||
String input = IOUtils.toString(FhirInstanceValidator.class.getResourceAsStream("/medicationstatement_invalidelement.xml"));
|
String input = IOUtils
|
||||||
|
.toString(FhirInstanceValidator.class.getResourceAsStream("/medicationstatement_invalidelement.xml"));
|
||||||
ValidationResult output = myVal.validateWithResult(input);
|
ValidationResult output = myVal.validateWithResult(input);
|
||||||
|
|
||||||
List<SingleValidationMessage> res = logResultsAndReturnNonInformationalOnes(output);
|
List<SingleValidationMessage> res = logResultsAndReturnNonInformationalOnes(output);
|
||||||
|
@ -205,7 +246,8 @@ public class FhirInstanceValidatorTest {
|
||||||
ValidationResult output = myVal.validateWithResult(input);
|
ValidationResult output = myVal.validateWithResult(input);
|
||||||
List<SingleValidationMessage> errors = logResultsAndReturnNonInformationalOnes(output);
|
List<SingleValidationMessage> errors = logResultsAndReturnNonInformationalOnes(output);
|
||||||
assertEquals(errors.toString(), 1, errors.size());
|
assertEquals(errors.toString(), 1, errors.size());
|
||||||
assertEquals("StructureDefinition reference \"http://foo/myprofile\" could not be resolved", errors.get(0).getMessage());
|
assertEquals("StructureDefinition reference \"http://foo/myprofile\" could not be resolved",
|
||||||
|
errors.get(0).getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -224,9 +266,11 @@ public class FhirInstanceValidatorTest {
|
||||||
ValidationResult output = myVal.validateWithResult(input);
|
ValidationResult output = myVal.validateWithResult(input);
|
||||||
List<SingleValidationMessage> errors = logResultsAndReturnNonInformationalOnes(output);
|
List<SingleValidationMessage> errors = logResultsAndReturnNonInformationalOnes(output);
|
||||||
|
|
||||||
assertThat(errors.toString(), containsString("Element '/f:Observation.subject': minimum required = 1, but only found 0"));
|
assertThat(errors.toString(),
|
||||||
|
containsString("Element '/f:Observation.subject': minimum required = 1, but only found 0"));
|
||||||
assertThat(errors.toString(), containsString("Element encounter @ /f:Observation: max allowed = 0, but found 1"));
|
assertThat(errors.toString(), containsString("Element encounter @ /f:Observation: max allowed = 0, but found 1"));
|
||||||
assertThat(errors.toString(), containsString("Element '/f:Observation.device': minimum required = 1, but only found 0"));
|
assertThat(errors.toString(),
|
||||||
|
containsString("Element '/f:Observation.device': minimum required = 1, but only found 0"));
|
||||||
assertThat(errors.toString(), containsString(""));
|
assertThat(errors.toString(), containsString(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,8 +289,8 @@ public class FhirInstanceValidatorTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testValidateResourceWithDefaultValuesetBadCode() {
|
public void testValidateResourceWithDefaultValuesetBadCode() {
|
||||||
String input = "<Observation xmlns=\"http://hl7.org/fhir\">\n" + " <status value=\"notvalidcode\"/>\n" + " <code>\n"
|
String input = "<Observation xmlns=\"http://hl7.org/fhir\">\n" + " <status value=\"notvalidcode\"/>\n"
|
||||||
+ " <text value=\"No code here!\"/>\n" + " </code>\n" + "</Observation>";
|
+ " <code>\n" + " <text value=\"No code here!\"/>\n" + " </code>\n" + "</Observation>";
|
||||||
ValidationResult output = myVal.validateWithResult(input);
|
ValidationResult output = myVal.validateWithResult(input);
|
||||||
assertEquals(
|
assertEquals(
|
||||||
"Coded value notvalidcode is not in value set http://hl7.org/fhir/ValueSet/observation-status (http://hl7.org/fhir/ValueSet/observation-status)",
|
"Coded value notvalidcode is not in value set http://hl7.org/fhir/ValueSet/observation-status (http://hl7.org/fhir/ValueSet/observation-status)",
|
||||||
|
@ -270,7 +314,7 @@ public class FhirInstanceValidatorTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testValidateResourceWithExampleBindingCodeValidationPassing() {
|
public void testValidateResourceWithExampleBindingCodeValidationPassingLoinc() {
|
||||||
Observation input = new Observation();
|
Observation input = new Observation();
|
||||||
|
|
||||||
myInstanceVal.setValidationSupport(myMockSupport);
|
myInstanceVal.setValidationSupport(myMockSupport);
|
||||||
|
@ -283,4 +327,39 @@ public class FhirInstanceValidatorTest {
|
||||||
List<SingleValidationMessage> errors = logResultsAndReturnNonInformationalOnes(output);
|
List<SingleValidationMessage> errors = logResultsAndReturnNonInformationalOnes(output);
|
||||||
assertEquals(errors.toString(), 0, errors.size());
|
assertEquals(errors.toString(), 0, errors.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateResourceWithExampleBindingCodeValidationPassingLoincWithExpansion() {
|
||||||
|
Observation input = new Observation();
|
||||||
|
|
||||||
|
ValueSetExpansionComponent expansionComponent = new ValueSetExpansionComponent();
|
||||||
|
expansionComponent.addContains().setSystem("http://loinc.org").setCode("12345").setDisplay("Some display code");
|
||||||
|
|
||||||
|
mySupportedCodeSystemsForExpansion.put("http://loinc.org", expansionComponent);
|
||||||
|
myInstanceVal.setValidationSupport(myMockSupport);
|
||||||
|
addValidConcept("http://loinc.org", "12345");
|
||||||
|
|
||||||
|
input.setStatus(ObservationStatus.FINAL);
|
||||||
|
input.getCode().addCoding().setSystem("http://loinc.org").setCode("1234");
|
||||||
|
|
||||||
|
ValidationResult output = myVal.validateWithResult(input);
|
||||||
|
List<SingleValidationMessage> errors = logResultsAndReturnNonInformationalOnes(output);
|
||||||
|
assertEquals(errors.toString(), 0, errors.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateResourceWithExampleBindingCodeValidationPassingNonLoinc() {
|
||||||
|
Observation input = new Observation();
|
||||||
|
|
||||||
|
myInstanceVal.setValidationSupport(myMockSupport);
|
||||||
|
addValidConcept("http://acme.org", "12345");
|
||||||
|
|
||||||
|
input.setStatus(ObservationStatus.FINAL);
|
||||||
|
input.getCode().addCoding().setSystem("http://acme.org").setCode("12345");
|
||||||
|
|
||||||
|
ValidationResult output = myVal.validateWithResult(input);
|
||||||
|
List<SingleValidationMessage> errors = logResultsAndReturnAll(output);
|
||||||
|
assertEquals(errors.toString(), 0, errors.size());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -188,6 +188,30 @@ public class QuestionnaireResponseValidatorIntegrationTest {
|
||||||
assertThat(result.getMessages().toString(), containsString("myMessage=Reference could not be found: http://some"));
|
assertThat(result.getMessages().toString(), containsString("myMessage=Reference could not be found: http://some"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testContainedQuestionnaireAndValueSet() {
|
||||||
|
ValueSet vs = new ValueSet();
|
||||||
|
vs.getCodeSystem().setSystem("urn:system").addConcept().setCode("code1");
|
||||||
|
vs.setId("#VVV");
|
||||||
|
|
||||||
|
Questionnaire q = new Questionnaire();
|
||||||
|
q.setId("#QQQ");
|
||||||
|
Reference vsRef = new Reference("#VVV");
|
||||||
|
vsRef.setResource(vs);
|
||||||
|
q.getGroup().addQuestion().setLinkId("link0").setRequired(false).setType(AnswerFormat.CHOICE).setOptions(vsRef);
|
||||||
|
|
||||||
|
QuestionnaireResponse qa;
|
||||||
|
|
||||||
|
// Bad code
|
||||||
|
|
||||||
|
qa = new QuestionnaireResponse();
|
||||||
|
qa.getQuestionnaire().setReference("#QQQ");
|
||||||
|
qa.getQuestionnaire().setResource(q);
|
||||||
|
qa.getGroup().addQuestion().setLinkId("link0").addAnswer().setValue(new Coding().setSystem("urn:system").setCode("code1"));
|
||||||
|
ValidationResult result = myVal.validateWithResult(qa);
|
||||||
|
assertEquals(result.getMessages().toString(), 0, result.getMessages().size());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sample provided by Eric van der Zwan
|
* Sample provided by Eric van der Zwan
|
||||||
*/
|
*/
|
||||||
|
|
58
pom.xml
58
pom.xml
|
@ -212,8 +212,9 @@
|
||||||
<apache_httpclient_version>4.4</apache_httpclient_version>
|
<apache_httpclient_version>4.4</apache_httpclient_version>
|
||||||
<apache_httpcore_version>4.4</apache_httpcore_version>
|
<apache_httpcore_version>4.4</apache_httpcore_version>
|
||||||
<derby_version>10.11.1.1</derby_version>
|
<derby_version>10.11.1.1</derby_version>
|
||||||
<!-- Note on Hibernate versions: Hibernate 4.3+ uses JPA 2.1, which is too new for a number of platforms including JBoss EAP 6.x and Glassfish 3.0. Upgrade this version with caution! Also note that if
|
<!-- Note on Hibernate versions: Hibernate 4.3+ uses JPA 2.1, which is too new for a number of platforms including JBoss EAP 6.x and Glassfish 3.0. Upgrade this
|
||||||
you change this, you may get a failure in hibernate4-maven-plugin. See the note in hapi-fhir-jpaserver-base/pom.xml's configuration for that plugin... <hibernate_version>4.3.7.Final</hibernate_version> -->
|
version with caution! Also note that if you change this, you may get a failure in hibernate4-maven-plugin. See the note in hapi-fhir-jpaserver-base/pom.xml's configuration
|
||||||
|
for that plugin... <hibernate_version>4.3.7.Final</hibernate_version> -->
|
||||||
<hibernate_version>4.2.17.Final</hibernate_version>
|
<hibernate_version>4.2.17.Final</hibernate_version>
|
||||||
<hibernate_validator_version>5.1.0.Final</hibernate_validator_version>
|
<hibernate_validator_version>5.1.0.Final</hibernate_validator_version>
|
||||||
<jetty_version>9.2.6.v20141205</jetty_version>
|
<jetty_version>9.2.6.v20141205</jetty_version>
|
||||||
|
@ -429,6 +430,11 @@
|
||||||
<artifactId>jetty-webapp</artifactId>
|
<artifactId>jetty-webapp</artifactId>
|
||||||
<version>9.2.6.v20141205</version>
|
<version>9.2.6.v20141205</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.fusesource.jansi</groupId>
|
||||||
|
<artifactId>jansi</artifactId>
|
||||||
|
<version>1.11</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.glassfish</groupId>
|
<groupId>org.glassfish</groupId>
|
||||||
<artifactId>javax.el</artifactId>
|
<artifactId>javax.el</artifactId>
|
||||||
|
@ -833,43 +839,43 @@
|
||||||
<configuration>
|
<configuration>
|
||||||
<target>
|
<target>
|
||||||
<copy todir="target/site/apidocs">
|
<copy todir="target/site/apidocs">
|
||||||
<fileset dir="hapi-fhir-base/target/site/apidocs"/>
|
<fileset dir="hapi-fhir-base/target/site/apidocs" />
|
||||||
</copy>
|
</copy>
|
||||||
<copy todir="target/site/apidocs-dstu">
|
<copy todir="target/site/apidocs-dstu">
|
||||||
<fileset dir="hapi-fhir-structures-dstu/target/site/apidocs"/>
|
<fileset dir="hapi-fhir-structures-dstu/target/site/apidocs" />
|
||||||
</copy>
|
</copy>
|
||||||
<copy todir="target/site/apidocs-hl7org-dstu2">
|
<copy todir="target/site/apidocs-hl7org-dstu2">
|
||||||
<fileset dir="hapi-fhir-structures-hl7org-dstu2/target/site/apidocs"/>
|
<fileset dir="hapi-fhir-structures-hl7org-dstu2/target/site/apidocs" />
|
||||||
</copy>
|
</copy>
|
||||||
<copy todir="target/site/apidocs-dstu2">
|
<copy todir="target/site/apidocs-dstu2">
|
||||||
<fileset dir="hapi-fhir-structures-dstu2/target/site/apidocs"/>
|
<fileset dir="hapi-fhir-structures-dstu2/target/site/apidocs" />
|
||||||
</copy>
|
</copy>
|
||||||
<copy todir="target/site/apidocs-jpaserver">
|
<copy todir="target/site/apidocs-jpaserver">
|
||||||
<fileset dir="hapi-fhir-jpaserver-base/target/site/apidocs"/>
|
<fileset dir="hapi-fhir-jpaserver-base/target/site/apidocs" />
|
||||||
</copy>
|
</copy>
|
||||||
<copy todir="target/site/xref-jpaserver">
|
<copy todir="target/site/xref-jpaserver">
|
||||||
<fileset dir="hapi-fhir-jpaserver-base/target/site/xref"/>
|
<fileset dir="hapi-fhir-jpaserver-base/target/site/xref" />
|
||||||
</copy>
|
</copy>
|
||||||
<copy todir="target/site/xref-base">
|
<copy todir="target/site/xref-base">
|
||||||
<fileset dir="hapi-fhir-base/target/site/xref"/>
|
<fileset dir="hapi-fhir-base/target/site/xref" />
|
||||||
</copy>
|
</copy>
|
||||||
<!-- <copy todir="target/site/cobertura"> <fileset dir="hapi-fhir-cobertura/target/site/cobertura" /> </copy> -->
|
<!-- <copy todir="target/site/cobertura"> <fileset dir="hapi-fhir-cobertura/target/site/cobertura" /> </copy> -->
|
||||||
<copy todir="target/site">
|
<copy todir="target/site">
|
||||||
<fileset dir="hapi-fhir-base/target/site" includes="checkstyle.*"/>
|
<fileset dir="hapi-fhir-base/target/site" includes="checkstyle.*" />
|
||||||
</copy>
|
</copy>
|
||||||
<echo>Fixing Checkstyle Report</echo>
|
<echo>Fixing Checkstyle Report</echo>
|
||||||
<replace dir="target/site" summary="true">
|
<replace dir="target/site" summary="true">
|
||||||
<include name="checkstyle.html"/>
|
<include name="checkstyle.html" />
|
||||||
<replacetoken>"../../</replacetoken>
|
<replacetoken>"../../</replacetoken>
|
||||||
<replacevalue>"./</replacevalue>
|
<replacevalue>"./</replacevalue>
|
||||||
</replace>
|
</replace>
|
||||||
<replace dir="target/site" summary="true">
|
<replace dir="target/site" summary="true">
|
||||||
<include name="*.html"/>
|
<include name="*.html" />
|
||||||
<replacetoken>http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-responsive.min.css</replacetoken>
|
<replacetoken>http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-responsive.min.css</replacetoken>
|
||||||
<replacevalue>./css/bootstrap-responsive.min.css</replacevalue>
|
<replacevalue>./css/bootstrap-responsive.min.css</replacevalue>
|
||||||
</replace>
|
</replace>
|
||||||
<replace dir="target/site" summary="true">
|
<replace dir="target/site" summary="true">
|
||||||
<include name="index.html"/>
|
<include name="index.html" />
|
||||||
<replacetoken><![CDATA[<h2 id="Welcome">Welcome</h2>]]></replacetoken>
|
<replacetoken><![CDATA[<h2 id="Welcome">Welcome</h2>]]></replacetoken>
|
||||||
<replacevalue><![CDATA[<div class="jumbotron subhead">
|
<replacevalue><![CDATA[<div class="jumbotron subhead">
|
||||||
<div class="row" id="banner">
|
<div class="row" id="banner">
|
||||||
|
@ -898,33 +904,33 @@
|
||||||
<target>
|
<target>
|
||||||
<echo>Adding Fontawesome</echo>
|
<echo>Adding Fontawesome</echo>
|
||||||
<replace dir="target/site" summary="true">
|
<replace dir="target/site" summary="true">
|
||||||
<include name="*.html"/>
|
<include name="*.html" />
|
||||||
<replacetoken><![CDATA[<a href="download.html" title="Download">Download</a>]]></replacetoken>
|
<replacetoken><![CDATA[<a href="download.html" title="Download">Download</a>]]></replacetoken>
|
||||||
<replacevalue><![CDATA[<a href="download.html" title="Download"><i class="fa fa-download"></i> Download</a>]]></replacevalue>
|
<replacevalue><![CDATA[<a href="download.html" title="Download"><i class="fa fa-download"></i> Download</a>]]></replacevalue>
|
||||||
</replace>
|
</replace>
|
||||||
<replace dir="target/site" summary="true">
|
<replace dir="target/site" summary="true">
|
||||||
<include name="*.html"/>
|
<include name="*.html" />
|
||||||
<replacetoken><![CDATA[<a href="https://github.com/jamesagnew/hapi-fhir/" title="GitHub Project" class="externalLink">GitHub Project</a>]]></replacetoken>
|
<replacetoken><![CDATA[<a href="https://github.com/jamesagnew/hapi-fhir/" title="GitHub Project" class="externalLink">GitHub Project</a>]]></replacetoken>
|
||||||
<replacevalue><![CDATA[<a href="https://github.com/jamesagnew/hapi-fhir/" title="GitHub Project" class="externalLink"><i class="fa fa-github"></i> GitHub Project</a>]]></replacevalue>
|
<replacevalue><![CDATA[<a href="https://github.com/jamesagnew/hapi-fhir/" title="GitHub Project" class="externalLink"><i class="fa fa-github"></i> GitHub Project</a>]]></replacevalue>
|
||||||
</replace>
|
</replace>
|
||||||
<replace dir="target/site" summary="true">
|
<replace dir="target/site" summary="true">
|
||||||
<include name="*.html"/>
|
<include name="*.html" />
|
||||||
<replacetoken><![CDATA[data-toggle="dropdown">Test Servers <]]></replacetoken>
|
<replacetoken><![CDATA[data-toggle="dropdown">Test Servers <]]></replacetoken>
|
||||||
<replacevalue><![CDATA[data-toggle="dropdown"><i class="fa fa-fire"></i> Test Servers <]]></replacevalue>
|
<replacevalue><![CDATA[data-toggle="dropdown"><i class="fa fa-fire"></i> Test Servers <]]></replacevalue>
|
||||||
</replace>
|
</replace>
|
||||||
<replace dir="target/site" summary="true">
|
<replace dir="target/site" summary="true">
|
||||||
<include name="*.html"/>
|
<include name="*.html" />
|
||||||
<replacetoken><![CDATA[data-toggle="dropdown">Documentation <]]></replacetoken>
|
<replacetoken><![CDATA[data-toggle="dropdown">Documentation <]]></replacetoken>
|
||||||
<replacevalue><![CDATA[data-toggle="dropdown"><i class="fa fa-book"></i> Documentation <]]></replacevalue>
|
<replacevalue><![CDATA[data-toggle="dropdown"><i class="fa fa-book"></i> Documentation <]]></replacevalue>
|
||||||
</replace>
|
</replace>
|
||||||
<replace dir="target/site" summary="true">
|
<replace dir="target/site" summary="true">
|
||||||
<include name="*.html"/>
|
<include name="*.html" />
|
||||||
<replacetoken><![CDATA[data-toggle="dropdown">Get Help <]]></replacetoken>
|
<replacetoken><![CDATA[data-toggle="dropdown">Get Help <]]></replacetoken>
|
||||||
<replacevalue><![CDATA[data-toggle="dropdown"><i class="fa fa-support"></i> Get Help <]]></replacevalue>
|
<replacevalue><![CDATA[data-toggle="dropdown"><i class="fa fa-support"></i> Get Help <]]></replacevalue>
|
||||||
</replace>
|
</replace>
|
||||||
<echo>Changing Breadcrumbs</echo>
|
<echo>Changing Breadcrumbs</echo>
|
||||||
<replace dir="target/site" summary="true">
|
<replace dir="target/site" summary="true">
|
||||||
<include name="doc_*.html"/>
|
<include name="doc_*.html" />
|
||||||
<replacetoken><![CDATA[<li class="divider">/</li>]]></replacetoken>
|
<replacetoken><![CDATA[<li class="divider">/</li>]]></replacetoken>
|
||||||
<replacevalue><![CDATA[<li class="divider">/</li>
|
<replacevalue><![CDATA[<li class="divider">/</li>
|
||||||
<li><a href="docindex.html" title="Documentation">Documentation</a></li>
|
<li><a href="docindex.html" title="Documentation">Documentation</a></li>
|
||||||
|
@ -975,8 +981,8 @@
|
||||||
<echo>Adding Google analytics in target/site for <body></echo>
|
<echo>Adding Google analytics in target/site for <body></echo>
|
||||||
<replace dir="target/site" summary="true">
|
<replace dir="target/site" summary="true">
|
||||||
<include name="**/*.html"></include>
|
<include name="**/*.html"></include>
|
||||||
<replacefilter token="#build#" value="${label}"/>
|
<replacefilter token="#build#" value="${label}" />
|
||||||
<replacefilter token="#version#" value="${project.version}"/>
|
<replacefilter token="#version#" value="${project.version}" />
|
||||||
<replacetoken><![CDATA[</body>]]></replacetoken>
|
<replacetoken><![CDATA[</body>]]></replacetoken>
|
||||||
<replacevalue><![CDATA[
|
<replacevalue><![CDATA[
|
||||||
<script>
|
<script>
|
||||||
|
@ -1114,8 +1120,9 @@
|
||||||
|
|
||||||
<reporting>
|
<reporting>
|
||||||
<plugins>
|
<plugins>
|
||||||
<!-- <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-checkstyle-plugin</artifactId> <reportSets> <reportSet> <reports><report>checkstyle-aggregate</report></reports> </reportSet>
|
<!-- <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-checkstyle-plugin</artifactId> <reportSets> <reportSet> <reports><report>checkstyle-aggregate</report></reports>
|
||||||
</reportSets> <configuration> <configLocation>config/sun_checks.xml</configLocation> <includes> hapi-fhir-base/src/main/java/**/*.java </includes> </configuration> </plugin> -->
|
</reportSet> </reportSets> <configuration> <configLocation>config/sun_checks.xml</configLocation> <includes> hapi-fhir-base/src/main/java/**/*.java </includes> </configuration>
|
||||||
|
</plugin> -->
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-changes-plugin</artifactId>
|
<artifactId>maven-changes-plugin</artifactId>
|
||||||
|
@ -1190,8 +1197,9 @@
|
||||||
</modules>
|
</modules>
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
<!-- <plugin> <artifactId>maven-assembly-plugin</artifactId> <version>${maven_assembly_plugin_version}</version> <executions> <execution> <phase>package</phase> <goals> <goal>single</goal> </goals>
|
<!-- <plugin> <artifactId>maven-assembly-plugin</artifactId> <version>${maven_assembly_plugin_version}</version> <executions> <execution> <phase>package</phase>
|
||||||
<configuration> <attach>false</attach> <descriptors> <descriptor>${project.basedir}/src/assembly/hapi-fhir-sample-projects.xml</descriptor> </descriptors> </configuration> </execution> </executions> </plugin> -->
|
<goals> <goal>single</goal> </goals> <configuration> <attach>false</attach> <descriptors> <descriptor>${project.basedir}/src/assembly/hapi-fhir-sample-projects.xml</descriptor>
|
||||||
|
</descriptors> </configuration> </execution> </executions> </plugin> -->
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
|
|
|
@ -181,6 +181,12 @@
|
||||||
definitions provided either by HL7 or by the user.
|
definitions provided either by HL7 or by the user.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p class="doc_info_bubble">
|
||||||
|
This style of validation is still experimental, and should be used with caution.
|
||||||
|
It is very powerful, but is still under active development and may continue to
|
||||||
|
change over time.
|
||||||
|
</p>
|
||||||
|
|
||||||
<subsection name="Preparation">
|
<subsection name="Preparation">
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
|
Loading…
Reference in New Issue