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.filefilter.WildcardFileFilter;
|
||||
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;
|
||||
|
@ -21,6 +23,8 @@ import ca.uhn.fhir.parser.IParser;
|
|||
import ca.uhn.fhir.parser.StrictErrorHandler;
|
||||
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||
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.FhirValidator;
|
||||
import ca.uhn.fhir.validation.IValidationSupport;
|
||||
|
@ -172,7 +176,7 @@ public class ValidatorExamples {
|
|||
IValidationSupport valSupport = new IValidationSupport() {
|
||||
|
||||
@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
|
||||
return null;
|
||||
}
|
||||
|
@ -194,8 +198,23 @@ public class ValidatorExamples {
|
|||
// TODO: Implement
|
||||
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
|
||||
}
|
||||
|
|
|
@ -1101,7 +1101,9 @@ public class JsonParser extends BaseParser implements IParser {
|
|||
continue;
|
||||
}
|
||||
// _id is incorrect, but some early examples in the FHIR spec used it
|
||||
if (theObject.get(nextName).getValueType() == ValueType.STRING) {
|
||||
elementId = theObject.getString(nextName);
|
||||
}
|
||||
continue;
|
||||
} else if ("extension".equals(nextName)) {
|
||||
JsonArray array = theObject.getJsonArray(nextName);
|
||||
|
|
|
@ -1076,6 +1076,8 @@ class ParserState<T> {
|
|||
} else if ("fullUrl".equals(theLocalPart)) {
|
||||
myFullUrl = new IdDt();
|
||||
push(new PrimitiveState(getPreResourceState(), myFullUrl));
|
||||
} else if ("fhir_comments".equals(theLocalPart) && myJsonMode) {
|
||||
push(new SwallowChildrenWholeState(getPreResourceState()));
|
||||
} else {
|
||||
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">
|
||||
<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
|
||||
if you are using this file as a basis for your own project. -->
|
||||
<!-- 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 if you are using this file as a basis for your own project. -->
|
||||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
|
@ -30,6 +30,11 @@
|
|||
<version>1.2-SNAPSHOT</version>
|
||||
<type>war</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-structures-dstu</artifactId>
|
||||
<version>1.2-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-structures-dstu2</artifactId>
|
||||
|
@ -91,6 +96,33 @@
|
|||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-webapp</artifactId>
|
||||
</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>
|
||||
|
||||
|
@ -163,6 +195,35 @@
|
|||
</executions>
|
||||
</plugin>
|
||||
</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>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ca.uhn.fhir.cli;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
@ -7,26 +8,130 @@ import java.util.List;
|
|||
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.apache.commons.cli.DefaultParser;
|
||||
import org.apache.commons.cli.HelpFormatter;
|
||||
import org.apache.commons.cli.Options;
|
||||
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 {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(App.class);
|
||||
private static List<BaseCommand> ourCommands;
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(App.class);
|
||||
|
||||
static {
|
||||
ourCommands = new ArrayList<BaseCommand>();
|
||||
ourCommands.add(new RunServerCommand());
|
||||
ourCommands.add(new ExampleDataUploader());
|
||||
|
||||
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) {
|
||||
loggingConfigOff();
|
||||
AnsiConsole.systemInstall();
|
||||
|
||||
// log version while the logging is off
|
||||
VersionUtil.getVersion();
|
||||
|
||||
if (theArgs.length == 0) {
|
||||
logUsage();
|
||||
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;
|
||||
for (BaseCommand nextCommand : ourCommands) {
|
||||
if (nextCommand.getCommandName().equals(theArgs[0])) {
|
||||
|
@ -41,18 +146,24 @@ public class App {
|
|||
try {
|
||||
String[] args = Arrays.asList(theArgs).subList(1, theArgs.length).toArray(new String[theArgs.length - 1]);
|
||||
parsedOptions = parser.parse(options, args, true);
|
||||
|
||||
logAppHeader();
|
||||
loggingConfigOn();
|
||||
|
||||
// Actually execute the command
|
||||
command.run(parsedOptions);
|
||||
|
||||
} catch (ParseException e) {
|
||||
ourLog.error("Invalid command options for command: " + command.getCommandName());
|
||||
ourLog.error(e.getMessage());
|
||||
ourLog.error("Aborting!");
|
||||
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();
|
||||
}
|
||||
|
||||
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
|
||||
public int compareTo(BaseCommand theO) {
|
||||
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;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.zip.ZipEntry;
|
||||
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.http.client.ClientProtocolException;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
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.Entry;
|
||||
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.valueset.HTTPVerbEnum;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||
import ca.uhn.fhir.util.ResourceReferenceInfo;
|
||||
|
||||
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);
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
new ExampleDataUploader().execute();
|
||||
@Override
|
||||
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 {
|
||||
ourLog.info("Starting...");
|
||||
@Override
|
||||
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();
|
||||
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();
|
||||
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());
|
||||
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));
|
||||
byte[] buffer = new byte[2048];
|
||||
|
@ -69,6 +114,10 @@ public class ExampleDataUploader extends BaseCommand {
|
|||
byte[] exampleBytes = bos.toByteArray();
|
||||
String exampleString = new String(exampleBytes, "UTF-8");
|
||||
|
||||
if (ourLog.isTraceEnabled()) {
|
||||
ourLog.trace("Next example: " + exampleString);
|
||||
}
|
||||
|
||||
IBaseResource parsed;
|
||||
try {
|
||||
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++) {
|
||||
Entry next = bundle.getEntry().get(i);
|
||||
if (next.getResource().getId().getIdPart() != null) {
|
||||
String nextId = next.getResource().getResourceName() + '/' + next.getResource().getId().getIdPart();
|
||||
if (!ids.add(nextId)) {
|
||||
String idPart = next.getResource().getId().getIdPart();
|
||||
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);
|
||||
bundle.getEntry().remove(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());
|
||||
for (ResourceReferenceInfo nextRef : refs) {
|
||||
// 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(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);
|
||||
nextRef.getResourceReference().getReferenceElement().setValue(null);
|
||||
}
|
||||
} else {
|
||||
goodRefs++;
|
||||
}
|
||||
|
@ -134,8 +233,10 @@ public class ExampleDataUploader extends BaseCommand {
|
|||
}
|
||||
|
||||
// for (Entry next : bundle.getEntry()) {
|
||||
// if (next.getResource().getId().hasIdPart() && Character.isLetter(next.getResource().getId().getIdPart().charAt(0))) {
|
||||
// next.getTransaction().setUrl(next.getResource().getResourceName() + '/' + next.getResource().getId().getIdPart());
|
||||
// if (next.getResource().getId().hasIdPart() &&
|
||||
// Character.isLetter(next.getResource().getId().getIdPart().charAt(0))) {
|
||||
// next.getTransaction().setUrl(next.getResource().getResourceName() + '/' +
|
||||
// next.getResource().getId().getIdPart());
|
||||
// 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: {} 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();
|
||||
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();
|
||||
root.setAllowDuplicateFragmentNames(true);
|
||||
root.setWar(tempWarFile.getAbsolutePath());
|
||||
root.setParentLoaderPriority(true);
|
||||
root.setContextPath("/");
|
||||
|
||||
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.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.http.client.ClientProtocolException;
|
||||
import org.hl7.fhir.instance.model.Bundle;
|
||||
|
@ -29,7 +32,7 @@ public class ValidationDataUploader extends BaseCommand {
|
|||
|
||||
FhirContext ctx = FhirContext.forDstu2Hl7Org();
|
||||
|
||||
IGenericClient client = newClient(ctx);
|
||||
IGenericClient client = newClient(ctx,"");
|
||||
|
||||
int total;
|
||||
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
|
||||
|
||||
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);
|
||||
IResource res = nextEntry.getResource();
|
||||
IdDt nextResourceId = null;
|
||||
|
@ -313,7 +318,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
|||
case POST: {
|
||||
// CREATE
|
||||
@SuppressWarnings("rawtypes")
|
||||
IFhirResourceDao resourceDao = getDao(res.getClass());
|
||||
IFhirResourceDao resourceDao = getDaoOrThrowException(res.getClass());
|
||||
res.setId((String) null);
|
||||
DaoMethodOutcome outcome;
|
||||
Entry newEntry = response.addEntry();
|
||||
|
@ -338,7 +343,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
|||
case PUT: {
|
||||
// UPDATE
|
||||
@SuppressWarnings("rawtypes")
|
||||
IFhirResourceDao resourceDao = getDao(res.getClass());
|
||||
IFhirResourceDao resourceDao = getDaoOrThrowException(res.getClass());
|
||||
|
||||
DaoMethodOutcome outcome;
|
||||
Entry newEntry = response.addEntry();
|
||||
|
@ -490,6 +495,14 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
|||
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,
|
||||
Entry newEntry, String theResourceType, IResource theRes) {
|
||||
IdDt newId = (IdDt) outcome.getId().toUnqualifiedVersionless();
|
||||
|
|
|
@ -587,12 +587,10 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
|
|||
continue;
|
||||
}
|
||||
|
||||
ourLog.info("Adding param: {}, {}", resourceName, nextValue.getValue());
|
||||
ourLog.trace("Adding param: {}, {}", resourceName, nextValue.getValue());
|
||||
|
||||
ResourceIndexedSearchParamUri nextEntity = new ResourceIndexedSearchParamUri(resourceName, nextValue.getValue());
|
||||
|
||||
ourLog.info("Added : {}", nextEntity);
|
||||
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
} else {
|
||||
|
|
|
@ -474,6 +474,17 @@ public class JsonParserDstu2Test {
|
|||
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
|
||||
public void testParseAndEncodeBundle() throws Exception {
|
||||
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.OperationOutcome.IssueSeverity;
|
||||
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;
|
||||
|
@ -68,8 +70,8 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
|
|||
}
|
||||
|
||||
@Override
|
||||
public org.hl7.fhir.instance.utils.IWorkerContext.ValidationResult validateCode(String theCodeSystem, String theCode, String theDisplay) {
|
||||
return new org.hl7.fhir.instance.utils.IWorkerContext.ValidationResult(IssueSeverity.INFORMATION, "Unknown code: " + theCodeSystem + " / " + theCode);
|
||||
public CodeValidationResult validateCode(String theCodeSystem, String theCode, String theDisplay) {
|
||||
return new CodeValidationResult(IssueSeverity.INFORMATION, "Unknown code: " + theCodeSystem + " / " + theCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -77,4 +79,9 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
|
|||
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.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.validation.IValidationSupport.CodeValidationResult;
|
||||
|
||||
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 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) {
|
||||
myCtx = theCtx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueSetExpansionComponent expandVS(ConceptSetComponent theInc) {
|
||||
throw new UnsupportedOperationException();
|
||||
return myValidationSupport.expandValueSet(theInc);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -354,7 +341,11 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
|
||||
@Override
|
||||
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
|
||||
|
|
|
@ -25,16 +25,23 @@ import ca.uhn.fhir.util.ResourceReferenceInfo;
|
|||
|
||||
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
|
||||
.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)
|
||||
* <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.
|
||||
* 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)
|
||||
* <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.
|
||||
*
|
||||
* @param 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.)
|
||||
* 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;
|
||||
|
@ -83,7 +90,8 @@ public class FhirQuestionnaireResponseValidator extends BaseValidatorBridge {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
private boolean loadReferences(IBaseResource theResource, WorkerContext theWorkerCtx, IValidationContext<?> theValCtx, ArrayList<ValidationMessage> theMessages) {
|
||||
private boolean loadReferences(IBaseResource theResource, WorkerContext theWorkerCtx, IValidationContext<?> theValCtx,
|
||||
ArrayList<ValidationMessage> theMessages) {
|
||||
List<ResourceReferenceInfo> refs = theValCtx.getFhirContext().newTerser().getAllResourceReferences(theResource);
|
||||
|
||||
List<IBaseResource> newResources = new ArrayList<IBaseResource>();
|
||||
|
@ -91,8 +99,23 @@ public class FhirQuestionnaireResponseValidator extends BaseValidatorBridge {
|
|||
for (ResourceReferenceInfo nextRefInfo : refs) {
|
||||
IIdType nextRef = nextRefInfo.getResourceReference().getReferenceElement();
|
||||
String resourceType = nextRef.getResourceType();
|
||||
if (isBlank(resourceType)) {
|
||||
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));
|
||||
if (nextRef.isLocal()) {
|
||||
IBaseResource resource = nextRefInfo.getResourceReference().getResource();
|
||||
if (resource instanceof ValueSet) {
|
||||
theWorkerCtx.getValueSets().put(nextRef.getValue(), (ValueSet) resource);
|
||||
newResources.add(resource);
|
||||
} else if (resource instanceof Questionnaire) {
|
||||
theWorkerCtx.getQuestionnaires().put(nextRef.getValue(), (Questionnaire) resource);
|
||||
newResources.add(resource);
|
||||
} else if (resource == null) {
|
||||
theMessages.add(new ValidationMessage(Source.QuestionnaireResponseValidator,
|
||||
org.hl7.fhir.instance.model.OperationOutcome.IssueType.INVALID,
|
||||
"Invalid reference '" + nextRef.getValue() + "' - No contained resource with this ID found", IssueSeverity.FATAL));
|
||||
}
|
||||
} else if (isBlank(resourceType)) {
|
||||
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));
|
||||
} else if ("ValueSet".equals(resourceType)) {
|
||||
if (!theWorkerCtx.getValueSets().containsKey(nextRef.getValue())) {
|
||||
ValueSet resource = tryToLoad(ValueSet.class, nextRef, theMessages);
|
||||
|
@ -124,20 +147,24 @@ public class FhirQuestionnaireResponseValidator extends BaseValidatorBridge {
|
|||
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,
|
||||
List<ValidationMessage> theMessages) {
|
||||
if (myResourceLoader == null) {
|
||||
theMessages.add(new ValidationMessage().setLevel(IssueSeverity.FATAL).setMessage("No resource loader present, could not load " + theReference));
|
||||
theMessages.add(new ValidationMessage().setLevel(IssueSeverity.FATAL)
|
||||
.setMessage("No resource loader present, could not load " + theReference));
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
T retVal = myResourceLoader.load(theType, theReference);
|
||||
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) {
|
||||
theMessages.add(new ValidationMessage().setLevel(IssueSeverity.FATAL).setMessage("Reference could not be found: " + theReference));
|
||||
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;
|
||||
|
||||
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 ca.uhn.fhir.context.FhirContext;
|
||||
|
@ -8,15 +12,22 @@ import ca.uhn.fhir.context.FhirContext;
|
|||
public interface IValidationSupport {
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if codes in the given code system can be
|
||||
* validated
|
||||
* Expands the given portion of a ValueSet
|
||||
*
|
||||
* @param theInclude
|
||||
* The portion to include
|
||||
* @return The expansion
|
||||
*/
|
||||
ValueSetExpansionComponent expandValueSet(ConceptSetComponent theInclude);
|
||||
|
||||
/**
|
||||
* Fetch a code system by ID
|
||||
*
|
||||
* @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
|
||||
* The code system
|
||||
* @return The valueset (must not be null, but can be an empty ValueSet)
|
||||
*/
|
||||
boolean isCodeSystemSupported(String theSystem);
|
||||
ValueSet fetchCodeSystem(String theSystem);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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
|
||||
* @return Returns a validation result object
|
||||
*/
|
||||
org.hl7.fhir.instance.utils.IWorkerContext.ValidationResult validateCode(String theCodeSystem, String theCode,
|
||||
String theDisplay);
|
||||
CodeValidationResult validateCode(String theCodeSystem, String theCode, String theDisplay);
|
||||
|
||||
/**
|
||||
* Fetch a code system by ID
|
||||
*
|
||||
* @param theSystem
|
||||
* The code system
|
||||
* @return The valueset (must not be null, but can be an empty ValueSet)
|
||||
*/
|
||||
ValueSet fetchCodeSystem(String theSystem);
|
||||
public class CodeValidationResult {
|
||||
private ConceptDefinitionComponent definition;
|
||||
private String message;
|
||||
private IssueSeverity severity;
|
||||
|
||||
public CodeValidationResult(ConceptDefinitionComponent definition) {
|
||||
this.definition = definition;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
public class ValueSetExpanderSimple implements ValueSetExpander {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValueSetExpanderSimple.class);
|
||||
private IWorkerContext context;
|
||||
private List<ValueSetExpansionContainsComponent> codes = new ArrayList<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);
|
||||
} 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
|
||||
// that might fail too, but it might not, later.
|
||||
return new ValueSetExpansionOutcome(new ValueSetCheckerSimple(source, factory, context), e.getMessage());
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package org.hl7.fhir.instance.validation;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
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");
|
||||
|
||||
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) {
|
||||
ElementDefinitionBindingComponent binding = context.getBinding();
|
||||
if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, binding != null, "Binding for " + path + " missing")) {
|
||||
|
@ -320,6 +322,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkContactPoint(List<ValidationMessage> errors, String path, WrapperElement focus, ContactPoint fixed) {
|
||||
checkFixedValue(errors, path + ".system", focus.getNamedChild("system"), fixed.getSystemElement(), "system");
|
||||
|
@ -1746,44 +1749,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
// 5. inspect each child for validity
|
||||
for (ElementInfo ei : children) {
|
||||
if (ei.definition != null) {
|
||||
String type = null;
|
||||
String type = determineType(ei.definition, ei.name, ei.line(), ei.col(), errors, stack, profile);
|
||||
ElementDefinition typeDefn = null;
|
||||
if (ei.definition.getType().size() == 1 && !ei.definition.getType().get(0).getCode().equals("*") && !ei.definition.getType().get(0).getCode().equals("Element")
|
||||
&& !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) {
|
||||
if (isBlank(type) && ei.definition.getNameReference() != null) {
|
||||
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));
|
||||
String localStackLiterapPath = localStack.getLiteralPath();
|
||||
String eiPath = ei.path;
|
||||
|
@ -1793,21 +1763,36 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
if (isPrimitiveType(type))
|
||||
checkPrimitive(errors, ei.path, type, ei.definition, ei.element);
|
||||
else {
|
||||
if (type.equals("Identifier"))
|
||||
if (type.equals("Identifier")) {
|
||||
checkIdentifier(errors, ei.path, ei.element, ei.definition);
|
||||
else if (type.equals("Coding"))
|
||||
} else if (type.equals("Coding")) {
|
||||
/*
|
||||
* 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"))
|
||||
}
|
||||
} else if (type.equals("CodeableConcept")) {
|
||||
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);
|
||||
}
|
||||
|
||||
if (type.equals("Extension"))
|
||||
if (type.equals("Extension")) {
|
||||
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
|
||||
// (str.matches(".*([.,/])work\\1$"))
|
||||
else {
|
||||
} else {
|
||||
StructureDefinition p = getProfileForType(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);
|
||||
|
@ -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) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
|
@ -2378,6 +2407,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
return logicalPaths == null ? new ArrayList<String>() : logicalPaths;
|
||||
}
|
||||
|
||||
public NodeStack getParent() {
|
||||
return this.parent;
|
||||
}
|
||||
|
||||
private ElementDefinition getType() {
|
||||
return type;
|
||||
}
|
||||
|
|
|
@ -9,7 +9,9 @@ import static org.mockito.Mockito.mock;
|
|||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
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.ValueSet;
|
||||
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.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.validation.IValidationSupport.CodeValidationResult;
|
||||
|
||||
public class FhirInstanceValidatorTest {
|
||||
|
||||
|
@ -37,6 +41,7 @@ public class FhirInstanceValidatorTest {
|
|||
|
||||
private FhirValidator myVal;
|
||||
private ArrayList<String> myValidConcepts;
|
||||
private Map<String, ValueSetExpansionComponent> mySupportedCodeSystemsForExpansion;
|
||||
|
||||
private void addValidConcept(String theSystem, String theCode) {
|
||||
myValidConcepts.add(theSystem + "___" + theCode);
|
||||
|
@ -52,39 +57,57 @@ public class FhirInstanceValidatorTest {
|
|||
myInstanceVal = new FhirInstanceValidator();
|
||||
myVal.registerValidatorModule(myInstanceVal);
|
||||
|
||||
mySupportedCodeSystemsForExpansion = new HashMap<String, ValueSet.ValueSetExpansionComponent>();
|
||||
|
||||
myValidConcepts = new ArrayList<String>();
|
||||
|
||||
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>() {
|
||||
@Override
|
||||
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 });
|
||||
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)))
|
||||
.thenAnswer(new Answer<IBaseResource>() {
|
||||
@Override
|
||||
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 });
|
||||
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>() {
|
||||
.thenAnswer(new Answer<CodeValidationResult>() {
|
||||
@Override
|
||||
public org.hl7.fhir.instance.utils.IWorkerContext.ValidationResult answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
public CodeValidationResult answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
String system = (String) theInvocation.getArguments()[0];
|
||||
String code = (String) theInvocation.getArguments()[1];
|
||||
org.hl7.fhir.instance.utils.IWorkerContext.ValidationResult retVal;
|
||||
CodeValidationResult retVal;
|
||||
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 {
|
||||
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;
|
||||
}
|
||||
});
|
||||
|
@ -104,7 +127,8 @@ public class FhirInstanceValidatorTest {
|
|||
|
||||
int index = 0;
|
||||
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++;
|
||||
|
||||
if (next.getSeverity() != ResultSeverityEnum.INFORMATION) {
|
||||
|
@ -115,6 +139,21 @@ public class FhirInstanceValidatorTest {
|
|||
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
|
||||
public void testValidateRawJsonResource() {
|
||||
// @formatter:off
|
||||
|
@ -152,7 +191,8 @@ public class FhirInstanceValidatorTest {
|
|||
@Test
|
||||
public void testValidateRawXmlResourceBadAttributes() {
|
||||
// @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
|
||||
|
||||
ValidationResult output = myVal.validateWithResult(input);
|
||||
|
@ -173,7 +213,8 @@ public class FhirInstanceValidatorTest {
|
|||
|
||||
ValidationResult output = myVal.validateWithResult(input);
|
||||
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
|
||||
*/
|
||||
@Test
|
||||
@Ignore
|
||||
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);
|
||||
|
||||
List<SingleValidationMessage> res = logResultsAndReturnNonInformationalOnes(output);
|
||||
|
@ -205,7 +246,8 @@ public class FhirInstanceValidatorTest {
|
|||
ValidationResult output = myVal.validateWithResult(input);
|
||||
List<SingleValidationMessage> errors = logResultsAndReturnNonInformationalOnes(output);
|
||||
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
|
||||
|
@ -224,9 +266,11 @@ public class FhirInstanceValidatorTest {
|
|||
ValidationResult output = myVal.validateWithResult(input);
|
||||
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 '/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(""));
|
||||
}
|
||||
|
||||
|
@ -245,8 +289,8 @@ public class FhirInstanceValidatorTest {
|
|||
|
||||
@Test
|
||||
public void testValidateResourceWithDefaultValuesetBadCode() {
|
||||
String input = "<Observation xmlns=\"http://hl7.org/fhir\">\n" + " <status value=\"notvalidcode\"/>\n" + " <code>\n"
|
||||
+ " <text value=\"No code here!\"/>\n" + " </code>\n" + "</Observation>";
|
||||
String input = "<Observation xmlns=\"http://hl7.org/fhir\">\n" + " <status value=\"notvalidcode\"/>\n"
|
||||
+ " <code>\n" + " <text value=\"No code here!\"/>\n" + " </code>\n" + "</Observation>";
|
||||
ValidationResult output = myVal.validateWithResult(input);
|
||||
assertEquals(
|
||||
"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
|
||||
public void testValidateResourceWithExampleBindingCodeValidationPassing() {
|
||||
public void testValidateResourceWithExampleBindingCodeValidationPassingLoinc() {
|
||||
Observation input = new Observation();
|
||||
|
||||
myInstanceVal.setValidationSupport(myMockSupport);
|
||||
|
@ -283,4 +327,39 @@ public class FhirInstanceValidatorTest {
|
|||
List<SingleValidationMessage> errors = logResultsAndReturnNonInformationalOnes(output);
|
||||
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"));
|
||||
}
|
||||
|
||||
@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
|
||||
*/
|
||||
|
|
20
pom.xml
20
pom.xml
|
@ -212,8 +212,9 @@
|
|||
<apache_httpclient_version>4.4</apache_httpclient_version>
|
||||
<apache_httpcore_version>4.4</apache_httpcore_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
|
||||
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> -->
|
||||
<!-- 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 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_validator_version>5.1.0.Final</hibernate_validator_version>
|
||||
<jetty_version>9.2.6.v20141205</jetty_version>
|
||||
|
@ -429,6 +430,11 @@
|
|||
<artifactId>jetty-webapp</artifactId>
|
||||
<version>9.2.6.v20141205</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.fusesource.jansi</groupId>
|
||||
<artifactId>jansi</artifactId>
|
||||
<version>1.11</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish</groupId>
|
||||
<artifactId>javax.el</artifactId>
|
||||
|
@ -1114,8 +1120,9 @@
|
|||
|
||||
<reporting>
|
||||
<plugins>
|
||||
<!-- <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-checkstyle-plugin</artifactId> <reportSets> <reportSet> <reports><report>checkstyle-aggregate</report></reports> </reportSet>
|
||||
</reportSets> <configuration> <configLocation>config/sun_checks.xml</configLocation> <includes> hapi-fhir-base/src/main/java/**/*.java </includes> </configuration> </plugin> -->
|
||||
<!-- <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-checkstyle-plugin</artifactId> <reportSets> <reportSet> <reports><report>checkstyle-aggregate</report></reports>
|
||||
</reportSet> </reportSets> <configuration> <configLocation>config/sun_checks.xml</configLocation> <includes> hapi-fhir-base/src/main/java/**/*.java </includes> </configuration>
|
||||
</plugin> -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-changes-plugin</artifactId>
|
||||
|
@ -1190,8 +1197,9 @@
|
|||
</modules>
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- <plugin> <artifactId>maven-assembly-plugin</artifactId> <version>${maven_assembly_plugin_version}</version> <executions> <execution> <phase>package</phase> <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> -->
|
||||
<!-- <plugin> <artifactId>maven-assembly-plugin</artifactId> <version>${maven_assembly_plugin_version}</version> <executions> <execution> <phase>package</phase>
|
||||
<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>
|
||||
</build>
|
||||
|
||||
|
|
|
@ -181,6 +181,12 @@
|
|||
definitions provided either by HL7 or by the user.
|
||||
</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">
|
||||
|
||||
<p>
|
||||
|
|
Loading…
Reference in New Issue