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:
juan.marchionatto 2021-07-05 16:08:03 -04:00
parent d762a07817
commit 5f36fe56b0
14 changed files with 712 additions and 102 deletions

View File

@ -39,18 +39,22 @@ import ca.uhn.fhir.util.OperationOutcomeUtil;
* @since 0.7
*/
public class ValidationResult {
public static final int ERROR_DISPLAY_LIMIT_DEFAULT = 1;
private final FhirContext myCtx;
private final boolean myIsSuccessful;
private final List<SingleValidationMessage> myMessages;
private int myErrorDisplayLimit = ERROR_DISPLAY_LIMIT_DEFAULT;
public ValidationResult(FhirContext theCtx, List<SingleValidationMessage> theMessages) {
boolean successful = true;
myCtx = theCtx;
myMessages = theMessages;
for (SingleValidationMessage next : myMessages) {
next.getSeverity();
if (next.getSeverity() == null || next.getSeverity().ordinal() > ResultSeverityEnum.WARNING.ordinal()) {
successful = false;
break;
}
}
myIsSuccessful = successful;
@ -72,22 +76,36 @@ public class ValidationResult {
return myIsSuccessful;
}
private String toDescription() {
StringBuilder b = new StringBuilder(100);
if (myMessages.size() > 0) {
if (myMessages.get(0).getSeverity() != null) {
b.append(myMessages.get(0).getSeverity().name());
if (myMessages.isEmpty()) {
return "No issues";
}
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(myMessages.get(0).getMessage());
b.append(nextMsg.getMessage());
b.append(" - ");
b.append(myMessages.get(0).getLocationString());
} else {
b.append("No issues");
b.append(nextMsg.getLocationString());
}
return b.toString();
}
/**
* @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
@ -156,4 +174,11 @@ public class ValidationResult {
public FhirContext getContext() {
return myCtx;
}
public int getErrorDisplayLimit() { return myErrorDisplayLimit; }
public void setErrorDisplayLimit(int theErrorDisplayLimit) { myErrorDisplayLimit = theErrorDisplayLimit; }
private static final String ourNewLine = System.getProperty("line.separator");
}

View File

@ -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;
}
}

View File

@ -24,6 +24,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.rest.client.api.IGenericClient;
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.Options;
import org.apache.commons.cli.ParseException;
@ -40,12 +41,13 @@ import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
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.
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 String file;
@Override
protected void addFhirVersionOption(Options theOptions) {
String versions = Arrays.stream(FhirVersionEnum.values())
.filter(t -> t != FhirVersionEnum.DSTU2_1 && t != FhirVersionEnum.DSTU2_HL7ORG && t != 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 Collection<Object> getFilterOutVersions() {
return Sets.newHashSet(FhirVersionEnum.DSTU2_1,
FhirVersionEnum.DSTU2_HL7ORG, FhirVersionEnum.DSTU2);
}
protected BufferedReader getBufferedReader() throws IOException {
@ -139,7 +138,7 @@ public abstract class AbstractImportExportCsvConceptMapCommand extends BaseComma
process();
}
protected void parseAdditionalParameters(CommandLine theCommandLine) throws ParseException {}
protected void parseAdditionalParameters(CommandLine theCommandLine) {}
protected abstract void process() throws ParseException, ExecutionException;

View File

@ -43,6 +43,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.fusesource.jansi.Ansi.ansi;
@ -216,43 +217,14 @@ public abstract class BaseApp {
}
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) {
String message = "Unknown command: " + theArgs[1];
System.err.println(message);
exitDueToProblem(message);
return;
}
logCommandUsage(command);
processHelp(theArgs);
return;
}
BaseCommand command = null;
for (BaseCommand nextCommand : ourCommands) {
if (nextCommand.getCommandName().equals(theArgs[0])) {
command = nextCommand;
break;
}
}
Optional<BaseCommand> commandOpt = parseCommand(theArgs);
if (! commandOpt.isPresent()) return;
if (command == null) {
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;
}
BaseCommand command = commandOpt.get();
myShutdownHook = new MyShutdownHook(command);
Runtime.getRuntime().addShutdownHook(myShutdownHook);
@ -304,11 +276,44 @@ public abstract class BaseApp {
} catch (Throwable t) {
ourLog.error("Error during execution: ", t);
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) {
if ("true".equals(System.getProperty("test"))) {
throw new Error(theDescription);
@ -380,6 +385,7 @@ public abstract class BaseApp {
configurator.setContext((LoggerContext) LoggerFactory.getILoggerFactory());
((LoggerContext) LoggerFactory.getILoggerFactory()).reset();
configurator.doConfigure(App.class.getResourceAsStream("/logback-cli-on.xml"));
ourLog.info("Logging configuration set from file logback-cli-on.xml");
} catch (JoranException e) {
e.printStackTrace();
}
@ -391,6 +397,7 @@ public abstract class BaseApp {
configurator.setContext((LoggerContext) LoggerFactory.getILoggerFactory());
((LoggerContext) LoggerFactory.getILoggerFactory()).reset();
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) {
e.printStackTrace();
}

View File

@ -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.interceptor.SimpleRequestHeaderInterceptor;
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.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
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.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
@ -109,7 +116,7 @@ public abstract class BaseCommand implements Comparable<BaseCommand> {
try {
retVal = reader.readLine();
} 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 {
retVal = new String(console.readPassword());
@ -120,9 +127,13 @@ public abstract class BaseCommand implements Comparable<BaseCommand> {
return retVal;
}
protected Collection<Object> getFilterOutVersions() {
return Sets.newHashSet(FhirVersionEnum.DSTU2_1, FhirVersionEnum.DSTU2_HL7ORG);
}
protected void addFhirVersionOption(Options theOptions) {
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())
.sorted()
.collect(Collectors.joining(", "));
@ -275,12 +286,33 @@ public abstract class BaseCommand implements Comparable<BaseCommand> {
byte[] basicAuth = optionValue.getBytes();
String base64EncodedBasicAuth = Base64Utils.encodeToString(basicAuth);
basicAuthHeaderValue = Constants.HEADER_AUTHORIZATION_VALPREFIX_BASIC + base64EncodedBasicAuth;
} else {
basicAuthHeaderValue = null;
}
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 {
String val = theCommandLine.getOptionValue(theOption);
if (isBlank(val)) {

View File

@ -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;
}
}

View File

@ -23,6 +23,7 @@ package ca.uhn.fhir.cli;
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
import ca.uhn.fhir.model.api.IResource;
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.ResourceReferenceInfo;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.SingleValidationMessage;
import ca.uhn.fhir.validation.ValidationResult;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
@ -61,18 +63,20 @@ import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import static org.apache.commons.lang3.StringUtils.isBlank;
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.
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());
ValidationResult result = val.validateWithResult(parsed);
if (result.isSuccessful() == false) {
ourLog.info("FAILED to validate example {} - {}", nextEntry.getName(), result.toString());
if (! result.isSuccessful()) {
ourLog.info("FAILED to validate example {} - {}", nextEntry.getName(), result);
continue;
}
@ -284,8 +288,8 @@ public class ExampleDataUploader extends BaseCommand {
ourLog.info("Found example {} - {} - {} chars", nextEntry.getName(), parsed.getClass().getSimpleName(), exampleString.length());
ValidationResult result = val.validateWithResult(parsed);
if (result.isSuccessful() == false) {
ourLog.info("FAILED to validate example {} - {}", nextEntry.getName(), result.toString());
if (! result.isSuccessful()) {
ourLog.info("FAILED to validate example {} - {}", nextEntry.getName(), result);
continue;
}
@ -334,15 +338,9 @@ public class ExampleDataUploader extends BaseCommand {
@Override
public Options getOptions() {
Options options = new Options();
Options options = super.getOptions();
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.setRequired(false);
options.addOption(opt);
@ -356,11 +354,17 @@ public class ExampleDataUploader extends BaseCommand {
opt.setRequired(false);
options.addOption(opt);
addBasicAuthOption(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) {
switch (ctx.getVersion().getVersion()) {
case DSTU2:
@ -609,7 +613,7 @@ public class ExampleDataUploader extends BaseCommand {
String targetServer = theCommandLine.getOptionValue("t");
if (isBlank(targetServer)) {
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'");
}
@ -697,7 +701,7 @@ public class ExampleDataUploader extends BaseCommand {
String nextTarget = nextRefResourceType + "/EX" + nextRefIdPart;
nextRef.getResourceReference().setResource(null);
nextRef.getResourceReference().setReference(nextTarget);
if (checkedTargets.add(nextTarget) == false) {
if (! checkedTargets.add(nextTarget)) {
continue;
}

View File

@ -22,6 +22,7 @@ package ca.uhn.fhir.cli;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import com.google.common.collect.Sets;
import org.apache.commons.cli.Options;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVPrinter;
@ -38,6 +39,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ExecutionException;
@ -60,14 +62,10 @@ public class ExportConceptMapToCsvCommand extends AbstractImportExportCsvConcept
@Override
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, FILE_PARAM, FILE_PARAM_LONGOPT, FILE_PARAM_NAME, FILE_PARAM_DESC);
addBasicAuthOption(options);
addVerboseLoggingOption(options);
return options;
}

View File

@ -80,18 +80,14 @@ public class ImportCsvToConceptMapCommand extends AbstractImportExportCsvConcept
@Override
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);
// </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, TARGET_VALUE_SET_PARAM, TARGET_VALUE_SET_PARAM_LONGOPT, TARGET_VALUE_SET_PARAM_NAME, TARGET_VALUE_SET_PARAM_DESC);
// </editor-fold>
addRequiredOption(options, FILE_PARAM, FILE_PARAM_LONGOPT, FILE_PARAM_NAME, FILE_PARAM_DESC);
addBasicAuthOption(options);
addVerboseLoggingOption(options);
return options;
}

View File

@ -38,6 +38,7 @@ import org.apache.commons.cli.ParseException;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
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.ICompositeType;
import org.hl7.fhir.r4.model.CodeSystem;
@ -48,7 +49,7 @@ import java.util.zip.ZipOutputStream;
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";
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;
@ -66,15 +67,11 @@ public class UploadTerminologyCommand extends BaseCommand {
@Override
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 + ")");
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");
addBasicAuthOption(options);
addVerboseLoggingOption(options);
return options;
}
@ -101,33 +98,35 @@ public class UploadTerminologyCommand extends BaseCommand {
throw new ParseException("No data file provided");
}
IGenericClient client = super.newClient(theCommandLine);
IBaseParameters inputParameters = ParametersUtil.newInstance(myFhirCtx);
IGenericClient client = newClient(theCommandLine);
if (theCommandLine.hasOption(VERBOSE_LOGGING_PARAM)) {
client.registerInterceptor(new LoggingInterceptor(true));
}
String requestName = null;
switch (mode) {
case SNAPSHOT:
invokeOperation(termUrl, datafile, client, inputParameters, JpaConstants.OPERATION_UPLOAD_EXTERNAL_CODE_SYSTEM);
requestName = JpaConstants.OPERATION_UPLOAD_EXTERNAL_CODE_SYSTEM;
break;
case ADD:
invokeOperation(termUrl, datafile, client, inputParameters, JpaConstants.OPERATION_APPLY_CODESYSTEM_DELTA_ADD);
requestName = JpaConstants.OPERATION_APPLY_CODESYSTEM_DELTA_ADD;
break;
case REMOVE:
invokeOperation(termUrl, datafile, client, inputParameters, JpaConstants.OPERATION_APPLY_CODESYSTEM_DELTA_REMOVE);
requestName = JpaConstants.OPERATION_APPLY_CODESYSTEM_DELTA_REMOVE;
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 =
theOperationName.equals(JpaConstants.OPERATION_APPLY_CODESYSTEM_DELTA_ADD) ||
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();
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);
ParametersUtil.addParameterToParameters(myFhirCtx, theInputParameters, TerminologyUploaderProvider.PARAM_CODESYSTEM, resource);
ParametersUtil.addParameterToParameters(myFhirCtx, inputParameters, TerminologyUploaderProvider.PARAM_CODESYSTEM, resource);
} else {
@ -174,7 +173,7 @@ public class UploadTerminologyCommand extends BaseCommand {
ourLog.info("Adding ZIP file: {}", nextDataFile);
String fileName = "file:" + nextDataFile;
addFileToRequestBundle(theInputParameters, fileName, IOUtils.toByteArray(fileInputStream));
addFileToRequestBundle(inputParameters, fileName, IOUtils.toByteArray(fileInputStream));
} else {
@ -194,13 +193,13 @@ public class UploadTerminologyCommand extends BaseCommand {
byte[] compressedBytes = byteArrayOutputStream.toByteArray();
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...");
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;
@ -209,7 +208,7 @@ public class UploadTerminologyCommand extends BaseCommand {
.operation()
.onType(myFhirCtx.getResourceDefinition("CodeSystem").getImplementingClass())
.named(theOperationName)
.withParameters(theInputParameters)
.withParameters(inputParameters)
.execute();
} catch (BaseServerResponseException e) {
if (e.getOperationOutcome() != null) {

View File

@ -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) { }
}
}

View File

@ -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;
}
}
}

View File

@ -1,37 +1,80 @@
package ca.uhn.fhir.cli;
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.jpa.provider.TerminologyUploaderProvider;
import ca.uhn.fhir.jpa.term.UploadStatistics;
import ca.uhn.fhir.jpa.term.api.ITermLoaderSvc;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.test.utilities.JettyUtil;
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
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.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.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
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.IdType;
import org.hl7.fhir.r4.model.Patient;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
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.stubbing.Answer;
import org.springframework.util.ReflectionUtils;
import org.testcontainers.shaded.org.bouncycastle.util.Arrays;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
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.ZipOutputStream;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.hasItems;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.greaterThan;
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));
}
@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 {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream, Charsets.UTF_8);