Add header-passthrough option to hapi-fhir-cli commands which generate http requests.
Add class to parent request-generating commands. Allow ValidationResult to be set number of detected errors to inform.
This commit is contained in:
parent
d762a07817
commit
5f36fe56b0
|
@ -39,18 +39,22 @@ import ca.uhn.fhir.util.OperationOutcomeUtil;
|
||||||
* @since 0.7
|
* @since 0.7
|
||||||
*/
|
*/
|
||||||
public class ValidationResult {
|
public class ValidationResult {
|
||||||
|
public static final int ERROR_DISPLAY_LIMIT_DEFAULT = 1;
|
||||||
|
|
||||||
private final FhirContext myCtx;
|
private final FhirContext myCtx;
|
||||||
private final boolean myIsSuccessful;
|
private final boolean myIsSuccessful;
|
||||||
private final List<SingleValidationMessage> myMessages;
|
private final List<SingleValidationMessage> myMessages;
|
||||||
|
|
||||||
|
private int myErrorDisplayLimit = ERROR_DISPLAY_LIMIT_DEFAULT;
|
||||||
|
|
||||||
public ValidationResult(FhirContext theCtx, List<SingleValidationMessage> theMessages) {
|
public ValidationResult(FhirContext theCtx, List<SingleValidationMessage> theMessages) {
|
||||||
boolean successful = true;
|
boolean successful = true;
|
||||||
myCtx = theCtx;
|
myCtx = theCtx;
|
||||||
myMessages = theMessages;
|
myMessages = theMessages;
|
||||||
for (SingleValidationMessage next : myMessages) {
|
for (SingleValidationMessage next : myMessages) {
|
||||||
next.getSeverity();
|
|
||||||
if (next.getSeverity() == null || next.getSeverity().ordinal() > ResultSeverityEnum.WARNING.ordinal()) {
|
if (next.getSeverity() == null || next.getSeverity().ordinal() > ResultSeverityEnum.WARNING.ordinal()) {
|
||||||
successful = false;
|
successful = false;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
myIsSuccessful = successful;
|
myIsSuccessful = successful;
|
||||||
|
@ -72,22 +76,36 @@ public class ValidationResult {
|
||||||
return myIsSuccessful;
|
return myIsSuccessful;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private String toDescription() {
|
private String toDescription() {
|
||||||
StringBuilder b = new StringBuilder(100);
|
if (myMessages.isEmpty()) {
|
||||||
if (myMessages.size() > 0) {
|
return "No issues";
|
||||||
if (myMessages.get(0).getSeverity() != null) {
|
}
|
||||||
b.append(myMessages.get(0).getSeverity().name());
|
|
||||||
|
StringBuilder b = new StringBuilder(100 * myMessages.size());
|
||||||
|
int shownMsgQty = Math.min(myErrorDisplayLimit, myMessages.size());
|
||||||
|
for (int i = 0; i < shownMsgQty; i++) {
|
||||||
|
SingleValidationMessage nextMsg = myMessages.get(i);
|
||||||
|
b.append(ourNewLine);
|
||||||
|
if (i == 0) {
|
||||||
|
if (shownMsgQty < myMessages.size()) {
|
||||||
|
b.append("(showing first ").append(shownMsgQty).append(" messages out of ")
|
||||||
|
.append(myMessages.size()).append(" total)").append(ourNewLine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nextMsg.getSeverity() != null) {
|
||||||
|
b.append(nextMsg.getSeverity().name());
|
||||||
b.append(" - ");
|
b.append(" - ");
|
||||||
}
|
}
|
||||||
b.append(myMessages.get(0).getMessage());
|
b.append(nextMsg.getMessage());
|
||||||
b.append(" - ");
|
b.append(" - ");
|
||||||
b.append(myMessages.get(0).getLocationString());
|
b.append(nextMsg.getLocationString());
|
||||||
} else {
|
|
||||||
b.append("No issues");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return b.toString();
|
return b.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated Use {@link #toOperationOutcome()} instead since this method returns a view.
|
* @deprecated Use {@link #toOperationOutcome()} instead since this method returns a view.
|
||||||
* {@link #toOperationOutcome()} is identical to this method, but has a more suitable name so this method
|
* {@link #toOperationOutcome()} is identical to this method, but has a more suitable name so this method
|
||||||
|
@ -156,4 +174,11 @@ public class ValidationResult {
|
||||||
public FhirContext getContext() {
|
public FhirContext getContext() {
|
||||||
return myCtx;
|
return myCtx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getErrorDisplayLimit() { return myErrorDisplayLimit; }
|
||||||
|
|
||||||
|
public void setErrorDisplayLimit(int theErrorDisplayLimit) { myErrorDisplayLimit = theErrorDisplayLimit; }
|
||||||
|
|
||||||
|
|
||||||
|
private static final String ourNewLine = System.getProperty("line.separator");
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
package ca.uhn.fhir.validation;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
|
class ValidationResultTest {
|
||||||
|
|
||||||
|
private final @Mock FhirContext myFhirContext = mock(FhirContext.class);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testLessThanDefaultDisplayQty() {
|
||||||
|
List<SingleValidationMessage> validationMessages = getTestValidationErrors(2);
|
||||||
|
ValidationResult vr = new ValidationResult(myFhirContext, validationMessages);
|
||||||
|
String toStringValue = vr.toString();
|
||||||
|
assertTrue(toStringValue.contains("Error message #" + 1));
|
||||||
|
assertFalse(toStringValue.contains("Error message #" + 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testMoreThanDefaultDisplayQty() {
|
||||||
|
List<SingleValidationMessage> validationMessages =
|
||||||
|
getTestValidationErrors(ValidationResult.ERROR_DISPLAY_LIMIT_DEFAULT * 2);
|
||||||
|
ValidationResult vr = new ValidationResult(myFhirContext, validationMessages);
|
||||||
|
String toStringValue = vr.toString();
|
||||||
|
assertTrue(toStringValue.contains("Error message #" + ValidationResult.ERROR_DISPLAY_LIMIT_DEFAULT));
|
||||||
|
assertFalse(toStringValue.contains("Error message #" + (ValidationResult.ERROR_DISPLAY_LIMIT_DEFAULT + 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testDisplayLimitSet() {
|
||||||
|
List<SingleValidationMessage> validationMessages = getTestValidationErrors(10);
|
||||||
|
ValidationResult vr = new ValidationResult(myFhirContext, validationMessages);
|
||||||
|
vr.setErrorDisplayLimit(8);
|
||||||
|
String toStringValue = vr.toString();
|
||||||
|
assertTrue(toStringValue.contains("Error message #" + 8));
|
||||||
|
assertFalse(toStringValue.contains("Error message #" + 9));
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<SingleValidationMessage> getTestValidationErrors(int theSize) {
|
||||||
|
List<SingleValidationMessage> msgList = new ArrayList<>();
|
||||||
|
for (int i = 0; i < theSize; i++) {
|
||||||
|
SingleValidationMessage validationMsg = new SingleValidationMessage();
|
||||||
|
validationMsg.setLocationCol(i);
|
||||||
|
validationMsg.setLocationLine(1);
|
||||||
|
validationMsg.setLocationString("Error #" + (i+1));
|
||||||
|
validationMsg.setMessage("Error message #" + (i+1));
|
||||||
|
validationMsg.setSeverity(ResultSeverityEnum.ERROR);
|
||||||
|
msgList.add(validationMsg);
|
||||||
|
}
|
||||||
|
return msgList;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -24,6 +24,7 @@ import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||||
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||||
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
import org.apache.commons.cli.CommandLine;
|
import org.apache.commons.cli.CommandLine;
|
||||||
import org.apache.commons.cli.Options;
|
import org.apache.commons.cli.Options;
|
||||||
import org.apache.commons.cli.ParseException;
|
import org.apache.commons.cli.ParseException;
|
||||||
|
@ -40,12 +41,13 @@ import java.nio.file.Files;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.nio.file.StandardOpenOption;
|
import java.nio.file.StandardOpenOption;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.*;
|
import static org.apache.commons.lang3.StringUtils.*;
|
||||||
|
|
||||||
public abstract class AbstractImportExportCsvConceptMapCommand extends BaseCommand {
|
public abstract class AbstractImportExportCsvConceptMapCommand extends BaseRequestGeneratingCommand {
|
||||||
// TODO: Don't use qualified names for loggers in HAPI CLI.
|
// TODO: Don't use qualified names for loggers in HAPI CLI.
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(AbstractImportExportCsvConceptMapCommand.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(AbstractImportExportCsvConceptMapCommand.class);
|
||||||
|
|
||||||
|
@ -63,14 +65,11 @@ public abstract class AbstractImportExportCsvConceptMapCommand extends BaseComma
|
||||||
protected FhirVersionEnum fhirVersion;
|
protected FhirVersionEnum fhirVersion;
|
||||||
protected String file;
|
protected String file;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void addFhirVersionOption(Options theOptions) {
|
protected Collection<Object> getFilterOutVersions() {
|
||||||
String versions = Arrays.stream(FhirVersionEnum.values())
|
return Sets.newHashSet(FhirVersionEnum.DSTU2_1,
|
||||||
.filter(t -> t != FhirVersionEnum.DSTU2_1 && t != FhirVersionEnum.DSTU2_HL7ORG && t != FhirVersionEnum.DSTU2)
|
FhirVersionEnum.DSTU2_HL7ORG, FhirVersionEnum.DSTU2);
|
||||||
.map(t -> t.name().toLowerCase())
|
|
||||||
.sorted()
|
|
||||||
.collect(Collectors.joining(", "));
|
|
||||||
addRequiredOption(theOptions, FHIR_VERSION_PARAM, FHIR_VERSION_PARAM_LONGOPT, FHIR_VERSION_PARAM_NAME, FHIR_VERSION_PARAM_DESC + versions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected BufferedReader getBufferedReader() throws IOException {
|
protected BufferedReader getBufferedReader() throws IOException {
|
||||||
|
@ -139,7 +138,7 @@ public abstract class AbstractImportExportCsvConceptMapCommand extends BaseComma
|
||||||
process();
|
process();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void parseAdditionalParameters(CommandLine theCommandLine) throws ParseException {}
|
protected void parseAdditionalParameters(CommandLine theCommandLine) {}
|
||||||
|
|
||||||
protected abstract void process() throws ParseException, ExecutionException;
|
protected abstract void process() throws ParseException, ExecutionException;
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,7 @@ import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
import static org.fusesource.jansi.Ansi.ansi;
|
import static org.fusesource.jansi.Ansi.ansi;
|
||||||
|
@ -216,43 +217,14 @@ public abstract class BaseApp {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (theArgs[0].equals("help")) {
|
if (theArgs[0].equals("help")) {
|
||||||
if (theArgs.length < 2) {
|
processHelp(theArgs);
|
||||||
logUsage();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
BaseCommand command = null;
|
|
||||||
for (BaseCommand nextCommand : ourCommands) {
|
|
||||||
if (nextCommand.getCommandName().equals(theArgs[1])) {
|
|
||||||
command = nextCommand;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (command == null) {
|
|
||||||
String message = "Unknown command: " + theArgs[1];
|
|
||||||
System.err.println(message);
|
|
||||||
exitDueToProblem(message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
logCommandUsage(command);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseCommand command = null;
|
Optional<BaseCommand> commandOpt = parseCommand(theArgs);
|
||||||
for (BaseCommand nextCommand : ourCommands) {
|
if (! commandOpt.isPresent()) return;
|
||||||
if (nextCommand.getCommandName().equals(theArgs[0])) {
|
|
||||||
command = nextCommand;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (command == null) {
|
BaseCommand command = commandOpt.get();
|
||||||
String message = "Unrecognized command: " + ansi().bold().fg(Ansi.Color.RED) + theArgs[0] + ansi().boldOff().fg(Ansi.Color.WHITE);
|
|
||||||
printMessageToStdout(message);
|
|
||||||
printMessageToStdout("");
|
|
||||||
logUsage();
|
|
||||||
exitDueToProblem(message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
myShutdownHook = new MyShutdownHook(command);
|
myShutdownHook = new MyShutdownHook(command);
|
||||||
Runtime.getRuntime().addShutdownHook(myShutdownHook);
|
Runtime.getRuntime().addShutdownHook(myShutdownHook);
|
||||||
|
@ -304,11 +276,44 @@ public abstract class BaseApp {
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
ourLog.error("Error during execution: ", t);
|
ourLog.error("Error during execution: ", t);
|
||||||
runCleanupHookAndUnregister();
|
runCleanupHookAndUnregister();
|
||||||
exitDueToException(new CommandFailureException("Error: " + t.toString(), t));
|
exitDueToException(new CommandFailureException("Error: " + t, t));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Optional<BaseCommand> parseCommand(String[] theArgs) {
|
||||||
|
Optional<BaseCommand> commandOpt = getNextCommand(theArgs);
|
||||||
|
|
||||||
|
if (! commandOpt.isPresent()) {
|
||||||
|
String message = "Unrecognized command: " + ansi().bold().fg(Ansi.Color.RED) + theArgs[0] + ansi().boldOff().fg(Ansi.Color.WHITE);
|
||||||
|
printMessageToStdout(message);
|
||||||
|
printMessageToStdout("");
|
||||||
|
logUsage();
|
||||||
|
exitDueToProblem(message);
|
||||||
|
}
|
||||||
|
return commandOpt;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<BaseCommand> getNextCommand(String[] theArgs) {
|
||||||
|
return ourCommands.stream().filter(cmd -> cmd.getCommandName().equals(theArgs[0])).findFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processHelp(String[] theArgs) {
|
||||||
|
if (theArgs.length < 2) {
|
||||||
|
logUsage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Optional<BaseCommand> commandOpt = getNextCommand(theArgs);
|
||||||
|
if (! commandOpt.isPresent()) {
|
||||||
|
String message = "Unknown command: " + theArgs[1];
|
||||||
|
System.err.println(message);
|
||||||
|
exitDueToProblem(message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
logCommandUsage(commandOpt.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private void exitDueToProblem(String theDescription) {
|
private void exitDueToProblem(String theDescription) {
|
||||||
if ("true".equals(System.getProperty("test"))) {
|
if ("true".equals(System.getProperty("test"))) {
|
||||||
throw new Error(theDescription);
|
throw new Error(theDescription);
|
||||||
|
@ -380,6 +385,7 @@ public abstract class BaseApp {
|
||||||
configurator.setContext((LoggerContext) LoggerFactory.getILoggerFactory());
|
configurator.setContext((LoggerContext) LoggerFactory.getILoggerFactory());
|
||||||
((LoggerContext) LoggerFactory.getILoggerFactory()).reset();
|
((LoggerContext) LoggerFactory.getILoggerFactory()).reset();
|
||||||
configurator.doConfigure(App.class.getResourceAsStream("/logback-cli-on.xml"));
|
configurator.doConfigure(App.class.getResourceAsStream("/logback-cli-on.xml"));
|
||||||
|
ourLog.info("Logging configuration set from file logback-cli-on.xml");
|
||||||
} catch (JoranException e) {
|
} catch (JoranException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
@ -391,6 +397,7 @@ public abstract class BaseApp {
|
||||||
configurator.setContext((LoggerContext) LoggerFactory.getILoggerFactory());
|
configurator.setContext((LoggerContext) LoggerFactory.getILoggerFactory());
|
||||||
((LoggerContext) LoggerFactory.getILoggerFactory()).reset();
|
((LoggerContext) LoggerFactory.getILoggerFactory()).reset();
|
||||||
configurator.doConfigure(App.class.getResourceAsStream("/logback-cli-on-debug.xml"));
|
configurator.doConfigure(App.class.getResourceAsStream("/logback-cli-on-debug.xml"));
|
||||||
|
ourLog.info("Logging configuration set from file logback-cli-on-debug.xml");
|
||||||
} catch (JoranException e) {
|
} catch (JoranException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,13 +26,20 @@ import ca.uhn.fhir.rest.api.Constants;
|
||||||
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||||
import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor;
|
import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor;
|
||||||
import com.google.common.base.Charsets;
|
import com.google.common.base.Charsets;
|
||||||
import org.apache.commons.cli.*;
|
import com.google.common.collect.Sets;
|
||||||
|
import org.apache.commons.cli.CommandLine;
|
||||||
|
import org.apache.commons.cli.Option;
|
||||||
|
import org.apache.commons.cli.OptionGroup;
|
||||||
|
import org.apache.commons.cli.Options;
|
||||||
|
import org.apache.commons.cli.ParseException;
|
||||||
import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
|
import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.apache.commons.io.FilenameUtils;
|
import org.apache.commons.io.FilenameUtils;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.commons.lang3.ObjectUtils;
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.time.DateUtils;
|
import org.apache.commons.lang3.time.DateUtils;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
import org.apache.http.client.methods.HttpGet;
|
import org.apache.http.client.methods.HttpGet;
|
||||||
import org.apache.http.impl.client.CloseableHttpClient;
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
@ -109,7 +116,7 @@ public abstract class BaseCommand implements Comparable<BaseCommand> {
|
||||||
try {
|
try {
|
||||||
retVal = reader.readLine();
|
retVal = reader.readLine();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new ParseException("Failed to read input from user: " + e.toString());
|
throw new ParseException("Failed to read input from user: " + e);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
retVal = new String(console.readPassword());
|
retVal = new String(console.readPassword());
|
||||||
|
@ -120,9 +127,13 @@ public abstract class BaseCommand implements Comparable<BaseCommand> {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Collection<Object> getFilterOutVersions() {
|
||||||
|
return Sets.newHashSet(FhirVersionEnum.DSTU2_1, FhirVersionEnum.DSTU2_HL7ORG);
|
||||||
|
}
|
||||||
|
|
||||||
protected void addFhirVersionOption(Options theOptions) {
|
protected void addFhirVersionOption(Options theOptions) {
|
||||||
String versions = Arrays.stream(FhirVersionEnum.values())
|
String versions = Arrays.stream(FhirVersionEnum.values())
|
||||||
.filter(t -> t != FhirVersionEnum.DSTU2_1 && t != FhirVersionEnum.DSTU2_HL7ORG)
|
.filter(t -> ! getFilterOutVersions().contains(t))
|
||||||
.map(t -> t.name().toLowerCase())
|
.map(t -> t.name().toLowerCase())
|
||||||
.sorted()
|
.sorted()
|
||||||
.collect(Collectors.joining(", "));
|
.collect(Collectors.joining(", "));
|
||||||
|
@ -275,12 +286,33 @@ public abstract class BaseCommand implements Comparable<BaseCommand> {
|
||||||
byte[] basicAuth = optionValue.getBytes();
|
byte[] basicAuth = optionValue.getBytes();
|
||||||
String base64EncodedBasicAuth = Base64Utils.encodeToString(basicAuth);
|
String base64EncodedBasicAuth = Base64Utils.encodeToString(basicAuth);
|
||||||
basicAuthHeaderValue = Constants.HEADER_AUTHORIZATION_VALPREFIX_BASIC + base64EncodedBasicAuth;
|
basicAuthHeaderValue = Constants.HEADER_AUTHORIZATION_VALPREFIX_BASIC + base64EncodedBasicAuth;
|
||||||
} else {
|
|
||||||
basicAuthHeaderValue = null;
|
|
||||||
}
|
}
|
||||||
return basicAuthHeaderValue;
|
return basicAuthHeaderValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected Pair<String, String> parseNameValueParameter(
|
||||||
|
String separator, String theParamName, String theParam) throws ParseException {
|
||||||
|
|
||||||
|
String errorMsg = "Parameter " + theParamName + " must be in the format: \"name:value\"";
|
||||||
|
|
||||||
|
if (! theParam.contains(separator)) {
|
||||||
|
throw new ParseException(errorMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] nameValue = theParam.split(separator);
|
||||||
|
if (nameValue.length != 2) {
|
||||||
|
throw new ParseException(errorMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(nameValue[0]) || StringUtils.isBlank(nameValue[1])) {
|
||||||
|
throw new ParseException(errorMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Pair.of(nameValue[0], nameValue[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public <T extends Enum> T getAndParseOptionEnum(CommandLine theCommandLine, String theOption, Class<T> theEnumClass, boolean theRequired, T theDefault) throws ParseException {
|
public <T extends Enum> T getAndParseOptionEnum(CommandLine theCommandLine, String theOption, Class<T> theEnumClass, boolean theRequired, T theDefault) throws ParseException {
|
||||||
String val = theCommandLine.getOptionValue(theOption);
|
String val = theCommandLine.getOptionValue(theOption);
|
||||||
if (isBlank(val)) {
|
if (isBlank(val)) {
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
package ca.uhn.fhir.cli;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||||
|
import ca.uhn.fhir.rest.client.interceptor.AdditionalRequestHeadersInterceptor;
|
||||||
|
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.lang3.tuple.Pair;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
|
public abstract class BaseRequestGeneratingCommand extends BaseCommand {
|
||||||
|
|
||||||
|
public enum BaseRequestGeneratingCommandOptions {
|
||||||
|
VERSION,
|
||||||
|
BASE_URL,
|
||||||
|
BASIC_AUTH,
|
||||||
|
VERBOSE_LOGGING,
|
||||||
|
HEADER_PASSTHROUGH
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected static final String HEADER_PASSTHROUGH = "hp";
|
||||||
|
protected static final String HEADER_PASSTHROUGH_NAME = "header";
|
||||||
|
protected static final String HEADER_PASSTHROUGH_LONGOPT = "header-passthrough";
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Options getOptions() {
|
||||||
|
return getSomeOptions(Collections.emptySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows child classes to obtain a subset of the parent-defined options
|
||||||
|
*/
|
||||||
|
protected Options getSomeOptions(Collection<BaseRequestGeneratingCommandOptions> theExcludeOptions) {
|
||||||
|
Options options = new Options();
|
||||||
|
|
||||||
|
if (! theExcludeOptions.contains(BaseRequestGeneratingCommandOptions.VERSION)) {
|
||||||
|
addFhirVersionOption(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! theExcludeOptions.contains(BaseRequestGeneratingCommandOptions.BASE_URL)) {
|
||||||
|
addBaseUrlOption(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! theExcludeOptions.contains(BaseRequestGeneratingCommandOptions.BASIC_AUTH)) {
|
||||||
|
addBasicAuthOption(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! theExcludeOptions.contains(BaseRequestGeneratingCommandOptions.VERBOSE_LOGGING)) {
|
||||||
|
addVerboseLoggingOption(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! theExcludeOptions.contains(BaseRequestGeneratingCommandOptions.HEADER_PASSTHROUGH)) {
|
||||||
|
addHeaderPassthroughOption(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IGenericClient newClient(CommandLine theCommandLine) throws ParseException {
|
||||||
|
IGenericClient client = super.newClient(theCommandLine);
|
||||||
|
|
||||||
|
if (theCommandLine.hasOption(HEADER_PASSTHROUGH)) {
|
||||||
|
client.registerInterceptor(
|
||||||
|
new AdditionalRequestHeadersInterceptor(
|
||||||
|
getAndParseOptionHeadersPassthrough(theCommandLine, HEADER_PASSTHROUGH)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addHeaderPassthroughOption(Options theOptions) {
|
||||||
|
addOptionalOption(theOptions, HEADER_PASSTHROUGH, HEADER_PASSTHROUGH_LONGOPT, HEADER_PASSTHROUGH_NAME,
|
||||||
|
"If specified, this argument specifies headers to include in the generated request");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns the optional pass-through header name and value
|
||||||
|
*/
|
||||||
|
protected Map<String, List<String>> getAndParseOptionHeadersPassthrough(
|
||||||
|
CommandLine theCommandLine, String theOptionName) throws ParseException {
|
||||||
|
|
||||||
|
if (! theCommandLine.hasOption(theOptionName)) {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, List<String>> headersMap = new HashMap<>();
|
||||||
|
for (String nextOptionValue: theCommandLine.getOptionValues(theOptionName)) {
|
||||||
|
Pair<String, String> nextHeader = parseNameValueParameter(":", theOptionName, nextOptionValue);
|
||||||
|
headersMap.compute(nextHeader.getKey(), (k, v) -> v == null ? new ArrayList<>() : v).add(nextHeader.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
return headersMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -23,6 +23,7 @@ package ca.uhn.fhir.cli;
|
||||||
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
||||||
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
|
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||||
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
|
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
|
||||||
import ca.uhn.fhir.model.api.IResource;
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
||||||
|
@ -39,6 +40,7 @@ import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||||
import ca.uhn.fhir.util.BundleUtil;
|
import ca.uhn.fhir.util.BundleUtil;
|
||||||
import ca.uhn.fhir.util.ResourceReferenceInfo;
|
import ca.uhn.fhir.util.ResourceReferenceInfo;
|
||||||
import ca.uhn.fhir.validation.FhirValidator;
|
import ca.uhn.fhir.validation.FhirValidator;
|
||||||
|
import ca.uhn.fhir.validation.SingleValidationMessage;
|
||||||
import ca.uhn.fhir.validation.ValidationResult;
|
import ca.uhn.fhir.validation.ValidationResult;
|
||||||
import org.apache.commons.cli.CommandLine;
|
import org.apache.commons.cli.CommandLine;
|
||||||
import org.apache.commons.cli.Option;
|
import org.apache.commons.cli.Option;
|
||||||
|
@ -61,18 +63,20 @@ import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipInputStream;
|
import java.util.zip.ZipInputStream;
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
public class ExampleDataUploader extends BaseCommand {
|
public class ExampleDataUploader extends BaseRequestGeneratingCommand {
|
||||||
// TODO: Don't use qualified names for loggers in HAPI CLI.
|
// TODO: Don't use qualified names for loggers in HAPI CLI.
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExampleDataUploader.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExampleDataUploader.class);
|
||||||
|
|
||||||
|
@ -200,8 +204,8 @@ public class ExampleDataUploader extends BaseCommand {
|
||||||
ourLog.info("Found example {} - {} - {} chars", nextEntry.getName(), parsed.getClass().getSimpleName(), exampleString.length());
|
ourLog.info("Found example {} - {} - {} chars", nextEntry.getName(), parsed.getClass().getSimpleName(), exampleString.length());
|
||||||
|
|
||||||
ValidationResult result = val.validateWithResult(parsed);
|
ValidationResult result = val.validateWithResult(parsed);
|
||||||
if (result.isSuccessful() == false) {
|
if (! result.isSuccessful()) {
|
||||||
ourLog.info("FAILED to validate example {} - {}", nextEntry.getName(), result.toString());
|
ourLog.info("FAILED to validate example {} - {}", nextEntry.getName(), result);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,8 +288,8 @@ public class ExampleDataUploader extends BaseCommand {
|
||||||
ourLog.info("Found example {} - {} - {} chars", nextEntry.getName(), parsed.getClass().getSimpleName(), exampleString.length());
|
ourLog.info("Found example {} - {} - {} chars", nextEntry.getName(), parsed.getClass().getSimpleName(), exampleString.length());
|
||||||
|
|
||||||
ValidationResult result = val.validateWithResult(parsed);
|
ValidationResult result = val.validateWithResult(parsed);
|
||||||
if (result.isSuccessful() == false) {
|
if (! result.isSuccessful()) {
|
||||||
ourLog.info("FAILED to validate example {} - {}", nextEntry.getName(), result.toString());
|
ourLog.info("FAILED to validate example {} - {}", nextEntry.getName(), result);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,15 +338,9 @@ public class ExampleDataUploader extends BaseCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Options getOptions() {
|
public Options getOptions() {
|
||||||
Options options = new Options();
|
Options options = super.getOptions();
|
||||||
Option opt;
|
Option opt;
|
||||||
|
|
||||||
addFhirVersionOption(options);
|
|
||||||
|
|
||||||
opt = new Option("t", "target", true, "Base URL for the target server (e.g. \"http://example.com/fhir\")");
|
|
||||||
opt.setRequired(true);
|
|
||||||
options.addOption(opt);
|
|
||||||
|
|
||||||
opt = new Option("l", "limit", true, "Sets a limit to the number of resources the uploader will try to upload");
|
opt = new Option("l", "limit", true, "Sets a limit to the number of resources the uploader will try to upload");
|
||||||
opt.setRequired(false);
|
opt.setRequired(false);
|
||||||
options.addOption(opt);
|
options.addOption(opt);
|
||||||
|
@ -356,11 +354,17 @@ public class ExampleDataUploader extends BaseCommand {
|
||||||
opt.setRequired(false);
|
opt.setRequired(false);
|
||||||
options.addOption(opt);
|
options.addOption(opt);
|
||||||
|
|
||||||
addBasicAuthOption(options);
|
|
||||||
|
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Collection<Object> getFilterOutVersions() {
|
||||||
|
Collection<Object> filterOutCollection = super.getFilterOutVersions();
|
||||||
|
filterOutCollection.add(FhirVersionEnum.R5);
|
||||||
|
return filterOutCollection;
|
||||||
|
}
|
||||||
|
|
||||||
private void processBundle(FhirContext ctx, IBaseBundle bundle) {
|
private void processBundle(FhirContext ctx, IBaseBundle bundle) {
|
||||||
switch (ctx.getVersion().getVersion()) {
|
switch (ctx.getVersion().getVersion()) {
|
||||||
case DSTU2:
|
case DSTU2:
|
||||||
|
@ -609,7 +613,7 @@ public class ExampleDataUploader extends BaseCommand {
|
||||||
String targetServer = theCommandLine.getOptionValue("t");
|
String targetServer = theCommandLine.getOptionValue("t");
|
||||||
if (isBlank(targetServer)) {
|
if (isBlank(targetServer)) {
|
||||||
throw new ParseException("No target server (-t) specified");
|
throw new ParseException("No target server (-t) specified");
|
||||||
} else if (targetServer.startsWith("http") == false && targetServer.startsWith("file") == false) {
|
} else if (! targetServer.startsWith("http") && ! targetServer.startsWith("file")) {
|
||||||
throw new ParseException("Invalid target server specified, must begin with 'http' or 'file'");
|
throw new ParseException("Invalid target server specified, must begin with 'http' or 'file'");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -697,7 +701,7 @@ public class ExampleDataUploader extends BaseCommand {
|
||||||
String nextTarget = nextRefResourceType + "/EX" + nextRefIdPart;
|
String nextTarget = nextRefResourceType + "/EX" + nextRefIdPart;
|
||||||
nextRef.getResourceReference().setResource(null);
|
nextRef.getResourceReference().setResource(null);
|
||||||
nextRef.getResourceReference().setReference(nextTarget);
|
nextRef.getResourceReference().setReference(nextTarget);
|
||||||
if (checkedTargets.add(nextTarget) == false) {
|
if (! checkedTargets.add(nextTarget)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ package ca.uhn.fhir.cli;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
import org.apache.commons.cli.Options;
|
import org.apache.commons.cli.Options;
|
||||||
import org.apache.commons.csv.CSVFormat;
|
import org.apache.commons.csv.CSVFormat;
|
||||||
import org.apache.commons.csv.CSVPrinter;
|
import org.apache.commons.csv.CSVPrinter;
|
||||||
|
@ -38,6 +39,7 @@ import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
|
@ -60,14 +62,10 @@ public class ExportConceptMapToCsvCommand extends AbstractImportExportCsvConcept
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Options getOptions() {
|
public Options getOptions() {
|
||||||
Options options = new Options();
|
Options options = super.getOptions();
|
||||||
|
|
||||||
this.addFhirVersionOption(options);
|
|
||||||
addBaseUrlOption(options);
|
|
||||||
addRequiredOption(options, CONCEPTMAP_URL_PARAM, CONCEPTMAP_URL_PARAM_LONGOPT, CONCEPTMAP_URL_PARAM_NAME, CONCEPTMAP_URL_PARAM_DESC);
|
addRequiredOption(options, CONCEPTMAP_URL_PARAM, CONCEPTMAP_URL_PARAM_LONGOPT, CONCEPTMAP_URL_PARAM_NAME, CONCEPTMAP_URL_PARAM_DESC);
|
||||||
addRequiredOption(options, FILE_PARAM, FILE_PARAM_LONGOPT, FILE_PARAM_NAME, FILE_PARAM_DESC);
|
addRequiredOption(options, FILE_PARAM, FILE_PARAM_LONGOPT, FILE_PARAM_NAME, FILE_PARAM_DESC);
|
||||||
addBasicAuthOption(options);
|
|
||||||
addVerboseLoggingOption(options);
|
|
||||||
|
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,18 +80,14 @@ public class ImportCsvToConceptMapCommand extends AbstractImportExportCsvConcept
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Options getOptions() {
|
public Options getOptions() {
|
||||||
Options options = new Options();
|
Options options = super.getOptions();
|
||||||
|
|
||||||
this.addFhirVersionOption(options);
|
|
||||||
addBaseUrlOption(options);
|
|
||||||
addRequiredOption(options, CONCEPTMAP_URL_PARAM, CONCEPTMAP_URL_PARAM_LONGOPT, CONCEPTMAP_URL_PARAM_NAME, CONCEPTMAP_URL_PARAM_DESC);
|
addRequiredOption(options, CONCEPTMAP_URL_PARAM, CONCEPTMAP_URL_PARAM_LONGOPT, CONCEPTMAP_URL_PARAM_NAME, CONCEPTMAP_URL_PARAM_DESC);
|
||||||
// </editor-fold desc="Additional parameters.">
|
// </editor-fold desc="Additional parameters.">
|
||||||
addOptionalOption(options, SOURCE_VALUE_SET_PARAM, SOURCE_VALUE_SET_PARAM_LONGOPT, SOURCE_VALUE_SET_PARAM_NAME, SOURCE_VALUE_SET_PARAM_DESC);
|
addOptionalOption(options, SOURCE_VALUE_SET_PARAM, SOURCE_VALUE_SET_PARAM_LONGOPT, SOURCE_VALUE_SET_PARAM_NAME, SOURCE_VALUE_SET_PARAM_DESC);
|
||||||
addOptionalOption(options, TARGET_VALUE_SET_PARAM, TARGET_VALUE_SET_PARAM_LONGOPT, TARGET_VALUE_SET_PARAM_NAME, TARGET_VALUE_SET_PARAM_DESC);
|
addOptionalOption(options, TARGET_VALUE_SET_PARAM, TARGET_VALUE_SET_PARAM_LONGOPT, TARGET_VALUE_SET_PARAM_NAME, TARGET_VALUE_SET_PARAM_DESC);
|
||||||
// </editor-fold>
|
// </editor-fold>
|
||||||
addRequiredOption(options, FILE_PARAM, FILE_PARAM_LONGOPT, FILE_PARAM_NAME, FILE_PARAM_DESC);
|
addRequiredOption(options, FILE_PARAM, FILE_PARAM_LONGOPT, FILE_PARAM_NAME, FILE_PARAM_DESC);
|
||||||
addBasicAuthOption(options);
|
|
||||||
addVerboseLoggingOption(options);
|
|
||||||
|
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@ import org.apache.commons.cli.ParseException;
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.commons.io.input.CountingInputStream;
|
import org.apache.commons.io.input.CountingInputStream;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||||
import org.hl7.fhir.instance.model.api.ICompositeType;
|
import org.hl7.fhir.instance.model.api.ICompositeType;
|
||||||
import org.hl7.fhir.r4.model.CodeSystem;
|
import org.hl7.fhir.r4.model.CodeSystem;
|
||||||
|
@ -48,7 +49,7 @@ import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
|
|
||||||
public class UploadTerminologyCommand extends BaseCommand {
|
public class UploadTerminologyCommand extends BaseRequestGeneratingCommand {
|
||||||
static final String UPLOAD_TERMINOLOGY = "upload-terminology";
|
static final String UPLOAD_TERMINOLOGY = "upload-terminology";
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(UploadTerminologyCommand.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(UploadTerminologyCommand.class);
|
||||||
private static final long DEFAULT_TRANSFER_SIZE_LIMIT = 10 * FileUtils.ONE_MB;
|
private static final long DEFAULT_TRANSFER_SIZE_LIMIT = 10 * FileUtils.ONE_MB;
|
||||||
|
@ -66,15 +67,11 @@ public class UploadTerminologyCommand extends BaseCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Options getOptions() {
|
public Options getOptions() {
|
||||||
Options options = new Options();
|
Options options = super.getOptions();
|
||||||
|
|
||||||
addFhirVersionOption(options);
|
|
||||||
addBaseUrlOption(options);
|
|
||||||
addRequiredOption(options, "u", "url", true, "The code system URL associated with this upload (e.g. " + ITermLoaderSvc.SCT_URI + ")");
|
addRequiredOption(options, "u", "url", true, "The code system URL associated with this upload (e.g. " + ITermLoaderSvc.SCT_URI + ")");
|
||||||
addOptionalOption(options, "d", "data", true, "Local file to use to upload (can be a raw file or a ZIP containing the raw file)");
|
addOptionalOption(options, "d", "data", true, "Local file to use to upload (can be a raw file or a ZIP containing the raw file)");
|
||||||
addOptionalOption(options, "m", "mode", true, "The upload mode: SNAPSHOT (default), ADD, REMOVE");
|
addOptionalOption(options, "m", "mode", true, "The upload mode: SNAPSHOT (default), ADD, REMOVE");
|
||||||
addBasicAuthOption(options);
|
|
||||||
addVerboseLoggingOption(options);
|
|
||||||
|
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
@ -101,33 +98,35 @@ public class UploadTerminologyCommand extends BaseCommand {
|
||||||
throw new ParseException("No data file provided");
|
throw new ParseException("No data file provided");
|
||||||
}
|
}
|
||||||
|
|
||||||
IGenericClient client = super.newClient(theCommandLine);
|
IGenericClient client = newClient(theCommandLine);
|
||||||
IBaseParameters inputParameters = ParametersUtil.newInstance(myFhirCtx);
|
|
||||||
|
|
||||||
if (theCommandLine.hasOption(VERBOSE_LOGGING_PARAM)) {
|
if (theCommandLine.hasOption(VERBOSE_LOGGING_PARAM)) {
|
||||||
client.registerInterceptor(new LoggingInterceptor(true));
|
client.registerInterceptor(new LoggingInterceptor(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String requestName = null;
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case SNAPSHOT:
|
case SNAPSHOT:
|
||||||
invokeOperation(termUrl, datafile, client, inputParameters, JpaConstants.OPERATION_UPLOAD_EXTERNAL_CODE_SYSTEM);
|
requestName = JpaConstants.OPERATION_UPLOAD_EXTERNAL_CODE_SYSTEM;
|
||||||
break;
|
break;
|
||||||
case ADD:
|
case ADD:
|
||||||
invokeOperation(termUrl, datafile, client, inputParameters, JpaConstants.OPERATION_APPLY_CODESYSTEM_DELTA_ADD);
|
requestName = JpaConstants.OPERATION_APPLY_CODESYSTEM_DELTA_ADD;
|
||||||
break;
|
break;
|
||||||
case REMOVE:
|
case REMOVE:
|
||||||
invokeOperation(termUrl, datafile, client, inputParameters, JpaConstants.OPERATION_APPLY_CODESYSTEM_DELTA_REMOVE);
|
requestName = JpaConstants.OPERATION_APPLY_CODESYSTEM_DELTA_REMOVE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
invokeOperation(termUrl, datafile, client, requestName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void invokeOperation(String theTermUrl, String[] theDatafile, IGenericClient theClient, IBaseParameters theInputParameters, String theOperationName) throws ParseException {
|
private void invokeOperation(String theTermUrl, String[] theDatafile, IGenericClient theClient, String theOperationName) throws ParseException {
|
||||||
|
IBaseParameters inputParameters = ParametersUtil.newInstance(myFhirCtx);
|
||||||
|
|
||||||
boolean isDeltaOperation =
|
boolean isDeltaOperation =
|
||||||
theOperationName.equals(JpaConstants.OPERATION_APPLY_CODESYSTEM_DELTA_ADD) ||
|
theOperationName.equals(JpaConstants.OPERATION_APPLY_CODESYSTEM_DELTA_ADD) ||
|
||||||
theOperationName.equals(JpaConstants.OPERATION_APPLY_CODESYSTEM_DELTA_REMOVE);
|
theOperationName.equals(JpaConstants.OPERATION_APPLY_CODESYSTEM_DELTA_REMOVE);
|
||||||
|
|
||||||
ParametersUtil.addParameterToParametersUri(myFhirCtx, theInputParameters, TerminologyUploaderProvider.PARAM_SYSTEM, theTermUrl);
|
ParametersUtil.addParameterToParametersUri(myFhirCtx, inputParameters, TerminologyUploaderProvider.PARAM_SYSTEM, theTermUrl);
|
||||||
|
|
||||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||||
ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream, Charsets.UTF_8);
|
ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream, Charsets.UTF_8);
|
||||||
|
@ -152,7 +151,7 @@ public class UploadTerminologyCommand extends BaseCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
CodeSystem resource = encoding.newParser(myFhirCtx).parseResource(CodeSystem.class, contents);
|
CodeSystem resource = encoding.newParser(myFhirCtx).parseResource(CodeSystem.class, contents);
|
||||||
ParametersUtil.addParameterToParameters(myFhirCtx, theInputParameters, TerminologyUploaderProvider.PARAM_CODESYSTEM, resource);
|
ParametersUtil.addParameterToParameters(myFhirCtx, inputParameters, TerminologyUploaderProvider.PARAM_CODESYSTEM, resource);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
@ -174,7 +173,7 @@ public class UploadTerminologyCommand extends BaseCommand {
|
||||||
|
|
||||||
ourLog.info("Adding ZIP file: {}", nextDataFile);
|
ourLog.info("Adding ZIP file: {}", nextDataFile);
|
||||||
String fileName = "file:" + nextDataFile;
|
String fileName = "file:" + nextDataFile;
|
||||||
addFileToRequestBundle(theInputParameters, fileName, IOUtils.toByteArray(fileInputStream));
|
addFileToRequestBundle(inputParameters, fileName, IOUtils.toByteArray(fileInputStream));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
@ -194,13 +193,13 @@ public class UploadTerminologyCommand extends BaseCommand {
|
||||||
byte[] compressedBytes = byteArrayOutputStream.toByteArray();
|
byte[] compressedBytes = byteArrayOutputStream.toByteArray();
|
||||||
ourLog.info("Compressed {} bytes in {} file(s) into {} bytes", FileUtil.formatFileSize(compressedSourceBytesCount), compressedFileCount, FileUtil.formatFileSize(compressedBytes.length));
|
ourLog.info("Compressed {} bytes in {} file(s) into {} bytes", FileUtil.formatFileSize(compressedSourceBytesCount), compressedFileCount, FileUtil.formatFileSize(compressedBytes.length));
|
||||||
|
|
||||||
addFileToRequestBundle(theInputParameters, "file:/files.zip", compressedBytes);
|
addFileToRequestBundle(inputParameters, "file:/files.zip", compressedBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
ourLog.info("Beginning upload - This may take a while...");
|
ourLog.info("Beginning upload - This may take a while...");
|
||||||
|
|
||||||
if (ourLog.isDebugEnabled() || "true".equals(System.getProperty("test"))) {
|
if (ourLog.isDebugEnabled() || "true".equals(System.getProperty("test"))) {
|
||||||
ourLog.info("Submitting parameters: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(theInputParameters));
|
ourLog.info("Submitting parameters: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inputParameters));
|
||||||
}
|
}
|
||||||
|
|
||||||
IBaseParameters response;
|
IBaseParameters response;
|
||||||
|
@ -209,7 +208,7 @@ public class UploadTerminologyCommand extends BaseCommand {
|
||||||
.operation()
|
.operation()
|
||||||
.onType(myFhirCtx.getResourceDefinition("CodeSystem").getImplementingClass())
|
.onType(myFhirCtx.getResourceDefinition("CodeSystem").getImplementingClass())
|
||||||
.named(theOperationName)
|
.named(theOperationName)
|
||||||
.withParameters(theInputParameters)
|
.withParameters(inputParameters)
|
||||||
.execute();
|
.execute();
|
||||||
} catch (BaseServerResponseException e) {
|
} catch (BaseServerResponseException e) {
|
||||||
if (e.getOperationOutcome() != null) {
|
if (e.getOperationOutcome() != null) {
|
||||||
|
|
|
@ -0,0 +1,119 @@
|
||||||
|
package ca.uhn.fhir.cli;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import org.apache.commons.cli.CommandLine;
|
||||||
|
import org.apache.commons.cli.Options;
|
||||||
|
import org.apache.commons.cli.ParseException;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
class BaseRequestGeneratingCommandTest {
|
||||||
|
|
||||||
|
private final BaseRequestGeneratingCommand tested = new BaseRequestGeneratingCommandChild();
|
||||||
|
|
||||||
|
private final List<BaseRequestGeneratingCommand.BaseRequestGeneratingCommandOptions> allOptions =
|
||||||
|
Arrays.asList(BaseRequestGeneratingCommand.BaseRequestGeneratingCommandOptions.values());
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getOptions() {
|
||||||
|
Options options = tested.getOptions();
|
||||||
|
assertEquals(6, options.getOptions().size());
|
||||||
|
assertTrue(options.hasShortOption(BaseCommand.FHIR_VERSION_PARAM));
|
||||||
|
assertTrue(options.hasShortOption(BaseCommand.BASE_URL_PARAM));
|
||||||
|
assertTrue(options.hasShortOption(BaseCommand.BASIC_AUTH_PARAM));
|
||||||
|
assertTrue(options.hasShortOption(BaseCommand.BEARER_TOKEN_PARAM_NAME));
|
||||||
|
assertTrue(options.hasShortOption(BaseCommand.VERBOSE_LOGGING_PARAM));
|
||||||
|
assertTrue(options.hasShortOption(BaseRequestGeneratingCommand.HEADER_PASSTHROUGH));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getSomeOptionsNoVersion() {
|
||||||
|
Options options = tested.getSomeOptions(
|
||||||
|
Collections.singleton(BaseRequestGeneratingCommand.BaseRequestGeneratingCommandOptions.VERSION));
|
||||||
|
assertEquals(5, options.getOptions().size());
|
||||||
|
assertTrue(options.hasShortOption(BaseCommand.BASE_URL_PARAM));
|
||||||
|
assertTrue(options.hasShortOption(BaseCommand.BASIC_AUTH_PARAM));
|
||||||
|
assertTrue(options.hasShortOption(BaseCommand.BEARER_TOKEN_PARAM_NAME));
|
||||||
|
assertTrue(options.hasShortOption(BaseCommand.VERBOSE_LOGGING_PARAM));
|
||||||
|
assertTrue(options.hasShortOption(BaseRequestGeneratingCommand.HEADER_PASSTHROUGH));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getSomeOptionsNoBaseUrl() {
|
||||||
|
Options options = tested.getSomeOptions(
|
||||||
|
Collections.singleton(BaseRequestGeneratingCommand.BaseRequestGeneratingCommandOptions.BASE_URL));
|
||||||
|
assertEquals(5, options.getOptions().size());
|
||||||
|
assertTrue(options.hasShortOption(BaseCommand.FHIR_VERSION_PARAM));
|
||||||
|
assertTrue(options.hasShortOption(BaseCommand.BASIC_AUTH_PARAM));
|
||||||
|
assertTrue(options.hasShortOption(BaseCommand.BEARER_TOKEN_PARAM_NAME));
|
||||||
|
assertTrue(options.hasShortOption(BaseCommand.VERBOSE_LOGGING_PARAM));
|
||||||
|
assertTrue(options.hasShortOption(BaseRequestGeneratingCommand.HEADER_PASSTHROUGH));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getSomeOptionsNoBasicAuth() {
|
||||||
|
Options options = tested.getSomeOptions(
|
||||||
|
Collections.singleton(BaseRequestGeneratingCommand.BaseRequestGeneratingCommandOptions.BASIC_AUTH));
|
||||||
|
assertEquals(4, options.getOptions().size());
|
||||||
|
assertTrue(options.hasShortOption(BaseCommand.FHIR_VERSION_PARAM));
|
||||||
|
assertTrue(options.hasShortOption(BaseCommand.BASE_URL_PARAM));
|
||||||
|
assertTrue(options.hasShortOption(BaseCommand.VERBOSE_LOGGING_PARAM));
|
||||||
|
assertTrue(options.hasShortOption(BaseRequestGeneratingCommand.HEADER_PASSTHROUGH));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getSomeOptionsNoVerboseLogging() {
|
||||||
|
Options options = tested.getSomeOptions(
|
||||||
|
Collections.singleton(BaseRequestGeneratingCommand.BaseRequestGeneratingCommandOptions.VERBOSE_LOGGING));
|
||||||
|
assertEquals(5, options.getOptions().size());
|
||||||
|
assertTrue(options.hasShortOption(BaseCommand.FHIR_VERSION_PARAM));
|
||||||
|
assertTrue(options.hasShortOption(BaseCommand.BASE_URL_PARAM));
|
||||||
|
assertTrue(options.hasShortOption(BaseCommand.BASIC_AUTH_PARAM));
|
||||||
|
assertTrue(options.hasShortOption(BaseCommand.BEARER_TOKEN_PARAM_NAME));
|
||||||
|
assertTrue(options.hasShortOption(BaseRequestGeneratingCommand.HEADER_PASSTHROUGH));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getSomeOptionsNoHeaderPassthrough() {
|
||||||
|
Options options = tested.getSomeOptions(
|
||||||
|
Collections.singleton(BaseRequestGeneratingCommand.BaseRequestGeneratingCommandOptions.HEADER_PASSTHROUGH));
|
||||||
|
assertEquals(5, options.getOptions().size());
|
||||||
|
assertTrue(options.hasShortOption(BaseCommand.FHIR_VERSION_PARAM));
|
||||||
|
assertTrue(options.hasShortOption(BaseCommand.BASE_URL_PARAM));
|
||||||
|
assertTrue(options.hasShortOption(BaseCommand.BASIC_AUTH_PARAM));
|
||||||
|
assertTrue(options.hasShortOption(BaseCommand.BEARER_TOKEN_PARAM_NAME));
|
||||||
|
assertTrue(options.hasShortOption(BaseCommand.VERBOSE_LOGGING_PARAM));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getSomeOptionsExcludeTwo() {
|
||||||
|
Options options = tested.getSomeOptions(Lists.newArrayList(
|
||||||
|
BaseRequestGeneratingCommand.BaseRequestGeneratingCommandOptions.VERSION,
|
||||||
|
BaseRequestGeneratingCommand.BaseRequestGeneratingCommandOptions.HEADER_PASSTHROUGH));
|
||||||
|
assertEquals(4, options.getOptions().size());
|
||||||
|
assertTrue(options.hasShortOption(BaseCommand.BASE_URL_PARAM));
|
||||||
|
assertTrue(options.hasShortOption(BaseCommand.BASIC_AUTH_PARAM));
|
||||||
|
assertTrue(options.hasShortOption(BaseCommand.BEARER_TOKEN_PARAM_NAME));
|
||||||
|
assertTrue(options.hasShortOption(BaseCommand.VERBOSE_LOGGING_PARAM));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static class BaseRequestGeneratingCommandChild extends BaseRequestGeneratingCommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCommandDescription() { return null; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCommandName() { return null; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(CommandLine theCommandLine) { }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
package ca.uhn.fhir.cli;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||||
|
import ca.uhn.fhir.rest.client.interceptor.CapturingInterceptor;
|
||||||
|
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
||||||
|
import org.apache.commons.cli.CommandLine;
|
||||||
|
import org.apache.commons.cli.DefaultParser;
|
||||||
|
import org.apache.commons.cli.ParseException;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.hasItems;
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
class ExampleDataUploaderTest {
|
||||||
|
|
||||||
|
private FhirContext myCtx = FhirContext.forR4();
|
||||||
|
|
||||||
|
@RegisterExtension
|
||||||
|
public final RestfulServerExtension myRestfulServerExtension = new RestfulServerExtension(myCtx);
|
||||||
|
|
||||||
|
private final CapturingInterceptor myCapturingInterceptor = new CapturingInterceptor();
|
||||||
|
private final ExampleDataUploader testedCommand = new RequestCapturingExampleDataUploader(myCapturingInterceptor);
|
||||||
|
|
||||||
|
private String inputFilePath;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void before() {
|
||||||
|
String resourcesPath = new File("src/test/resources").getAbsolutePath();
|
||||||
|
inputFilePath = resourcesPath + "/sample.json.zip";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHeaderPassthrough() throws ParseException {
|
||||||
|
String headerKey = "test-header-key";
|
||||||
|
String headerValue = "test header value";
|
||||||
|
|
||||||
|
String[] args = new String[] {
|
||||||
|
"-v", "r4", // BaseRequestGeneratingCommandTest required
|
||||||
|
"-t", "http://localhost:8000", // BaseRequestGeneratingCommandTest required
|
||||||
|
"-d", inputFilePath,
|
||||||
|
"-hp", headerKey + ":" + headerValue // optional
|
||||||
|
};
|
||||||
|
|
||||||
|
final CommandLine commandLine = new DefaultParser().parse(testedCommand.getOptions(), args, true);
|
||||||
|
testedCommand.run(commandLine);
|
||||||
|
|
||||||
|
assertNotNull(myCapturingInterceptor.getLastRequest());
|
||||||
|
Map<String, List<String>> allHeaders = myCapturingInterceptor.getLastRequest().getAllHeaders();
|
||||||
|
assertFalse(allHeaders.isEmpty());
|
||||||
|
|
||||||
|
assertTrue(allHeaders.containsKey(headerKey));
|
||||||
|
assertEquals(1, allHeaders.get(headerKey).size());
|
||||||
|
|
||||||
|
assertThat(allHeaders.get(headerKey), hasItems(headerValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static class RequestCapturingExampleDataUploader extends ExampleDataUploader {
|
||||||
|
private final CapturingInterceptor myCapturingInterceptor;
|
||||||
|
|
||||||
|
public RequestCapturingExampleDataUploader(CapturingInterceptor theCapturingInterceptor) {
|
||||||
|
myCapturingInterceptor = theCapturingInterceptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IGenericClient newClient(CommandLine theCommandLine) throws ParseException {
|
||||||
|
IGenericClient client = super.newClient(theCommandLine);
|
||||||
|
client.getInterceptorService().registerInterceptor(myCapturingInterceptor);
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,37 +1,80 @@
|
||||||
package ca.uhn.fhir.cli;
|
package ca.uhn.fhir.cli;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
|
import ca.uhn.fhir.rest.client.apache.ApacheRestfulClientFactory;
|
||||||
|
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||||
|
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
|
||||||
|
import ca.uhn.fhir.rest.client.impl.BaseClient;
|
||||||
|
import ca.uhn.fhir.rest.client.impl.GenericClient;
|
||||||
|
import ca.uhn.fhir.rest.client.impl.RestfulClientFactory;
|
||||||
|
import ca.uhn.fhir.rest.client.interceptor.CapturingInterceptor;
|
||||||
|
import ca.uhn.fhir.rest.server.interceptor.VerboseLoggingInterceptor;
|
||||||
import ca.uhn.fhir.test.BaseTest;
|
import ca.uhn.fhir.test.BaseTest;
|
||||||
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
|
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
|
||||||
import ca.uhn.fhir.jpa.term.UploadStatistics;
|
import ca.uhn.fhir.jpa.term.UploadStatistics;
|
||||||
import ca.uhn.fhir.jpa.term.api.ITermLoaderSvc;
|
import ca.uhn.fhir.jpa.term.api.ITermLoaderSvc;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
import ca.uhn.fhir.test.utilities.JettyUtil;
|
import ca.uhn.fhir.test.utilities.JettyUtil;
|
||||||
|
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
||||||
import com.google.common.base.Charsets;
|
import com.google.common.base.Charsets;
|
||||||
|
import org.apache.commons.cli.CommandLine;
|
||||||
|
import org.apache.commons.cli.DefaultParser;
|
||||||
|
import org.apache.commons.cli.Option;
|
||||||
|
import org.apache.commons.cli.Options;
|
||||||
|
import org.apache.commons.cli.ParseException;
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.commons.io.input.ReaderInputStream;
|
||||||
|
import org.apache.derby.iapi.types.ReaderToUTF8Stream;
|
||||||
|
import org.apache.http.HttpResponse;
|
||||||
|
import org.apache.http.ProtocolVersion;
|
||||||
|
import org.apache.http.client.HttpClient;
|
||||||
|
import org.apache.http.client.methods.HttpUriRequest;
|
||||||
|
import org.apache.http.message.BasicHeader;
|
||||||
|
import org.apache.http.message.BasicStatusLine;
|
||||||
import org.eclipse.jetty.server.Server;
|
import org.eclipse.jetty.server.Server;
|
||||||
import org.eclipse.jetty.servlet.ServletHandler;
|
import org.eclipse.jetty.servlet.ServletHandler;
|
||||||
import org.eclipse.jetty.servlet.ServletHolder;
|
import org.eclipse.jetty.servlet.ServletHolder;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||||
import org.hl7.fhir.r4.model.CodeSystem;
|
import org.hl7.fhir.r4.model.CodeSystem;
|
||||||
import org.hl7.fhir.r4.model.IdType;
|
import org.hl7.fhir.r4.model.IdType;
|
||||||
import org.hl7.fhir.r4.model.Patient;
|
import org.hl7.fhir.r4.model.Patient;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Nested;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.mockito.Captor;
|
import org.mockito.Captor;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Spy;
|
||||||
|
import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs;
|
||||||
|
import org.mockito.invocation.InvocationOnMock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import org.mockito.stubbing.Answer;
|
||||||
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
import org.testcontainers.shaded.org.bouncycastle.util.Arrays;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipOutputStream;
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.containsString;
|
import static org.hamcrest.CoreMatchers.containsString;
|
||||||
|
import static org.hamcrest.CoreMatchers.hasItems;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.greaterThan;
|
import static org.hamcrest.Matchers.greaterThan;
|
||||||
import static org.hamcrest.Matchers.matchesPattern;
|
import static org.hamcrest.Matchers.matchesPattern;
|
||||||
|
@ -323,6 +366,139 @@ public class UploadTerminologyCommandTest extends BaseTest {
|
||||||
assertThat(IOUtils.toByteArray(listOfDescriptors.get(0).getInputStream()).length, greaterThan(100));
|
assertThat(IOUtils.toByteArray(listOfDescriptors.get(0).getInputStream()).length, greaterThan(100));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
public class HeaderPassthroughOptionTests {
|
||||||
|
|
||||||
|
@RegisterExtension
|
||||||
|
public final RestfulServerExtension myRestfulServerExtension = new RestfulServerExtension(myCtx);
|
||||||
|
|
||||||
|
private final String headerKey1 = "test-header-key-1";
|
||||||
|
private final String headerValue1 = "test header value-1";
|
||||||
|
|
||||||
|
private final CapturingInterceptor myCapturingInterceptor = new CapturingInterceptor();
|
||||||
|
private final UploadTerminologyCommand testedCommand =
|
||||||
|
new RequestCapturingUploadTerminologyCommand(myCapturingInterceptor);
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void before() {
|
||||||
|
when(myTermLoaderSvc.loadCustom(eq("http://foo"), anyList(), any()))
|
||||||
|
.thenReturn(new UploadStatistics(100, new IdType("CodeSystem/101")));
|
||||||
|
|
||||||
|
TerminologyUploaderProvider provider = new TerminologyUploaderProvider(myCtx, myTermLoaderSvc);
|
||||||
|
myRestfulServerExtension.registerProvider(provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void oneHeader() throws Exception {
|
||||||
|
String[] args = new String[] {
|
||||||
|
"-v", "r4",
|
||||||
|
"-m", "SNAPSHOT",
|
||||||
|
"-t", "http://localhost:" + myRestfulServerExtension.getPort(),
|
||||||
|
"-u", "http://foo",
|
||||||
|
"-d", myConceptsFileName,
|
||||||
|
"-d", myHierarchyFileName,
|
||||||
|
"-hp", "\"" + headerKey1 + ":" + headerValue1 + "\""
|
||||||
|
};
|
||||||
|
|
||||||
|
writeConceptAndHierarchyFiles();
|
||||||
|
final CommandLine commandLine = new DefaultParser().parse(testedCommand.getOptions(), args, true);
|
||||||
|
testedCommand.run(commandLine);
|
||||||
|
|
||||||
|
assertNotNull(myCapturingInterceptor.getLastRequest());
|
||||||
|
Map<String, List<String>> allHeaders = myCapturingInterceptor.getLastRequest().getAllHeaders();
|
||||||
|
assertFalse(allHeaders.isEmpty());
|
||||||
|
|
||||||
|
assertTrue(allHeaders.containsKey(headerKey1));
|
||||||
|
assertEquals(1, allHeaders.get(headerKey1).size());
|
||||||
|
|
||||||
|
assertThat(allHeaders.get(headerKey1), hasItems(headerValue1));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void twoHeadersSameKey() throws Exception {
|
||||||
|
final String headerValue2 = "test header value-2";
|
||||||
|
|
||||||
|
String[] args = new String[] {
|
||||||
|
"-v", "r4",
|
||||||
|
"-m", "SNAPSHOT",
|
||||||
|
"-t", "http://localhost:" + myRestfulServerExtension.getPort(),
|
||||||
|
"-u", "http://foo",
|
||||||
|
"-d", myConceptsFileName,
|
||||||
|
"-d", myHierarchyFileName,
|
||||||
|
"-hp", "\"" + headerKey1 + ":" + headerValue1 + "\"",
|
||||||
|
"-hp", "\"" + headerKey1 + ":" + headerValue2 + "\""
|
||||||
|
};
|
||||||
|
|
||||||
|
writeConceptAndHierarchyFiles();
|
||||||
|
final CommandLine commandLine = new DefaultParser().parse(testedCommand.getOptions(), args, true);
|
||||||
|
testedCommand.run(commandLine);
|
||||||
|
|
||||||
|
assertNotNull(myCapturingInterceptor.getLastRequest());
|
||||||
|
Map<String, List<String>> allHeaders = myCapturingInterceptor.getLastRequest().getAllHeaders();
|
||||||
|
assertFalse(allHeaders.isEmpty());
|
||||||
|
assertEquals(2, allHeaders.get(headerKey1).size());
|
||||||
|
|
||||||
|
assertTrue(allHeaders.containsKey(headerKey1));
|
||||||
|
assertEquals(2, allHeaders.get(headerKey1).size());
|
||||||
|
|
||||||
|
assertEquals(headerValue1, allHeaders.get(headerKey1).get(0));
|
||||||
|
assertEquals(headerValue2, allHeaders.get(headerKey1).get(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void twoHeadersDifferentKeys() throws Exception {
|
||||||
|
final String headerKey2 = "test-header-key-2";
|
||||||
|
final String headerValue2 = "test header value-2";
|
||||||
|
|
||||||
|
String[] args = new String[] {
|
||||||
|
"-v", "r4",
|
||||||
|
"-m", "SNAPSHOT",
|
||||||
|
"-t", "http://localhost:" + myRestfulServerExtension.getPort(),
|
||||||
|
"-u", "http://foo",
|
||||||
|
"-d", myConceptsFileName,
|
||||||
|
"-d", myHierarchyFileName,
|
||||||
|
"-hp", "\"" + headerKey1 + ":" + headerValue1 + "\"",
|
||||||
|
"-hp", "\"" + headerKey2 + ":" + headerValue2 + "\""
|
||||||
|
};
|
||||||
|
|
||||||
|
writeConceptAndHierarchyFiles();
|
||||||
|
final CommandLine commandLine = new DefaultParser().parse(testedCommand.getOptions(), args, true);
|
||||||
|
testedCommand.run(commandLine);
|
||||||
|
|
||||||
|
assertNotNull(myCapturingInterceptor.getLastRequest());
|
||||||
|
Map<String, List<String>> allHeaders = myCapturingInterceptor.getLastRequest().getAllHeaders();
|
||||||
|
assertFalse(allHeaders.isEmpty());
|
||||||
|
|
||||||
|
assertTrue(allHeaders.containsKey(headerKey1));
|
||||||
|
assertEquals(1, allHeaders.get(headerKey1).size());
|
||||||
|
assertThat(allHeaders.get(headerKey1), hasItems(headerValue1));
|
||||||
|
|
||||||
|
assertTrue(allHeaders.containsKey(headerKey2));
|
||||||
|
assertEquals(1, allHeaders.get(headerKey2).size());
|
||||||
|
assertThat(allHeaders.get(headerKey2), hasItems(headerValue2));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private class RequestCapturingUploadTerminologyCommand extends UploadTerminologyCommand {
|
||||||
|
private CapturingInterceptor myCapturingInterceptor;
|
||||||
|
|
||||||
|
public RequestCapturingUploadTerminologyCommand(CapturingInterceptor theCapturingInterceptor) {
|
||||||
|
myCapturingInterceptor = theCapturingInterceptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IGenericClient newClient(CommandLine theCommandLine) throws ParseException {
|
||||||
|
IGenericClient client = super.newClient(theCommandLine);
|
||||||
|
client.getInterceptorService().registerInterceptor(myCapturingInterceptor);
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void writeArchiveFile(File... theFiles) throws IOException {
|
private void writeArchiveFile(File... theFiles) throws IOException {
|
||||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||||
ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream, Charsets.UTF_8);
|
ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream, Charsets.UTF_8);
|
||||||
|
|
Binary file not shown.
Loading…
Reference in New Issue