927 cli tooling is needed for creating and maintaining conceptmaps using csvs (#934)
CLI tooling for import/export of ConceptMaps using CSVs has been implemented.
This commit is contained in:
parent
d97fb8f5cf
commit
14a070a47e
|
@ -0,0 +1,369 @@
|
||||||
|
package ca.uhn.fhir.cli;
|
||||||
|
|
||||||
|
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 org.apache.commons.cli.CommandLine;
|
||||||
|
import org.apache.commons.cli.Options;
|
||||||
|
import org.apache.commons.cli.ParseException;
|
||||||
|
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||||
|
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.*;
|
||||||
|
|
||||||
|
public abstract class AbstractImportExportCsvConceptMapCommand extends BaseCommand {
|
||||||
|
// TODO: Don't use qualified names for loggers in HAPI CLI.
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(AbstractImportExportCsvConceptMapCommand.class);
|
||||||
|
|
||||||
|
protected static final String CONCEPTMAP_URL_PARAM = "u";
|
||||||
|
protected static final String CONCEPTMAP_URL_PARAM_LONGOPT = "url";
|
||||||
|
protected static final String CONCEPTMAP_URL_PARAM_NAME = "url";
|
||||||
|
protected static final String CONCEPTMAP_URL_PARAM_DESC = "The URL of the ConceptMap resource to be imported/exported (i.e. ConceptMap.url).";
|
||||||
|
protected static final String FILE_PARAM = "f";
|
||||||
|
protected static final String FILE_PARAM_LONGOPT = "filename";
|
||||||
|
protected static final String FILE_PARAM_NAME = "filename";
|
||||||
|
protected static final String FILE_PARAM_DESC = "The path and filename of the CSV file to be imported/exported (e.g. ./input.csv, ./output.csv, etc.).";
|
||||||
|
|
||||||
|
protected IGenericClient client;
|
||||||
|
protected String conceptMapUrl;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(CommandLine theCommandLine) throws ParseException, ExecutionException {
|
||||||
|
parseFhirContext(theCommandLine);
|
||||||
|
FhirContext ctx = getFhirContext();
|
||||||
|
|
||||||
|
String targetServer = theCommandLine.getOptionValue(BASE_URL_PARAM);
|
||||||
|
if (isBlank(targetServer)) {
|
||||||
|
throw new ParseException("No target server (-" + BASE_URL_PARAM + ") specified.");
|
||||||
|
} else if (!targetServer.startsWith("http") && !targetServer.startsWith("file")) {
|
||||||
|
throw new ParseException("Invalid target server specified, must begin with 'http' or 'file'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
conceptMapUrl = theCommandLine.getOptionValue(CONCEPTMAP_URL_PARAM);
|
||||||
|
if (isBlank(conceptMapUrl)) {
|
||||||
|
throw new ParseException("No ConceptMap URL (" + CONCEPTMAP_URL_PARAM + ") specified.");
|
||||||
|
} else {
|
||||||
|
ourLog.info("Specified ConceptMap URL (ConceptMap.url): {}", conceptMapUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
file = theCommandLine.getOptionValue(FILE_PARAM);
|
||||||
|
if (isBlank(file)) {
|
||||||
|
throw new ParseException("No file (" + FILE_PARAM + ") specified.");
|
||||||
|
}
|
||||||
|
if (!file.endsWith(".csv")) {
|
||||||
|
file = file.concat(".csv");
|
||||||
|
}
|
||||||
|
|
||||||
|
parseAdditionalParameters(theCommandLine);
|
||||||
|
|
||||||
|
client = super.newClient(theCommandLine);
|
||||||
|
fhirVersion = ctx.getVersion().getVersion();
|
||||||
|
if (fhirVersion != FhirVersionEnum.DSTU3
|
||||||
|
&& fhirVersion != FhirVersionEnum.R4) {
|
||||||
|
throw new ParseException("This command does not support FHIR version " + fhirVersion + ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (theCommandLine.hasOption(VERBOSE_LOGGING_PARAM)) {
|
||||||
|
client.registerInterceptor(new LoggingInterceptor(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
process();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void parseAdditionalParameters(CommandLine theCommandLine) throws ParseException {}
|
||||||
|
|
||||||
|
protected abstract void process() throws ParseException, ExecutionException;
|
||||||
|
|
||||||
|
protected enum Header {
|
||||||
|
SOURCE_CODE_SYSTEM,
|
||||||
|
SOURCE_CODE_SYSTEM_VERSION,
|
||||||
|
TARGET_CODE_SYSTEM,
|
||||||
|
TARGET_CODE_SYSTEM_VERSION,
|
||||||
|
SOURCE_CODE,
|
||||||
|
SOURCE_DISPLAY,
|
||||||
|
TARGET_CODE,
|
||||||
|
TARGET_DISPLAY,
|
||||||
|
EQUIVALENCE,
|
||||||
|
COMMENT
|
||||||
|
}
|
||||||
|
|
||||||
|
protected class TemporaryConceptMapGroup {
|
||||||
|
private String source;
|
||||||
|
private String sourceVersion;
|
||||||
|
private String target;
|
||||||
|
private String targetVersion;
|
||||||
|
|
||||||
|
public TemporaryConceptMapGroup() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public TemporaryConceptMapGroup(String theSource, String theSourceVersion, String theTarget, String theTargetVersion) {
|
||||||
|
this.source = theSource;
|
||||||
|
this.sourceVersion = theSourceVersion;
|
||||||
|
this.target = theTarget;
|
||||||
|
this.targetVersion = theTargetVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasSource() {
|
||||||
|
return isNotBlank(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSource() {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TemporaryConceptMapGroup setSource(String theSource) {
|
||||||
|
this.source = theSource;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasSourceVersion() {
|
||||||
|
return isNotBlank(sourceVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSourceVersion() {
|
||||||
|
return sourceVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TemporaryConceptMapGroup setSourceVersion(String theSourceVersion) {
|
||||||
|
this.sourceVersion = theSourceVersion;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasTarget() {
|
||||||
|
return isNotBlank(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTarget() {
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TemporaryConceptMapGroup setTarget(String theTarget) {
|
||||||
|
this.target = theTarget;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasTargetVersion() {
|
||||||
|
return isNotBlank(targetVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTargetVersion() {
|
||||||
|
return targetVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TemporaryConceptMapGroup setTargetVersion(String theTargetVersion) {
|
||||||
|
this.targetVersion = theTargetVersion;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasValues() {
|
||||||
|
return !isAllBlank(getSource(), getSourceVersion(), getTarget(), getTargetVersion());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
|
||||||
|
if (!(o instanceof TemporaryConceptMapGroup)) return false;
|
||||||
|
|
||||||
|
TemporaryConceptMapGroup that = (TemporaryConceptMapGroup) o;
|
||||||
|
|
||||||
|
return new EqualsBuilder()
|
||||||
|
.append(getSource(), that.getSource())
|
||||||
|
.append(getSourceVersion(), that.getSourceVersion())
|
||||||
|
.append(getTarget(), that.getTarget())
|
||||||
|
.append(getTargetVersion(), that.getTargetVersion())
|
||||||
|
.isEquals();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return new HashCodeBuilder(17, 37)
|
||||||
|
.append(getSource())
|
||||||
|
.append(getSourceVersion())
|
||||||
|
.append(getTarget())
|
||||||
|
.append(getTargetVersion())
|
||||||
|
.toHashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected class TemporaryConceptMapGroupElement {
|
||||||
|
private String code;
|
||||||
|
private String display;
|
||||||
|
|
||||||
|
public TemporaryConceptMapGroupElement() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public TemporaryConceptMapGroupElement(String theCode, String theDisplay) {
|
||||||
|
this.code = theCode;
|
||||||
|
this.display = theDisplay;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasCode() {
|
||||||
|
return isNotBlank(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TemporaryConceptMapGroupElement setCode(String theCode) {
|
||||||
|
this.code = theCode;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasDisplay() {
|
||||||
|
return isNotBlank(display);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplay() {
|
||||||
|
return display;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TemporaryConceptMapGroupElement setDisplay(String theDisplay) {
|
||||||
|
this.display = theDisplay;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasValues() {
|
||||||
|
return !isAllBlank(getCode(), getDisplay());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
|
||||||
|
if (!(o instanceof TemporaryConceptMapGroupElement)) return false;
|
||||||
|
|
||||||
|
TemporaryConceptMapGroupElement that = (TemporaryConceptMapGroupElement) o;
|
||||||
|
|
||||||
|
return new EqualsBuilder()
|
||||||
|
.append(getCode(), that.getCode())
|
||||||
|
.append(getDisplay(), that.getDisplay())
|
||||||
|
.isEquals();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return new HashCodeBuilder(17, 37)
|
||||||
|
.append(getCode())
|
||||||
|
.append(getDisplay())
|
||||||
|
.toHashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected class TemporaryConceptMapGroupElementTarget {
|
||||||
|
private String code;
|
||||||
|
private String display;
|
||||||
|
private String equivalence;
|
||||||
|
private String comment;
|
||||||
|
|
||||||
|
public TemporaryConceptMapGroupElementTarget() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public TemporaryConceptMapGroupElementTarget(String theCode, String theDisplay, String theEquivalence, String theComment) {
|
||||||
|
this.code = theCode;
|
||||||
|
this.display = theDisplay;
|
||||||
|
this.equivalence = theEquivalence;
|
||||||
|
this.comment = theComment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasCode() {
|
||||||
|
return isNotBlank(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TemporaryConceptMapGroupElementTarget setCode(String theCode) {
|
||||||
|
this.code = theCode;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasDisplay() {
|
||||||
|
return isNotBlank(display);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplay() {
|
||||||
|
return display;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TemporaryConceptMapGroupElementTarget setDisplay(String theDisplay) {
|
||||||
|
this.display = theDisplay;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasEquivalence() {
|
||||||
|
return isNotBlank(equivalence);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEquivalence() {
|
||||||
|
return equivalence;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TemporaryConceptMapGroupElementTarget setEquivalence(String theEquivalence) {
|
||||||
|
this.equivalence = theEquivalence;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasComment() {
|
||||||
|
return isNotBlank(comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getComment() {
|
||||||
|
return comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TemporaryConceptMapGroupElementTarget setComment(String theComment) {
|
||||||
|
this.comment = theComment;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasValues() {
|
||||||
|
return !isAllBlank(getCode(), getDisplay(), getEquivalence(), getComment());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
|
||||||
|
if (!(o instanceof TemporaryConceptMapGroupElementTarget)) return false;
|
||||||
|
|
||||||
|
TemporaryConceptMapGroupElementTarget that = (TemporaryConceptMapGroupElementTarget) o;
|
||||||
|
|
||||||
|
return new EqualsBuilder()
|
||||||
|
.append(getCode(), that.getCode())
|
||||||
|
.append(getDisplay(), that.getDisplay())
|
||||||
|
.append(getEquivalence(), that.getEquivalence())
|
||||||
|
.append(getComment(), that.getComment())
|
||||||
|
.isEquals();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return new HashCodeBuilder(17, 37)
|
||||||
|
.append(getCode())
|
||||||
|
.append(getDisplay())
|
||||||
|
.append(getEquivalence())
|
||||||
|
.append(getComment())
|
||||||
|
.toHashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -132,6 +132,8 @@ public abstract class BaseApp {
|
||||||
commands.add(new WebsocketSubscribeCommand());
|
commands.add(new WebsocketSubscribeCommand());
|
||||||
commands.add(new UploadTerminologyCommand());
|
commands.add(new UploadTerminologyCommand());
|
||||||
commands.add(new IgPackUploader());
|
commands.add(new IgPackUploader());
|
||||||
|
commands.add(new ExportConceptMapToCsvCommand());
|
||||||
|
commands.add(new ImportCsvToConceptMapCommand());
|
||||||
return commands;
|
return commands;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,21 +57,42 @@ import java.util.zip.GZIPInputStream;
|
||||||
import static org.apache.commons.lang3.StringUtils.*;
|
import static org.apache.commons.lang3.StringUtils.*;
|
||||||
|
|
||||||
public abstract class BaseCommand implements Comparable<BaseCommand> {
|
public abstract class BaseCommand implements Comparable<BaseCommand> {
|
||||||
public static final String BASE_URL_PARAM = "t";
|
// TODO: Don't use qualified names for loggers in HAPI CLI.
|
||||||
public static final String BASIC_AUTH_OPTION = "b";
|
|
||||||
public static final String BASIC_AUTH_LONGOPT = "basic-auth";
|
|
||||||
public static final String BEARER_TOKEN_LONGOPT = "bearer-token";
|
|
||||||
public static final String FHIR_VERSION_OPTION = "v";
|
|
||||||
private static final Logger ourLog = LoggerFactory.getLogger(BaseCommand.class);
|
private static final Logger ourLog = LoggerFactory.getLogger(BaseCommand.class);
|
||||||
private FhirContext myFhirCtx;
|
|
||||||
|
protected static final String BASE_URL_PARAM = "t";
|
||||||
|
protected static final String BASE_URL_PARAM_LONGOPT = "target";
|
||||||
|
protected static final String BASE_URL_PARAM_NAME = "target";
|
||||||
|
protected static final String BASE_URL_PARAM_DESC = "Base URL for the target server (e.g. \"http://example.com/fhir\").";
|
||||||
|
protected static final String BASIC_AUTH_PARAM = "b";
|
||||||
|
protected static final String BASIC_AUTH_PARAM_LONGOPT = "basic-auth";
|
||||||
|
protected static final String BASIC_AUTH_PARAM_NAME = "basic-auth";
|
||||||
|
protected static final String BASIC_AUTH_PARAM_DESC = "If specified, this parameter supplies a username and password (in the format \"username:password\") to include in an HTTP Basic Auth header.";
|
||||||
|
protected static final String BEARER_TOKEN_PARAM_LONGOPT = "bearer-token";
|
||||||
|
protected static final String BEARER_TOKEN_PARAM_NAME = "bearer-token";
|
||||||
|
protected static final String BEARER_TOKEN_PARAM_DESC = "If specified, this parameter supplies a Bearer Token to supply with the request.";
|
||||||
|
protected static final String FHIR_VERSION_PARAM = "v";
|
||||||
|
protected static final String FHIR_VERSION_PARAM_LONGOPT = "fhir-version";
|
||||||
|
protected static final String FHIR_VERSION_PARAM_NAME = "version";
|
||||||
|
protected static final String FHIR_VERSION_PARAM_DESC = "The FHIR version being used. Valid values: ";
|
||||||
|
protected static final String VERBOSE_LOGGING_PARAM = "l";
|
||||||
|
protected static final String VERBOSE_LOGGING_PARAM_LONGOPT = "logging";
|
||||||
|
protected static final String VERBOSE_LOGGING_PARAM_DESC = "If specified, verbose logging will be used.";
|
||||||
|
|
||||||
|
|
||||||
|
protected FhirContext myFhirCtx;
|
||||||
|
|
||||||
public BaseCommand() {
|
public BaseCommand() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void addBaseUrlOption(Options theOptions) {
|
||||||
|
addRequiredOption(theOptions, BASE_URL_PARAM, BASE_URL_PARAM_LONGOPT, BASE_URL_PARAM_NAME, BASE_URL_PARAM_DESC);
|
||||||
|
}
|
||||||
|
|
||||||
protected void addBasicAuthOption(Options theOptions) {
|
protected void addBasicAuthOption(Options theOptions) {
|
||||||
addOptionalOption(theOptions, BASIC_AUTH_OPTION, BASIC_AUTH_LONGOPT, true, "If specified, this parameter supplies a username and password (in the format \"username:password\") to include in an HTTP Basic Auth header");
|
addOptionalOption(theOptions, BASIC_AUTH_PARAM, BASIC_AUTH_PARAM_LONGOPT, BASIC_AUTH_PARAM_NAME, BASIC_AUTH_PARAM_DESC);
|
||||||
addOptionalOption(theOptions, null, BEARER_TOKEN_LONGOPT, true, "If specified, this parameter supplies a Bearer Token to supply with the request");
|
addOptionalOption(theOptions, null, BEARER_TOKEN_PARAM_LONGOPT, BEARER_TOKEN_PARAM_NAME, BEARER_TOKEN_PARAM_DESC);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void addFhirVersionOption(Options theOptions) {
|
protected void addFhirVersionOption(Options theOptions) {
|
||||||
|
@ -80,7 +101,11 @@ public abstract class BaseCommand implements Comparable<BaseCommand> {
|
||||||
.map(t -> t.name().toLowerCase())
|
.map(t -> t.name().toLowerCase())
|
||||||
.sorted()
|
.sorted()
|
||||||
.collect(Collectors.joining(", "));
|
.collect(Collectors.joining(", "));
|
||||||
addRequiredOption(theOptions, FHIR_VERSION_OPTION, "fhir-version", "version", "The FHIR version being used. Valid values: " + versions);
|
addRequiredOption(theOptions, FHIR_VERSION_PARAM, FHIR_VERSION_PARAM_LONGOPT, FHIR_VERSION_PARAM_NAME, FHIR_VERSION_PARAM_DESC + versions);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addVerboseLoggingOption(Options theOptions) {
|
||||||
|
addOptionalOption(theOptions, VERBOSE_LOGGING_PARAM, VERBOSE_LOGGING_PARAM_LONGOPT, false, VERBOSE_LOGGING_PARAM_DESC);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addOption(Options theOptions, boolean theRequired, String theOpt, String theLong, boolean theHasArgument, String theArgumentName, String theDescription) {
|
private void addOption(Options theOptions, boolean theRequired, String theOpt, String theLong, boolean theHasArgument, String theArgumentName, String theDescription) {
|
||||||
|
@ -117,9 +142,7 @@ public abstract class BaseCommand implements Comparable<BaseCommand> {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void addRequiredOption(Options theOptions, String theOpt, String theLong, String theArgumentName, String theDescription) {
|
protected void addRequiredOption(Options theOptions, String theOpt, String theLong, String theArgumentName, String theDescription) {
|
||||||
boolean hasArgument = isNotBlank(theArgumentName);
|
addOption(theOptions, true, theOpt, theLong, isNotBlank(theArgumentName), theArgumentName, theDescription);
|
||||||
boolean required = true;
|
|
||||||
addOption(theOptions, required, theOpt, theLong, hasArgument, theArgumentName, theDescription);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -182,7 +205,7 @@ public abstract class BaseCommand implements Comparable<BaseCommand> {
|
||||||
* @return Returns the complete authorization header value using the "-b" option
|
* @return Returns the complete authorization header value using the "-b" option
|
||||||
*/
|
*/
|
||||||
protected String getAndParseOptionBasicAuthHeader(CommandLine theCommandLine) {
|
protected String getAndParseOptionBasicAuthHeader(CommandLine theCommandLine) {
|
||||||
return getAndParseOptionBasicAuthHeader(theCommandLine, BASIC_AUTH_OPTION);
|
return getAndParseOptionBasicAuthHeader(theCommandLine, BASIC_AUTH_PARAM);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -307,12 +330,17 @@ public abstract class BaseCommand implements Comparable<BaseCommand> {
|
||||||
return inputFiles;
|
return inputFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected IGenericClient newClient(CommandLine theCommandLine) {
|
protected IGenericClient newClient(CommandLine theCommandLine) throws ParseException {
|
||||||
return newClient(theCommandLine, BASE_URL_PARAM, BASIC_AUTH_OPTION, BEARER_TOKEN_LONGOPT);
|
return newClient(theCommandLine, BASE_URL_PARAM, BASIC_AUTH_PARAM, BEARER_TOKEN_PARAM_LONGOPT);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected IGenericClient newClient(CommandLine theCommandLine, String theBaseUrlParamName, String theBasicAuthOptionName, String theBearerTokenOptionName) {
|
protected IGenericClient newClient(CommandLine theCommandLine, String theBaseUrlParamName, String theBasicAuthOptionName, String theBearerTokenOptionName) throws ParseException {
|
||||||
String baseUrl = theCommandLine.getOptionValue(theBaseUrlParamName);
|
String baseUrl = theCommandLine.getOptionValue(theBaseUrlParamName);
|
||||||
|
if (isBlank(baseUrl)) {
|
||||||
|
throw new ParseException("No target server (-" + BASE_URL_PARAM + ") specified.");
|
||||||
|
} else if (!baseUrl.startsWith("http") && !baseUrl.startsWith("file")) {
|
||||||
|
throw new ParseException("Invalid target server specified, must begin with 'http' or 'file'.");
|
||||||
|
}
|
||||||
|
|
||||||
myFhirCtx.getRestfulClientFactory().setSocketTimeout(10 * 60 * 1000);
|
myFhirCtx.getRestfulClientFactory().setSocketTimeout(10 * 60 * 1000);
|
||||||
IGenericClient retVal = myFhirCtx.newRestfulGenericClient(baseUrl);
|
IGenericClient retVal = myFhirCtx.newRestfulGenericClient(baseUrl);
|
||||||
|
@ -333,9 +361,9 @@ public abstract class BaseCommand implements Comparable<BaseCommand> {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void parseFhirContext(CommandLine theCommandLine) throws ParseException {
|
protected void parseFhirContext(CommandLine theCommandLine) throws ParseException {
|
||||||
String version = theCommandLine.getOptionValue(FHIR_VERSION_OPTION);
|
String version = theCommandLine.getOptionValue(FHIR_VERSION_PARAM);
|
||||||
if (isBlank(version)) {
|
if (isBlank(version)) {
|
||||||
throw new ParseException("Missing required option: -" + FHIR_VERSION_OPTION);
|
throw new ParseException("Missing required option: -" + FHIR_VERSION_PARAM);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -348,5 +376,4 @@ public abstract class BaseCommand implements Comparable<BaseCommand> {
|
||||||
|
|
||||||
|
|
||||||
public abstract void run(CommandLine theCommandLine) throws ParseException, ExecutionException;
|
public abstract void run(CommandLine theCommandLine) throws ParseException, ExecutionException;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,7 +63,7 @@ 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 BaseCommand {
|
||||||
|
// 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);
|
||||||
|
|
||||||
private IBaseBundle getBundleFromFile(Integer theLimit, File theSuppliedFile, FhirContext theCtx) throws ParseException, IOException {
|
private IBaseBundle getBundleFromFile(Integer theLimit, File theSuppliedFile, FhirContext theCtx) throws ParseException, IOException {
|
||||||
|
|
|
@ -0,0 +1,196 @@
|
||||||
|
package ca.uhn.fhir.cli;
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - Command Line Client - API
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2018 University Health Network
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
import org.apache.commons.cli.Options;
|
||||||
|
import org.apache.commons.cli.ParseException;
|
||||||
|
import org.apache.commons.csv.CSVFormat;
|
||||||
|
import org.apache.commons.csv.CSVPrinter;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.hl7.fhir.r4.model.Bundle;
|
||||||
|
import org.hl7.fhir.r4.model.ConceptMap;
|
||||||
|
import org.hl7.fhir.r4.model.ConceptMap.ConceptMapGroupComponent;
|
||||||
|
import org.hl7.fhir.r4.model.ConceptMap.SourceElementComponent;
|
||||||
|
|
||||||
|
import java.io.BufferedWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||||
|
|
||||||
|
public class ExportConceptMapToCsvCommand extends AbstractImportExportCsvConceptMapCommand {
|
||||||
|
// TODO: Don't use qualified names for loggers in HAPI CLI.
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExportConceptMapToCsvCommand.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCommandDescription() {
|
||||||
|
return "Exports a specific ConceptMap resource to a CSV file.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCommandName() {
|
||||||
|
return "export-conceptmap-to-csv";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Options getOptions() {
|
||||||
|
Options options = new Options();
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void process() throws ParseException {
|
||||||
|
searchForConceptMapByUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void searchForConceptMapByUrl() {
|
||||||
|
ourLog.info("Searching for ConceptMap with specified URL (i.e. ConceptMap.url): {}", conceptMapUrl);
|
||||||
|
if (fhirVersion == FhirVersionEnum.DSTU3) {
|
||||||
|
org.hl7.fhir.dstu3.model.Bundle response = client
|
||||||
|
.search()
|
||||||
|
.forResource(org.hl7.fhir.dstu3.model.ConceptMap.class)
|
||||||
|
.where(org.hl7.fhir.dstu3.model.ConceptMap.URL.matches().value(conceptMapUrl))
|
||||||
|
.returnBundle(org.hl7.fhir.dstu3.model.Bundle.class)
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
if (response.hasEntry()) {
|
||||||
|
ourLog.info("Found ConceptMap with specified URL (i.e. ConceptMap.url): {}", conceptMapUrl);
|
||||||
|
org.hl7.fhir.dstu3.model.ConceptMap conceptMap = (org.hl7.fhir.dstu3.model.ConceptMap) response.getEntryFirstRep().getResource();
|
||||||
|
convertConceptMapToCsv(conceptMap);
|
||||||
|
} else {
|
||||||
|
ourLog.info("No ConceptMap exists with specified URL (i.e. ConceptMap.url): {}", conceptMapUrl);
|
||||||
|
}
|
||||||
|
} else if (fhirVersion == FhirVersionEnum.R4) {
|
||||||
|
Bundle response = client
|
||||||
|
.search()
|
||||||
|
.forResource(ConceptMap.class)
|
||||||
|
.where(ConceptMap.URL.matches().value(conceptMapUrl))
|
||||||
|
.returnBundle(Bundle.class)
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
if (response.hasEntry()) {
|
||||||
|
ourLog.info("Found ConceptMap with specified URL (i.e. ConceptMap.url): {}", conceptMapUrl);
|
||||||
|
ConceptMap conceptMap = (ConceptMap) response.getEntryFirstRep().getResource();
|
||||||
|
convertConceptMapToCsv(conceptMap);
|
||||||
|
} else {
|
||||||
|
ourLog.info("No ConceptMap exists with specified URL (i.e. ConceptMap.url): {}", conceptMapUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void convertConceptMapToCsv(org.hl7.fhir.dstu3.model.ConceptMap theConceptMap) {
|
||||||
|
ourLog.info("Exporting ConceptMap to CSV...");
|
||||||
|
BufferedWriter bufferedWriter = null;
|
||||||
|
CSVPrinter csvPrinter = null;
|
||||||
|
try {
|
||||||
|
bufferedWriter = Files.newBufferedWriter(Paths.get(file));
|
||||||
|
csvPrinter = new CSVPrinter(
|
||||||
|
bufferedWriter,
|
||||||
|
CSVFormat
|
||||||
|
.DEFAULT
|
||||||
|
.withRecordSeparator("\n")
|
||||||
|
.withHeader(Header.class));
|
||||||
|
|
||||||
|
for (org.hl7.fhir.dstu3.model.ConceptMap.ConceptMapGroupComponent group : theConceptMap.getGroup()) {
|
||||||
|
for (org.hl7.fhir.dstu3.model.ConceptMap.SourceElementComponent element : group.getElement()) {
|
||||||
|
for (org.hl7.fhir.dstu3.model.ConceptMap.TargetElementComponent target : element.getTarget()) {
|
||||||
|
|
||||||
|
List<String> columns = new ArrayList<>();
|
||||||
|
columns.add(defaultString(group.getSource()));
|
||||||
|
columns.add(defaultString(group.getSourceVersion()));
|
||||||
|
columns.add(defaultString(group.getTarget()));
|
||||||
|
columns.add(defaultString(group.getTargetVersion()));
|
||||||
|
columns.add(defaultString(element.getCode()));
|
||||||
|
columns.add(defaultString(element.getDisplay()));
|
||||||
|
columns.add(defaultString(target.getCode()));
|
||||||
|
columns.add(defaultString(target.getDisplay()));
|
||||||
|
columns.add(defaultString(target.getEquivalence().toCode()));
|
||||||
|
columns.add(defaultString(target.getComment()));
|
||||||
|
|
||||||
|
csvPrinter.print(columns);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
throw new InternalErrorException(ioe);
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeQuietly(csvPrinter);
|
||||||
|
IOUtils.closeQuietly(bufferedWriter);
|
||||||
|
}
|
||||||
|
ourLog.info("Finished exporting to {}", file);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void convertConceptMapToCsv(ConceptMap theConceptMap) {
|
||||||
|
ourLog.info("Exporting ConceptMap to CSV...");
|
||||||
|
Writer writer = null;
|
||||||
|
CSVPrinter csvPrinter = null;
|
||||||
|
try {
|
||||||
|
writer = Files.newBufferedWriter(Paths.get(file));
|
||||||
|
csvPrinter = new CSVPrinter(
|
||||||
|
writer,
|
||||||
|
CSVFormat
|
||||||
|
.DEFAULT
|
||||||
|
.withRecordSeparator("\n")
|
||||||
|
.withHeader(Header.class));
|
||||||
|
|
||||||
|
for (ConceptMapGroupComponent group : theConceptMap.getGroup()) {
|
||||||
|
for (SourceElementComponent element : group.getElement()) {
|
||||||
|
for (ConceptMap.TargetElementComponent target : element.getTarget()) {
|
||||||
|
|
||||||
|
List<String> columns = new ArrayList<>();
|
||||||
|
columns.add(defaultString(group.getSource()));
|
||||||
|
columns.add(defaultString(group.getSourceVersion()));
|
||||||
|
columns.add(defaultString(group.getTarget()));
|
||||||
|
columns.add(defaultString(group.getTargetVersion()));
|
||||||
|
columns.add(defaultString(element.getCode()));
|
||||||
|
columns.add(defaultString(element.getDisplay()));
|
||||||
|
columns.add(defaultString(target.getCode()));
|
||||||
|
columns.add(defaultString(target.getDisplay()));
|
||||||
|
columns.add(defaultString(target.getEquivalence().toCode()));
|
||||||
|
columns.add(defaultString(target.getComment()));
|
||||||
|
|
||||||
|
csvPrinter.printRecord(columns);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
throw new InternalErrorException(ioe);
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeQuietly(csvPrinter);
|
||||||
|
IOUtils.closeQuietly(writer);
|
||||||
|
}
|
||||||
|
ourLog.info("Finished exporting to {}", file);
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,7 +23,6 @@ package ca.uhn.fhir.cli;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.igpacks.parser.IgPackParserDstu3;
|
import ca.uhn.fhir.igpacks.parser.IgPackParserDstu3;
|
||||||
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||||
import net.sf.ehcache.transaction.xa.commands.Command;
|
|
||||||
import org.apache.commons.cli.CommandLine;
|
import org.apache.commons.cli.CommandLine;
|
||||||
import org.apache.commons.cli.Option;
|
import org.apache.commons.cli.Option;
|
||||||
import org.apache.commons.cli.Options;
|
import org.apache.commons.cli.Options;
|
||||||
|
@ -42,6 +41,7 @@ import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
public class IgPackUploader extends BaseCommand {
|
public class IgPackUploader extends BaseCommand {
|
||||||
|
// TODO: Don't use qualified names for loggers in HAPI CLI.
|
||||||
private static final Logger ourLog = LoggerFactory.getLogger(IgPackUploader.class);
|
private static final Logger ourLog = LoggerFactory.getLogger(IgPackUploader.class);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,336 @@
|
||||||
|
package ca.uhn.fhir.cli;
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - Command Line Client - API
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2018 University Health Network
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||||
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
import org.apache.commons.cli.CommandLine;
|
||||||
|
import org.apache.commons.cli.Options;
|
||||||
|
import org.apache.commons.cli.ParseException;
|
||||||
|
import org.apache.commons.csv.CSVFormat;
|
||||||
|
import org.apache.commons.csv.CSVParser;
|
||||||
|
import org.apache.commons.csv.CSVRecord;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.hl7.fhir.convertors.VersionConvertor_30_40;
|
||||||
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
|
import org.hl7.fhir.r4.model.ConceptMap;
|
||||||
|
import org.hl7.fhir.r4.model.ConceptMap.ConceptMapGroupComponent;
|
||||||
|
import org.hl7.fhir.r4.model.ConceptMap.SourceElementComponent;
|
||||||
|
import org.hl7.fhir.r4.model.ConceptMap.TargetElementComponent;
|
||||||
|
import org.hl7.fhir.r4.model.Enumerations;
|
||||||
|
import org.hl7.fhir.r4.model.UriType;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.*;
|
||||||
|
|
||||||
|
public class ImportCsvToConceptMapCommand extends AbstractImportExportCsvConceptMapCommand {
|
||||||
|
// TODO: Don't use qualified names for loggers in HAPI CLI.
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ImportCsvToConceptMapCommand.class);
|
||||||
|
|
||||||
|
protected static final String SOURCE_VALUE_SET_PARAM = "i";
|
||||||
|
protected static final String SOURCE_VALUE_SET_PARAM_LONGOPT = "input";
|
||||||
|
protected static final String SOURCE_VALUE_SET_PARAM_NAME = "input";
|
||||||
|
protected static final String SOURCE_VALUE_SET_PARAM_DESC = "The source value set of the ConceptMap to be imported (i.e. ConceptMap.sourceUri).";
|
||||||
|
protected static final String TARGET_VALUE_SET_PARAM = "o";
|
||||||
|
protected static final String TARGET_VALUE_SET_PARAM_LONGOPT = "output";
|
||||||
|
protected static final String TARGET_VALUE_SET_PARAM_NAME = "output";
|
||||||
|
protected static final String TARGET_VALUE_SET_PARAM_DESC = "The target value set of the ConceptMap to be imported (i.e. ConceptMap.targetUri).";
|
||||||
|
|
||||||
|
protected String sourceValueSet;
|
||||||
|
protected String targetValueSet;
|
||||||
|
|
||||||
|
private boolean hasElements;
|
||||||
|
private boolean hasTargets;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCommandDescription() {
|
||||||
|
return "Imports a CSV file to a ConceptMap resource.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCommandName() {
|
||||||
|
return "import-csv-to-conceptmap";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Options getOptions() {
|
||||||
|
Options options = new Options();
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void parseAdditionalParameters(CommandLine theCommandLine) throws ParseException {
|
||||||
|
sourceValueSet = theCommandLine.getOptionValue(SOURCE_VALUE_SET_PARAM);
|
||||||
|
if (isBlank(sourceValueSet)) {
|
||||||
|
ourLog.info("Source value set is not specified (i.e. ConceptMap.sourceUri).");
|
||||||
|
} else {
|
||||||
|
ourLog.info("Specified source value set (i.e. ConceptMap.sourceUri): {}", sourceValueSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
targetValueSet = theCommandLine.getOptionValue(TARGET_VALUE_SET_PARAM);
|
||||||
|
if (isBlank(targetValueSet)) {
|
||||||
|
ourLog.info("Target value set is not specified (i.e. ConceptMap.targetUri).");
|
||||||
|
} else {
|
||||||
|
ourLog.info("Specified target value set (i.e. ConceptMap.targetUri): {}", targetValueSet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void process() throws ParseException, ExecutionException {
|
||||||
|
searchForConceptMapByUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void searchForConceptMapByUrl() throws ParseException, ExecutionException {
|
||||||
|
if (fhirVersion == FhirVersionEnum.DSTU3) {
|
||||||
|
org.hl7.fhir.dstu3.model.ConceptMap conceptMap = convertCsvToConceptMapDstu3();
|
||||||
|
|
||||||
|
ourLog.info("Searching for existing ConceptMap with specified URL (i.e. ConceptMap.url): {}", conceptMapUrl);
|
||||||
|
MethodOutcome methodOutcome = client
|
||||||
|
.update()
|
||||||
|
.resource(conceptMap)
|
||||||
|
.conditional()
|
||||||
|
.where(org.hl7.fhir.dstu3.model.ConceptMap.URL.matches().value(conceptMapUrl))
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
if (Boolean.TRUE.equals(methodOutcome.getCreated())) {
|
||||||
|
ourLog.info("Created new ConceptMap: {}", methodOutcome.getId().getValue());
|
||||||
|
} else {
|
||||||
|
ourLog.info("Updated existing ConceptMap: {}", methodOutcome.getId().getValue());
|
||||||
|
}
|
||||||
|
} else if (fhirVersion == FhirVersionEnum.R4) {
|
||||||
|
ConceptMap conceptMap = convertCsvToConceptMapR4();
|
||||||
|
|
||||||
|
ourLog.info("Searching for existing ConceptMap with specified URL (i.e. ConceptMap.url): {}", conceptMapUrl);
|
||||||
|
MethodOutcome methodOutcome = client
|
||||||
|
.update()
|
||||||
|
.resource(conceptMap)
|
||||||
|
.conditional()
|
||||||
|
.where(ConceptMap.URL.matches().value(conceptMapUrl))
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
if (Boolean.TRUE.equals(methodOutcome.getCreated())) {
|
||||||
|
ourLog.info("Created new ConceptMap: {}", methodOutcome.getId().getValue());
|
||||||
|
} else {
|
||||||
|
ourLog.info("Updated existing ConceptMap: {}", methodOutcome.getId().getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private org.hl7.fhir.dstu3.model.ConceptMap convertCsvToConceptMapDstu3() throws ParseException, ExecutionException {
|
||||||
|
try {
|
||||||
|
return VersionConvertor_30_40.convertConceptMap(convertCsvToConceptMapR4());
|
||||||
|
} catch (FHIRException fe) {
|
||||||
|
throw new ExecutionException(fe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConceptMap convertCsvToConceptMapR4() throws ParseException, ExecutionException {
|
||||||
|
ourLog.info("Converting CSV to ConceptMap...");
|
||||||
|
ConceptMap retVal = new ConceptMap();
|
||||||
|
Reader reader = null;
|
||||||
|
CSVParser csvParser = null;
|
||||||
|
try {
|
||||||
|
reader = Files.newBufferedReader(Paths.get(file));
|
||||||
|
csvParser = new CSVParser(
|
||||||
|
reader,
|
||||||
|
CSVFormat
|
||||||
|
.DEFAULT
|
||||||
|
.withRecordSeparator("\n")
|
||||||
|
.withHeader(Header.class)
|
||||||
|
.withFirstRecordAsHeader()
|
||||||
|
.withIgnoreHeaderCase()
|
||||||
|
.withTrim());
|
||||||
|
|
||||||
|
retVal.setUrl(conceptMapUrl);
|
||||||
|
|
||||||
|
if (isNotBlank(sourceValueSet)) {
|
||||||
|
retVal.setSource(new UriType(sourceValueSet));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNotBlank(targetValueSet)) {
|
||||||
|
retVal.setTarget(new UriType(targetValueSet));
|
||||||
|
}
|
||||||
|
|
||||||
|
TemporaryConceptMapGroup temporaryConceptMapGroup;
|
||||||
|
TemporaryConceptMapGroupElement temporaryConceptMapGroupElement;
|
||||||
|
Map<TemporaryConceptMapGroup, Map<TemporaryConceptMapGroupElement, Set<TemporaryConceptMapGroupElementTarget>>> groupMap = parseCsvRecords(csvParser);
|
||||||
|
Map<TemporaryConceptMapGroupElement, Set<TemporaryConceptMapGroupElementTarget>> elementMap;
|
||||||
|
Set<TemporaryConceptMapGroupElementTarget> targetSet;
|
||||||
|
ConceptMapGroupComponent conceptMapGroupComponent;
|
||||||
|
SourceElementComponent sourceElementComponent;
|
||||||
|
TargetElementComponent targetElementComponent;
|
||||||
|
for (Map.Entry<TemporaryConceptMapGroup, Map<TemporaryConceptMapGroupElement, Set<TemporaryConceptMapGroupElementTarget>>> groupEntry : groupMap.entrySet()) {
|
||||||
|
|
||||||
|
hasElements = false;
|
||||||
|
hasTargets = false;
|
||||||
|
|
||||||
|
temporaryConceptMapGroup = groupEntry.getKey();
|
||||||
|
conceptMapGroupComponent = new ConceptMapGroupComponent();
|
||||||
|
|
||||||
|
if (temporaryConceptMapGroup.hasSource()) {
|
||||||
|
conceptMapGroupComponent.setSource(temporaryConceptMapGroup.getSource());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (temporaryConceptMapGroup.hasSourceVersion()) {
|
||||||
|
conceptMapGroupComponent.setSourceVersion(temporaryConceptMapGroup.getSourceVersion());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (temporaryConceptMapGroup.hasTarget()) {
|
||||||
|
conceptMapGroupComponent.setTarget(temporaryConceptMapGroup.getTarget());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (temporaryConceptMapGroup.hasTargetVersion()) {
|
||||||
|
conceptMapGroupComponent.setTargetVersion(temporaryConceptMapGroup.getTargetVersion());
|
||||||
|
}
|
||||||
|
|
||||||
|
elementMap = groupEntry.getValue();
|
||||||
|
for (Map.Entry<TemporaryConceptMapGroupElement, Set<TemporaryConceptMapGroupElementTarget>> elementEntry : elementMap.entrySet()) {
|
||||||
|
|
||||||
|
temporaryConceptMapGroupElement = elementEntry.getKey();
|
||||||
|
sourceElementComponent = new SourceElementComponent();
|
||||||
|
|
||||||
|
if (temporaryConceptMapGroupElement.hasCode()) {
|
||||||
|
sourceElementComponent.setCode(temporaryConceptMapGroupElement.getCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (temporaryConceptMapGroupElement.hasDisplay()) {
|
||||||
|
sourceElementComponent.setDisplay(temporaryConceptMapGroupElement.getDisplay());
|
||||||
|
}
|
||||||
|
|
||||||
|
targetSet = elementEntry.getValue();
|
||||||
|
for (TemporaryConceptMapGroupElementTarget temporaryConceptMapGroupElementTarget : targetSet) {
|
||||||
|
|
||||||
|
targetElementComponent = new TargetElementComponent();
|
||||||
|
|
||||||
|
if (temporaryConceptMapGroupElementTarget.hasCode()) {
|
||||||
|
targetElementComponent.setCode(temporaryConceptMapGroupElementTarget.getCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (temporaryConceptMapGroupElementTarget.hasDisplay()) {
|
||||||
|
targetElementComponent.setDisplay(temporaryConceptMapGroupElementTarget.getDisplay());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (temporaryConceptMapGroupElementTarget.hasEquivalence()) {
|
||||||
|
try {
|
||||||
|
targetElementComponent.setEquivalence(Enumerations.ConceptMapEquivalence.fromCode(temporaryConceptMapGroupElementTarget.getEquivalence()));
|
||||||
|
} catch (FHIRException fe) {
|
||||||
|
throw new ExecutionException(fe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (temporaryConceptMapGroupElementTarget.hasComment()) {
|
||||||
|
targetElementComponent.setComment(temporaryConceptMapGroupElementTarget.getComment());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (temporaryConceptMapGroupElementTarget.hasValues()) {
|
||||||
|
sourceElementComponent.addTarget(targetElementComponent);
|
||||||
|
hasTargets = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (temporaryConceptMapGroupElement.hasValues() || hasTargets) {
|
||||||
|
conceptMapGroupComponent.addElement(sourceElementComponent);
|
||||||
|
hasElements = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (temporaryConceptMapGroup.hasValues() || hasElements || hasTargets) {
|
||||||
|
retVal.addGroup(conceptMapGroupComponent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new InternalErrorException(e);
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeQuietly(csvParser);
|
||||||
|
IOUtils.closeQuietly(reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
ourLog.info("Finished converting CSV to ConceptMap.");
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<TemporaryConceptMapGroup, Map<TemporaryConceptMapGroupElement, Set<TemporaryConceptMapGroupElementTarget>>> parseCsvRecords(CSVParser theCsvParser) {
|
||||||
|
Map<TemporaryConceptMapGroup, Map<TemporaryConceptMapGroupElement, Set<TemporaryConceptMapGroupElementTarget>>> retVal = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
TemporaryConceptMapGroup group;
|
||||||
|
TemporaryConceptMapGroupElement element;
|
||||||
|
TemporaryConceptMapGroupElementTarget target;
|
||||||
|
Map<TemporaryConceptMapGroupElement, Set<TemporaryConceptMapGroupElementTarget>> elementMap;
|
||||||
|
Set<TemporaryConceptMapGroupElementTarget> targetSet;
|
||||||
|
|
||||||
|
for (CSVRecord csvRecord : theCsvParser) {
|
||||||
|
|
||||||
|
group = new TemporaryConceptMapGroup(
|
||||||
|
defaultString(csvRecord.get(Header.SOURCE_CODE_SYSTEM)),
|
||||||
|
defaultString(csvRecord.get(Header.SOURCE_CODE_SYSTEM_VERSION)),
|
||||||
|
defaultString(csvRecord.get(Header.TARGET_CODE_SYSTEM)),
|
||||||
|
defaultString(csvRecord.get(Header.TARGET_CODE_SYSTEM_VERSION)));
|
||||||
|
|
||||||
|
element = new TemporaryConceptMapGroupElement(
|
||||||
|
defaultString(csvRecord.get(Header.SOURCE_CODE)),
|
||||||
|
defaultString(csvRecord.get(Header.SOURCE_DISPLAY)));
|
||||||
|
|
||||||
|
target = new TemporaryConceptMapGroupElementTarget(
|
||||||
|
defaultString(csvRecord.get(Header.TARGET_CODE)),
|
||||||
|
defaultString(csvRecord.get(Header.TARGET_DISPLAY)),
|
||||||
|
defaultString(csvRecord.get(Header.EQUIVALENCE)),
|
||||||
|
defaultString(csvRecord.get(Header.COMMENT)));
|
||||||
|
|
||||||
|
if (!retVal.containsKey(group)) {
|
||||||
|
targetSet = new LinkedHashSet<>();
|
||||||
|
targetSet.add(target);
|
||||||
|
|
||||||
|
elementMap = new LinkedHashMap<>();
|
||||||
|
elementMap.put(element, targetSet);
|
||||||
|
|
||||||
|
retVal.put(group, elementMap);
|
||||||
|
} else if (!retVal.get(group).containsKey(element)) {
|
||||||
|
targetSet = new LinkedHashSet<>();
|
||||||
|
targetSet.add(target);
|
||||||
|
|
||||||
|
retVal.get(group).put(element, targetSet);
|
||||||
|
} else {
|
||||||
|
retVal.get(group).get(element).add(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
}
|
|
@ -35,6 +35,7 @@ public class LoadingValidationSupportDstu2 implements IValidationSupport {
|
||||||
|
|
||||||
private FhirContext myCtx = FhirContext.forDstu2Hl7Org();
|
private FhirContext myCtx = FhirContext.forDstu2Hl7Org();
|
||||||
|
|
||||||
|
// TODO: Don't use qualified names for loggers in HAPI CLI.
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(LoadingValidationSupportDstu2.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(LoadingValidationSupportDstu2.class);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -38,6 +38,7 @@ public class LoadingValidationSupportDstu3 implements IValidationSupport {
|
||||||
|
|
||||||
private FhirContext myCtx = FhirContext.forDstu3();
|
private FhirContext myCtx = FhirContext.forDstu3();
|
||||||
|
|
||||||
|
// TODO: Don't use qualified names for loggers in HAPI CLI.
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(LoadingValidationSupportDstu3.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(LoadingValidationSupportDstu3.class);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -34,7 +34,7 @@ import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class LoadingValidationSupportR4 implements org.hl7.fhir.r4.hapi.ctx.IValidationSupport {
|
public class LoadingValidationSupportR4 implements org.hl7.fhir.r4.hapi.ctx.IValidationSupport {
|
||||||
|
// TODO: Don't use qualified names for loggers in HAPI CLI.
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(LoadingValidationSupportR4.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(LoadingValidationSupportR4.class);
|
||||||
private FhirContext myCtx = FhirContext.forR4();
|
private FhirContext myCtx = FhirContext.forR4();
|
||||||
|
|
||||||
|
|
|
@ -20,17 +20,11 @@ package ca.uhn.fhir.cli;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.io.BufferedOutputStream;
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
import java.io.File;
|
import ca.uhn.fhir.jpa.demo.ContextHolder;
|
||||||
import java.io.FileOutputStream;
|
import ca.uhn.fhir.jpa.demo.FhirServerConfig;
|
||||||
import java.io.IOException;
|
import ca.uhn.fhir.jpa.demo.FhirServerConfigDstu3;
|
||||||
import java.io.InputStream;
|
import ca.uhn.fhir.jpa.demo.FhirServerConfigR4;
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.net.SocketException;
|
|
||||||
|
|
||||||
import javax.servlet.ServletContextEvent;
|
|
||||||
import javax.servlet.ServletContextListener;
|
|
||||||
|
|
||||||
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;
|
||||||
|
@ -41,8 +35,10 @@ import org.springframework.web.context.ContextLoader;
|
||||||
import org.springframework.web.context.ContextLoaderListener;
|
import org.springframework.web.context.ContextLoaderListener;
|
||||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
import javax.servlet.ServletContextEvent;
|
||||||
import ca.uhn.fhir.jpa.demo.*;
|
import javax.servlet.ServletContextListener;
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.SocketException;
|
||||||
|
|
||||||
public class RunServerCommand extends BaseCommand {
|
public class RunServerCommand extends BaseCommand {
|
||||||
|
|
||||||
|
@ -53,6 +49,7 @@ public class RunServerCommand extends BaseCommand {
|
||||||
private static final int DEFAULT_PORT = 8080;
|
private static final int DEFAULT_PORT = 8080;
|
||||||
private static final String OPTION_P = "p";
|
private static final String OPTION_P = "p";
|
||||||
|
|
||||||
|
// TODO: Don't use qualified names for loggers in HAPI CLI.
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RunServerCommand.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RunServerCommand.class);
|
||||||
public static final String RUN_SERVER_COMMAND = "run-server";
|
public static final String RUN_SERVER_COMMAND = "run-server";
|
||||||
private int myPort;
|
private int myPort;
|
||||||
|
|
|
@ -24,10 +24,8 @@ import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||||
import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc;
|
import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc;
|
||||||
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||||
import ca.uhn.fhir.rest.client.interceptor.BearerTokenAuthInterceptor;
|
|
||||||
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
||||||
import org.apache.commons.cli.CommandLine;
|
import org.apache.commons.cli.CommandLine;
|
||||||
import org.apache.commons.cli.Option;
|
|
||||||
import org.apache.commons.cli.Options;
|
import org.apache.commons.cli.Options;
|
||||||
import org.apache.commons.cli.ParseException;
|
import org.apache.commons.cli.ParseException;
|
||||||
import org.hl7.fhir.dstu3.model.Parameters;
|
import org.hl7.fhir.dstu3.model.Parameters;
|
||||||
|
@ -36,12 +34,10 @@ import org.hl7.fhir.dstu3.model.UriType;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|
||||||
|
|
||||||
public class UploadTerminologyCommand extends BaseCommand {
|
public class UploadTerminologyCommand extends BaseCommand {
|
||||||
|
// TODO: Don't use qualified names for loggers in HAPI CLI.
|
||||||
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 String BASE_URL_PARAM = "t";
|
|
||||||
private static final String UPLOAD_EXTERNAL_CODE_SYSTEM = "upload-external-code-system";
|
private static final String UPLOAD_EXTERNAL_CODE_SYSTEM = "upload-external-code-system";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -59,10 +55,11 @@ public class UploadTerminologyCommand extends BaseCommand {
|
||||||
Options options = new Options();
|
Options options = new Options();
|
||||||
|
|
||||||
addFhirVersionOption(options);
|
addFhirVersionOption(options);
|
||||||
addRequiredOption(options, "t", "target", true, "Base URL for the target server (e.g. \"http://example.com/fhir\")");
|
addBaseUrlOption(options);
|
||||||
addRequiredOption(options, "u", "url", true, "The code system URL associated with this upload (e.g. " + IHapiTerminologyLoaderSvc.SCT_URI + ")");
|
addRequiredOption(options, "u", "url", true, "The code system URL associated with this upload (e.g. " + IHapiTerminologyLoaderSvc.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)");
|
||||||
addBasicAuthOption(options);
|
addBasicAuthOption(options);
|
||||||
|
addVerboseLoggingOption(options);
|
||||||
|
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
@ -72,13 +69,6 @@ public class UploadTerminologyCommand extends BaseCommand {
|
||||||
parseFhirContext(theCommandLine);
|
parseFhirContext(theCommandLine);
|
||||||
FhirContext ctx = getFhirContext();
|
FhirContext ctx = getFhirContext();
|
||||||
|
|
||||||
String targetServer = theCommandLine.getOptionValue(BASE_URL_PARAM);
|
|
||||||
if (isBlank(targetServer)) {
|
|
||||||
throw new ParseException("No target server (-" + BASE_URL_PARAM + ") specified");
|
|
||||||
} else if (targetServer.startsWith("http") == false && targetServer.startsWith("file") == false) {
|
|
||||||
throw new ParseException("Invalid target server specified, must begin with 'http' or 'file'");
|
|
||||||
}
|
|
||||||
|
|
||||||
String termUrl = theCommandLine.getOptionValue("u");
|
String termUrl = theCommandLine.getOptionValue("u");
|
||||||
if (isBlank(termUrl)) {
|
if (isBlank(termUrl)) {
|
||||||
throw new ParseException("No URL provided");
|
throw new ParseException("No URL provided");
|
||||||
|
@ -89,8 +79,6 @@ public class UploadTerminologyCommand extends BaseCommand {
|
||||||
throw new ParseException("No data file provided");
|
throw new ParseException("No data file provided");
|
||||||
}
|
}
|
||||||
|
|
||||||
String bearerToken = theCommandLine.getOptionValue("b");
|
|
||||||
|
|
||||||
IGenericClient client = super.newClient(theCommandLine);
|
IGenericClient client = super.newClient(theCommandLine);
|
||||||
IBaseParameters inputParameters;
|
IBaseParameters inputParameters;
|
||||||
if (ctx.getVersion().getVersion() == FhirVersionEnum.DSTU3) {
|
if (ctx.getVersion().getVersion() == FhirVersionEnum.DSTU3) {
|
||||||
|
@ -104,11 +92,7 @@ public class UploadTerminologyCommand extends BaseCommand {
|
||||||
throw new ParseException("This command does not support FHIR version " + ctx.getVersion().getVersion());
|
throw new ParseException("This command does not support FHIR version " + ctx.getVersion().getVersion());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isNotBlank(bearerToken)) {
|
if (theCommandLine.hasOption(VERBOSE_LOGGING_PARAM)) {
|
||||||
client.registerInterceptor(new BearerTokenAuthInterceptor(bearerToken));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (theCommandLine.hasOption('v')) {
|
|
||||||
client.registerInterceptor(new LoggingInterceptor(true));
|
client.registerInterceptor(new LoggingInterceptor(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ import static org.apache.commons.lang3.StringUtils.*;
|
||||||
import static org.fusesource.jansi.Ansi.ansi;
|
import static org.fusesource.jansi.Ansi.ansi;
|
||||||
|
|
||||||
public class ValidateCommand extends BaseCommand {
|
public class ValidateCommand extends BaseCommand {
|
||||||
|
// TODO: Don't use qualified names for loggers in HAPI CLI.
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValidateCommand.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValidateCommand.class);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -53,7 +53,7 @@ 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 ValidationDataUploader extends BaseCommand {
|
public class ValidationDataUploader extends BaseCommand {
|
||||||
|
// TODO: Don't use qualified names for loggers in HAPI CLI.
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValidationDataUploader.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValidationDataUploader.class);
|
||||||
|
|
||||||
private ArrayList<IIdType> myExcludes = new ArrayList<>();
|
private ArrayList<IIdType> myExcludes = new ArrayList<>();
|
||||||
|
@ -167,7 +167,7 @@ public class ValidationDataUploader extends BaseCommand {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void uploadDefinitionsDstu2(CommandLine theCommandLine, FhirContext ctx) throws CommandFailureException {
|
private void uploadDefinitionsDstu2(CommandLine theCommandLine, FhirContext ctx) throws CommandFailureException, ParseException {
|
||||||
IGenericClient client = newClient(theCommandLine);
|
IGenericClient client = newClient(theCommandLine);
|
||||||
|
|
||||||
ourLog.info("Uploading definitions to server");
|
ourLog.info("Uploading definitions to server");
|
||||||
|
@ -267,7 +267,7 @@ public class ValidationDataUploader extends BaseCommand {
|
||||||
ourLog.info("Finished uploading definitions to server (took {} ms)", delay);
|
ourLog.info("Finished uploading definitions to server (took {} ms)", delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void uploadDefinitionsDstu3(CommandLine theCommandLine, FhirContext theCtx) throws CommandFailureException {
|
private void uploadDefinitionsDstu3(CommandLine theCommandLine, FhirContext theCtx) throws CommandFailureException, ParseException {
|
||||||
IGenericClient client = newClient(theCommandLine);
|
IGenericClient client = newClient(theCommandLine);
|
||||||
ourLog.info("Uploading definitions to server");
|
ourLog.info("Uploading definitions to server");
|
||||||
|
|
||||||
|
@ -365,7 +365,7 @@ public class ValidationDataUploader extends BaseCommand {
|
||||||
ourLog.info("Finished uploading definitions to server (took {} ms)", delay);
|
ourLog.info("Finished uploading definitions to server (took {} ms)", delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void uploadDefinitionsR4(CommandLine theCommandLine, FhirContext theCtx) throws CommandFailureException {
|
private void uploadDefinitionsR4(CommandLine theCommandLine, FhirContext theCtx) throws CommandFailureException, ParseException {
|
||||||
IGenericClient client = newClient(theCommandLine);
|
IGenericClient client = newClient(theCommandLine);
|
||||||
ourLog.info("Uploading definitions to server");
|
ourLog.info("Uploading definitions to server");
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
public class WebsocketSubscribeCommand extends BaseCommand {
|
public class WebsocketSubscribeCommand extends BaseCommand {
|
||||||
private static final org.slf4j.Logger LOG_RECV = org.slf4j.LoggerFactory.getLogger("websocket.RECV");
|
private static final org.slf4j.Logger LOG_RECV = org.slf4j.LoggerFactory.getLogger("websocket.RECV");
|
||||||
private static final org.slf4j.Logger LOG_SEND = org.slf4j.LoggerFactory.getLogger("websocket.SEND");
|
private static final org.slf4j.Logger LOG_SEND = org.slf4j.LoggerFactory.getLogger("websocket.SEND");
|
||||||
|
// TODO: Don't use qualified names for loggers in HAPI CLI.
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(WebsocketSubscribeCommand.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(WebsocketSubscribeCommand.class);
|
||||||
|
|
||||||
private boolean myQuit;
|
private boolean myQuit;
|
||||||
|
|
|
@ -0,0 +1,282 @@
|
||||||
|
package ca.uhn.fhir.cli;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||||
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
|
import ca.uhn.fhir.rest.server.interceptor.VerboseLoggingInterceptor;
|
||||||
|
import ca.uhn.fhir.util.PortUtil;
|
||||||
|
import ca.uhn.fhir.util.TestUtil;
|
||||||
|
import com.google.common.base.Charsets;
|
||||||
|
import org.apache.commons.io.FileUtils;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.eclipse.jetty.server.Server;
|
||||||
|
import org.eclipse.jetty.servlet.ServletHandler;
|
||||||
|
import org.eclipse.jetty.servlet.ServletHolder;
|
||||||
|
import org.hl7.fhir.r4.model.ConceptMap;
|
||||||
|
import org.hl7.fhir.r4.model.Enumerations.ConceptMapEquivalence;
|
||||||
|
import org.hl7.fhir.r4.model.UriType;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class ExportConceptMapToCsvCommandTest {
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExportConceptMapToCsvCommandTest.class);
|
||||||
|
private static final String CM_URL = "http://example.com/conceptmap";
|
||||||
|
private static final String VS_URL_1 = "http://example.com/valueset/1";
|
||||||
|
private static final String VS_URL_2 = "http://example.com/valueset/2";
|
||||||
|
private static final String CS_URL_1 = "http://example.com/codesystem/1";
|
||||||
|
private static final String CS_URL_2 = "http://example.com/codesystem/2";
|
||||||
|
private static final String CS_URL_3 = "http://example.com/codesystem/3";
|
||||||
|
private static final String FILE = "./target/output.csv";
|
||||||
|
|
||||||
|
private static String ourBase;
|
||||||
|
private static IGenericClient ourClient;
|
||||||
|
private static FhirContext ourCtx = FhirContext.forR4();
|
||||||
|
private static int ourPort;
|
||||||
|
private static Server ourServer;
|
||||||
|
|
||||||
|
static {
|
||||||
|
System.setProperty("test", "true");
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void afterClassClearContext() throws Exception {
|
||||||
|
ourServer.stop();
|
||||||
|
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void beforeClass() throws Exception {
|
||||||
|
ourPort = PortUtil.findFreePort();
|
||||||
|
ourServer = new Server(ourPort);
|
||||||
|
|
||||||
|
ServletHandler servletHandler = new ServletHandler();
|
||||||
|
|
||||||
|
RestfulServer restfulServer = new RestfulServer(ourCtx);
|
||||||
|
restfulServer.registerInterceptor(new VerboseLoggingInterceptor());
|
||||||
|
restfulServer.setResourceProviders(new HashMapResourceProviderConceptMapR4(ourCtx));
|
||||||
|
|
||||||
|
ServletHolder servletHolder = new ServletHolder(restfulServer);
|
||||||
|
servletHandler.addServletWithMapping(servletHolder, "/*");
|
||||||
|
ourServer.setHandler(servletHandler);
|
||||||
|
|
||||||
|
ourServer.start();
|
||||||
|
|
||||||
|
ourBase = "http://localhost:" + ourPort;
|
||||||
|
|
||||||
|
ourClient = ourCtx.newRestfulGenericClient(ourBase);
|
||||||
|
|
||||||
|
ourClient.create().resource(createConceptMap()).execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExportConceptMapToCsvCommand() throws IOException {
|
||||||
|
ourLog.info("ConceptMap:\n" + ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(createConceptMap()));
|
||||||
|
|
||||||
|
App.main(new String[] {"export-conceptmap-to-csv",
|
||||||
|
"-v", "r4",
|
||||||
|
"-t", ourBase,
|
||||||
|
"-u", CM_URL,
|
||||||
|
"-f", FILE,
|
||||||
|
"-l"});
|
||||||
|
|
||||||
|
String expected = "SOURCE_CODE_SYSTEM,SOURCE_CODE_SYSTEM_VERSION,TARGET_CODE_SYSTEM,TARGET_CODE_SYSTEM_VERSION,SOURCE_CODE,SOURCE_DISPLAY,TARGET_CODE,TARGET_DISPLAY,EQUIVALENCE,COMMENT\n" +
|
||||||
|
"http://example.com/codesystem/1,Version 1s,http://example.com/codesystem/2,Version 2t,Code 1a,Display 1a,Code 2a,Display 2a,equal,2a This is a comment.\n" +
|
||||||
|
"http://example.com/codesystem/1,Version 1s,http://example.com/codesystem/2,Version 2t,Code 1b,Display 1b,Code 2b,Display 2b,equal,2b This is a comment.\n" +
|
||||||
|
"http://example.com/codesystem/1,Version 1s,http://example.com/codesystem/2,Version 2t,Code 1c,Display 1c,Code 2c,Display 2c,equal,2c This is a comment.\n" +
|
||||||
|
"http://example.com/codesystem/1,Version 1s,http://example.com/codesystem/2,Version 2t,Code 1d,Display 1d,Code 2d,Display 2d,equal,2d This is a comment.\n" +
|
||||||
|
"http://example.com/codesystem/1,Version 1s,http://example.com/codesystem/3,Version 3t,Code 1a,Display 1a,Code 3a,Display 3a,equal,3a This is a comment.\n" +
|
||||||
|
"http://example.com/codesystem/1,Version 1s,http://example.com/codesystem/3,Version 3t,Code 1b,Display 1b,Code 3b,Display 3b,equal,3b This is a comment.\n" +
|
||||||
|
"http://example.com/codesystem/1,Version 1s,http://example.com/codesystem/3,Version 3t,Code 1c,Display 1c,Code 3c,Display 3c,equal,3c This is a comment.\n" +
|
||||||
|
"http://example.com/codesystem/1,Version 1s,http://example.com/codesystem/3,Version 3t,Code 1d,Display 1d,Code 3d,Display 3d,equal,3d This is a comment.\n" +
|
||||||
|
"http://example.com/codesystem/2,Version 2s,http://example.com/codesystem/3,Version 3t,Code 2a,Display 2a,Code 3a,Display 3a,equal,3a This is a comment.\n" +
|
||||||
|
"http://example.com/codesystem/2,Version 2s,http://example.com/codesystem/3,Version 3t,Code 2b,Display 2b,Code 3b,Display 3b,equal,3b This is a comment.\n" +
|
||||||
|
"http://example.com/codesystem/2,Version 2s,http://example.com/codesystem/3,Version 3t,Code 2c,Display 2c,Code 3c,Display 3c,equal,3c This is a comment.\n" +
|
||||||
|
"http://example.com/codesystem/2,Version 2s,http://example.com/codesystem/3,Version 3t,Code 2d,Display 2d,Code 3d,Display 3d,equal,3d This is a comment.\n";
|
||||||
|
String result = IOUtils.toString(new FileInputStream(FILE), Charsets.UTF_8);
|
||||||
|
assertEquals(expected, result);
|
||||||
|
|
||||||
|
FileUtils.deleteQuietly(new File(FILE));
|
||||||
|
}
|
||||||
|
|
||||||
|
static ConceptMap createConceptMap() {
|
||||||
|
ConceptMap conceptMap = new ConceptMap();
|
||||||
|
conceptMap
|
||||||
|
.setUrl(CM_URL)
|
||||||
|
.setSource(new UriType(VS_URL_1))
|
||||||
|
.setTarget(new UriType(VS_URL_2));
|
||||||
|
|
||||||
|
ConceptMap.ConceptMapGroupComponent group = conceptMap.addGroup();
|
||||||
|
group
|
||||||
|
.setSource(CS_URL_1)
|
||||||
|
.setSourceVersion("Version 1s")
|
||||||
|
.setTarget(CS_URL_2)
|
||||||
|
.setTargetVersion("Version 2t");
|
||||||
|
|
||||||
|
ConceptMap.SourceElementComponent element = group.addElement();
|
||||||
|
element
|
||||||
|
.setCode("Code 1a")
|
||||||
|
.setDisplay("Display 1a");
|
||||||
|
|
||||||
|
ConceptMap.TargetElementComponent target = element.addTarget();
|
||||||
|
target
|
||||||
|
.setCode("Code 2a")
|
||||||
|
.setDisplay("Display 2a")
|
||||||
|
.setEquivalence(ConceptMapEquivalence.EQUAL)
|
||||||
|
.setComment("2a This is a comment.");
|
||||||
|
|
||||||
|
element = group.addElement();
|
||||||
|
element
|
||||||
|
.setCode("Code 1b")
|
||||||
|
.setDisplay("Display 1b");
|
||||||
|
|
||||||
|
target = element.addTarget();
|
||||||
|
target
|
||||||
|
.setCode("Code 2b")
|
||||||
|
.setDisplay("Display 2b")
|
||||||
|
.setEquivalence(ConceptMapEquivalence.EQUAL)
|
||||||
|
.setComment("2b This is a comment.");
|
||||||
|
|
||||||
|
element = group.addElement();
|
||||||
|
element
|
||||||
|
.setCode("Code 1c")
|
||||||
|
.setDisplay("Display 1c");
|
||||||
|
|
||||||
|
target = element.addTarget();
|
||||||
|
target
|
||||||
|
.setCode("Code 2c")
|
||||||
|
.setDisplay("Display 2c")
|
||||||
|
.setEquivalence(ConceptMapEquivalence.EQUAL)
|
||||||
|
.setComment("2c This is a comment.");
|
||||||
|
|
||||||
|
element = group.addElement();
|
||||||
|
element
|
||||||
|
.setCode("Code 1d")
|
||||||
|
.setDisplay("Display 1d");
|
||||||
|
|
||||||
|
target = element.addTarget();
|
||||||
|
target
|
||||||
|
.setCode("Code 2d")
|
||||||
|
.setDisplay("Display 2d")
|
||||||
|
.setEquivalence(ConceptMapEquivalence.EQUAL)
|
||||||
|
.setComment("2d This is a comment.");
|
||||||
|
|
||||||
|
group = conceptMap.addGroup();
|
||||||
|
group
|
||||||
|
.setSource(CS_URL_1)
|
||||||
|
.setSourceVersion("Version 1s")
|
||||||
|
.setTarget(CS_URL_3)
|
||||||
|
.setTargetVersion("Version 3t");
|
||||||
|
|
||||||
|
element = group.addElement();
|
||||||
|
element
|
||||||
|
.setCode("Code 1a")
|
||||||
|
.setDisplay("Display 1a");
|
||||||
|
|
||||||
|
target = element.addTarget();
|
||||||
|
target
|
||||||
|
.setCode("Code 3a")
|
||||||
|
.setDisplay("Display 3a")
|
||||||
|
.setEquivalence(ConceptMapEquivalence.EQUAL)
|
||||||
|
.setComment("3a This is a comment.");
|
||||||
|
|
||||||
|
element = group.addElement();
|
||||||
|
element
|
||||||
|
.setCode("Code 1b")
|
||||||
|
.setDisplay("Display 1b");
|
||||||
|
|
||||||
|
target = element.addTarget();
|
||||||
|
target
|
||||||
|
.setCode("Code 3b")
|
||||||
|
.setDisplay("Display 3b")
|
||||||
|
.setEquivalence(ConceptMapEquivalence.EQUAL)
|
||||||
|
.setComment("3b This is a comment.");
|
||||||
|
|
||||||
|
element = group.addElement();
|
||||||
|
element
|
||||||
|
.setCode("Code 1c")
|
||||||
|
.setDisplay("Display 1c");
|
||||||
|
|
||||||
|
target = element.addTarget();
|
||||||
|
target
|
||||||
|
.setCode("Code 3c")
|
||||||
|
.setDisplay("Display 3c")
|
||||||
|
.setEquivalence(ConceptMapEquivalence.EQUAL)
|
||||||
|
.setComment("3c This is a comment.");
|
||||||
|
|
||||||
|
element = group.addElement();
|
||||||
|
element
|
||||||
|
.setCode("Code 1d")
|
||||||
|
.setDisplay("Display 1d");
|
||||||
|
|
||||||
|
target = element.addTarget();
|
||||||
|
target
|
||||||
|
.setCode("Code 3d")
|
||||||
|
.setDisplay("Display 3d")
|
||||||
|
.setEquivalence(ConceptMapEquivalence.EQUAL)
|
||||||
|
.setComment("3d This is a comment.");
|
||||||
|
|
||||||
|
group = conceptMap.addGroup();
|
||||||
|
group
|
||||||
|
.setSource(CS_URL_2)
|
||||||
|
.setSourceVersion("Version 2s")
|
||||||
|
.setTarget(CS_URL_3)
|
||||||
|
.setTargetVersion("Version 3t");
|
||||||
|
|
||||||
|
element = group.addElement();
|
||||||
|
element
|
||||||
|
.setCode("Code 2a")
|
||||||
|
.setDisplay("Display 2a");
|
||||||
|
|
||||||
|
target = element.addTarget();
|
||||||
|
target
|
||||||
|
.setCode("Code 3a")
|
||||||
|
.setDisplay("Display 3a")
|
||||||
|
.setEquivalence(ConceptMapEquivalence.EQUAL)
|
||||||
|
.setComment("3a This is a comment.");
|
||||||
|
|
||||||
|
element = group.addElement();
|
||||||
|
element
|
||||||
|
.setCode("Code 2b")
|
||||||
|
.setDisplay("Display 2b");
|
||||||
|
|
||||||
|
target = element.addTarget();
|
||||||
|
target
|
||||||
|
.setCode("Code 3b")
|
||||||
|
.setDisplay("Display 3b")
|
||||||
|
.setEquivalence(ConceptMapEquivalence.EQUAL)
|
||||||
|
.setComment("3b This is a comment.");
|
||||||
|
|
||||||
|
element = group.addElement();
|
||||||
|
element
|
||||||
|
.setCode("Code 2c")
|
||||||
|
.setDisplay("Display 2c");
|
||||||
|
|
||||||
|
target = element.addTarget();
|
||||||
|
target
|
||||||
|
.setCode("Code 3c")
|
||||||
|
.setDisplay("Display 3c")
|
||||||
|
.setEquivalence(ConceptMapEquivalence.EQUAL)
|
||||||
|
.setComment("3c This is a comment.");
|
||||||
|
|
||||||
|
element = group.addElement();
|
||||||
|
element
|
||||||
|
.setCode("Code 2d")
|
||||||
|
.setDisplay("Display 2d");
|
||||||
|
|
||||||
|
target = element.addTarget();
|
||||||
|
target
|
||||||
|
.setCode("Code 3d")
|
||||||
|
.setDisplay("Display 3d")
|
||||||
|
.setEquivalence(ConceptMapEquivalence.EQUAL)
|
||||||
|
.setComment("3d This is a comment.");
|
||||||
|
|
||||||
|
return conceptMap;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,127 @@
|
||||||
|
package ca.uhn.fhir.cli;
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - Server Framework
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2018 University Health Network
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||||
|
import ca.uhn.fhir.rest.annotation.*;
|
||||||
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
import ca.uhn.fhir.rest.server.provider.AbstractHashMapResourceProvider;
|
||||||
|
import com.google.common.base.Charsets;
|
||||||
|
import org.apache.http.NameValuePair;
|
||||||
|
import org.apache.http.client.utils.URLEncodedUtils;
|
||||||
|
import org.hl7.fhir.r4.model.ConceptMap;
|
||||||
|
import org.hl7.fhir.r4.model.IdType;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a subclass to implement FHIR operations specific to R4 ConceptMap
|
||||||
|
* resources. Its superclass, {@link AbstractHashMapResourceProvider}, is a simple
|
||||||
|
* implementation of the resource provider interface that uses a HashMap to
|
||||||
|
* store all resources in memory.
|
||||||
|
* <p>
|
||||||
|
* This subclass currently supports the following FHIR operations:
|
||||||
|
* </p>
|
||||||
|
* <ul>
|
||||||
|
* <li>Search for R4 ConceptMap resources by ConceptMap.url</li>
|
||||||
|
* <li>Conditional update for R4 ConceptMap resources by ConceptMap.url</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
public class HashMapResourceProviderConceptMapR4 extends AbstractHashMapResourceProvider<ConceptMap> {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public HashMapResourceProviderConceptMapR4(FhirContext theFhirContext) {
|
||||||
|
super(theFhirContext, ConceptMap.class);
|
||||||
|
|
||||||
|
FhirVersionEnum fhirVersion = theFhirContext.getVersion().getVersion();
|
||||||
|
if (fhirVersion != FhirVersionEnum.R4) {
|
||||||
|
throw new IllegalStateException("Requires FHIR version R4. Unsupported FHIR version provided: " + fhirVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Search
|
||||||
|
public List<ConceptMap> searchByUrl(
|
||||||
|
@RequiredParam(name=ConceptMap.SP_URL) String theConceptMapUrl) {
|
||||||
|
|
||||||
|
List<ConceptMap> retVal = new ArrayList<>();
|
||||||
|
|
||||||
|
for (TreeMap<Long, ConceptMap> next : myIdToVersionToResourceMap.values()) {
|
||||||
|
if (!next.isEmpty()) {
|
||||||
|
ConceptMap conceptMap = next.lastEntry().getValue();
|
||||||
|
if (theConceptMapUrl.equals(conceptMap.getUrl()))
|
||||||
|
retVal.add(conceptMap);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Update
|
||||||
|
public MethodOutcome updateConceptMapConditional(
|
||||||
|
@ResourceParam ConceptMap theConceptMap,
|
||||||
|
@IdParam IdType theId,
|
||||||
|
@ConditionalUrlParam String theConditional) {
|
||||||
|
|
||||||
|
MethodOutcome methodOutcome = new MethodOutcome();
|
||||||
|
|
||||||
|
if (theConditional != null) {
|
||||||
|
|
||||||
|
String url = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
List<NameValuePair> params = URLEncodedUtils.parse(new URI(theConditional), Charsets.UTF_8);
|
||||||
|
for (NameValuePair param : params) {
|
||||||
|
if (param.getName().equalsIgnoreCase("url")) {
|
||||||
|
url = param.getValue();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (URISyntaxException urise) {
|
||||||
|
throw new InvalidRequestException(urise);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNotBlank(url)) {
|
||||||
|
List<ConceptMap> conceptMaps = searchByUrl(url);
|
||||||
|
|
||||||
|
if (!conceptMaps.isEmpty()) {
|
||||||
|
methodOutcome = update(conceptMaps.get(0));
|
||||||
|
} else {
|
||||||
|
methodOutcome = create(theConceptMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
methodOutcome = update(theConceptMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
return methodOutcome;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,371 @@
|
||||||
|
package ca.uhn.fhir.cli;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
|
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||||
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
|
import ca.uhn.fhir.rest.server.interceptor.VerboseLoggingInterceptor;
|
||||||
|
import ca.uhn.fhir.util.PortUtil;
|
||||||
|
import ca.uhn.fhir.util.TestUtil;
|
||||||
|
import org.eclipse.jetty.server.Server;
|
||||||
|
import org.eclipse.jetty.servlet.ServletHandler;
|
||||||
|
import org.eclipse.jetty.servlet.ServletHolder;
|
||||||
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
|
import org.hl7.fhir.r4.model.Bundle;
|
||||||
|
import org.hl7.fhir.r4.model.ConceptMap;
|
||||||
|
import org.hl7.fhir.r4.model.ConceptMap.ConceptMapGroupComponent;
|
||||||
|
import org.hl7.fhir.r4.model.ConceptMap.SourceElementComponent;
|
||||||
|
import org.hl7.fhir.r4.model.ConceptMap.TargetElementComponent;
|
||||||
|
import org.hl7.fhir.r4.model.Enumerations.ConceptMapEquivalence;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
public class ImportCsvToConceptMapCommandTest {
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ImportCsvToConceptMapCommandTest.class);
|
||||||
|
private static final String CM_URL = "http://example.com/conceptmap";
|
||||||
|
private static final String VS_URL_1 = "http://example.com/valueset/1";
|
||||||
|
private static final String VS_URL_2 = "http://example.com/valueset/2";
|
||||||
|
private static final String CS_URL_1 = "http://example.com/codesystem/1";
|
||||||
|
private static final String CS_URL_2 = "http://example.com/codesystem/2";
|
||||||
|
private static final String CS_URL_3 = "http://example.com/codesystem/3";
|
||||||
|
private static final String FILENAME = "import-csv-to-conceptmap-command-test-input.csv";
|
||||||
|
|
||||||
|
private static String file;
|
||||||
|
private static String ourBase;
|
||||||
|
private static IGenericClient ourClient;
|
||||||
|
private static FhirContext ourCtx = FhirContext.forR4();
|
||||||
|
private static int ourPort;
|
||||||
|
private static Server ourServer;
|
||||||
|
|
||||||
|
private static RestfulServer restfulServer;
|
||||||
|
|
||||||
|
private static HashMapResourceProviderConceptMapR4 hashMapResourceProviderConceptMapR4;
|
||||||
|
|
||||||
|
static {
|
||||||
|
System.setProperty("test", "true");
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void afterClearResourceProvider() {
|
||||||
|
HashMapResourceProviderConceptMapR4 resourceProvider = (HashMapResourceProviderConceptMapR4) restfulServer.getResourceProviders().iterator().next();
|
||||||
|
resourceProvider.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void afterClassClearContext() throws Exception {
|
||||||
|
ourServer.stop();
|
||||||
|
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void beforeClass() throws Exception {
|
||||||
|
ourPort = PortUtil.findFreePort();
|
||||||
|
ourServer = new Server(ourPort);
|
||||||
|
|
||||||
|
ServletHandler servletHandler = new ServletHandler();
|
||||||
|
|
||||||
|
restfulServer = new RestfulServer(ourCtx);
|
||||||
|
restfulServer.registerInterceptor(new VerboseLoggingInterceptor());
|
||||||
|
restfulServer.setResourceProviders(new HashMapResourceProviderConceptMapR4(ourCtx));
|
||||||
|
|
||||||
|
ServletHolder servletHolder = new ServletHolder(restfulServer);
|
||||||
|
servletHandler.addServletWithMapping(servletHolder, "/*");
|
||||||
|
ourServer.setHandler(servletHandler);
|
||||||
|
|
||||||
|
ourServer.start();
|
||||||
|
|
||||||
|
ourBase = "http://localhost:" + ourPort;
|
||||||
|
|
||||||
|
ourClient = ourCtx.newRestfulGenericClient(ourBase);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConditionalUpdateResultsInCreate() {
|
||||||
|
ConceptMap conceptMap = ExportConceptMapToCsvCommandTest.createConceptMap();
|
||||||
|
String conceptMapUrl = conceptMap.getUrl();
|
||||||
|
|
||||||
|
ourLog.info("Searching for existing ConceptMap with specified URL (i.e. ConceptMap.url): {}", conceptMapUrl);
|
||||||
|
MethodOutcome methodOutcome = ourClient
|
||||||
|
.update()
|
||||||
|
.resource(conceptMap)
|
||||||
|
.conditional()
|
||||||
|
.where(ConceptMap.URL.matches().value(conceptMapUrl))
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
// Do not simplify to assertEquals(...)
|
||||||
|
assertTrue(Boolean.TRUE.equals(methodOutcome.getCreated()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConditionalUpdateResultsInUpdate() {
|
||||||
|
ConceptMap conceptMap = ExportConceptMapToCsvCommandTest.createConceptMap();
|
||||||
|
ourClient.create().resource(conceptMap).execute();
|
||||||
|
String conceptMapUrl = conceptMap.getUrl();
|
||||||
|
|
||||||
|
ourLog.info("Searching for existing ConceptMap with specified URL (i.e. ConceptMap.url): {}", conceptMapUrl);
|
||||||
|
MethodOutcome methodOutcome = ourClient
|
||||||
|
.update()
|
||||||
|
.resource(conceptMap)
|
||||||
|
.conditional()
|
||||||
|
.where(ConceptMap.URL.matches().value(conceptMapUrl))
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
// Do not simplify to assertEquals(...)
|
||||||
|
assertTrue(!Boolean.TRUE.equals(methodOutcome.getCreated()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNonConditionalUpdate() {
|
||||||
|
ConceptMap conceptMap = ExportConceptMapToCsvCommandTest.createConceptMap();
|
||||||
|
ourClient.create().resource(conceptMap).execute();
|
||||||
|
|
||||||
|
Bundle response = ourClient
|
||||||
|
.search()
|
||||||
|
.forResource(ConceptMap.class)
|
||||||
|
.where(ConceptMap.URL.matches().value(CM_URL))
|
||||||
|
.returnBundle(Bundle.class)
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
ConceptMap resultConceptMap = (ConceptMap) response.getEntryFirstRep().getResource();
|
||||||
|
|
||||||
|
MethodOutcome methodOutcome = ourClient
|
||||||
|
.update()
|
||||||
|
.resource(resultConceptMap)
|
||||||
|
.withId(resultConceptMap.getIdElement())
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
assertNull(methodOutcome.getCreated());
|
||||||
|
|
||||||
|
// Do not simplify to assertEquals(...)
|
||||||
|
assertTrue(!Boolean.TRUE.equals(methodOutcome.getCreated()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testImportCsvToConceptMapCommand() throws FHIRException {
|
||||||
|
ClassLoader classLoader = getClass().getClassLoader();
|
||||||
|
File fileToImport = new File(classLoader.getResource(FILENAME).getFile());
|
||||||
|
ImportCsvToConceptMapCommandTest.file = fileToImport.getAbsolutePath();
|
||||||
|
|
||||||
|
App.main(new String[] {"import-csv-to-conceptmap",
|
||||||
|
"-v", "r4",
|
||||||
|
"-t", ourBase,
|
||||||
|
"-u", CM_URL,
|
||||||
|
"-i", VS_URL_1,
|
||||||
|
"-o", VS_URL_2,
|
||||||
|
"-f", file,
|
||||||
|
"-l"});
|
||||||
|
|
||||||
|
Bundle response = ourClient
|
||||||
|
.search()
|
||||||
|
.forResource(ConceptMap.class)
|
||||||
|
.where(ConceptMap.URL.matches().value(CM_URL))
|
||||||
|
.returnBundle(Bundle.class)
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
ConceptMap conceptMap = (ConceptMap) response.getEntryFirstRep().getResource();
|
||||||
|
|
||||||
|
ourLog.info(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap));
|
||||||
|
|
||||||
|
assertEquals("http://localhost:" + ourPort + "/ConceptMap/1/_history/1", conceptMap.getId());
|
||||||
|
|
||||||
|
assertEquals(CM_URL, conceptMap.getUrl());
|
||||||
|
assertEquals(VS_URL_1, conceptMap.getSourceUriType().getValueAsString());
|
||||||
|
assertEquals(VS_URL_2, conceptMap.getTargetUriType().getValueAsString());
|
||||||
|
|
||||||
|
assertEquals(3, conceptMap.getGroup().size());
|
||||||
|
|
||||||
|
ConceptMapGroupComponent group = conceptMap.getGroup().get(0);
|
||||||
|
assertEquals(CS_URL_1, group.getSource());
|
||||||
|
assertEquals("Version 1s", group.getSourceVersion());
|
||||||
|
assertEquals(CS_URL_2, group.getTarget());
|
||||||
|
assertEquals("Version 2t", group.getTargetVersion());
|
||||||
|
|
||||||
|
assertEquals(4, group.getElement().size());
|
||||||
|
|
||||||
|
SourceElementComponent source = group.getElement().get(0);
|
||||||
|
assertEquals("Code 1a", source.getCode());
|
||||||
|
assertEquals("Display 1a", source.getDisplay());
|
||||||
|
|
||||||
|
assertEquals(1, source.getTarget().size());
|
||||||
|
|
||||||
|
TargetElementComponent target = source.getTarget().get(0);
|
||||||
|
assertEquals("Code 2a", target.getCode());
|
||||||
|
assertEquals("Display 2a", target.getDisplay());
|
||||||
|
assertEquals(ConceptMapEquivalence.EQUAL, target.getEquivalence());
|
||||||
|
assertEquals("2a This is a comment.", target.getComment());
|
||||||
|
|
||||||
|
source = group.getElement().get(1);
|
||||||
|
assertEquals("Code 1b", source.getCode());
|
||||||
|
assertEquals("Display 1b", source.getDisplay());
|
||||||
|
|
||||||
|
assertEquals(1, source.getTarget().size());
|
||||||
|
|
||||||
|
target = source.getTarget().get(0);
|
||||||
|
assertEquals("Code 2b", target.getCode());
|
||||||
|
assertEquals("Display 2b", target.getDisplay());
|
||||||
|
assertEquals(ConceptMapEquivalence.EQUAL, target.getEquivalence());
|
||||||
|
assertEquals("2b This is a comment.", target.getComment());
|
||||||
|
|
||||||
|
source = group.getElement().get(2);
|
||||||
|
assertEquals("Code 1c", source.getCode());
|
||||||
|
assertEquals("Display 1c", source.getDisplay());
|
||||||
|
|
||||||
|
assertEquals(1, source.getTarget().size());
|
||||||
|
|
||||||
|
target = source.getTarget().get(0);
|
||||||
|
assertEquals("Code 2c", target.getCode());
|
||||||
|
assertEquals("Display 2c", target.getDisplay());
|
||||||
|
assertEquals(ConceptMapEquivalence.EQUAL, target.getEquivalence());
|
||||||
|
assertEquals("2c This is a comment.", target.getComment());
|
||||||
|
|
||||||
|
source = group.getElement().get(3);
|
||||||
|
assertEquals("Code 1d", source.getCode());
|
||||||
|
assertEquals("Display 1d", source.getDisplay());
|
||||||
|
|
||||||
|
assertEquals(1, source.getTarget().size());
|
||||||
|
|
||||||
|
target = source.getTarget().get(0);
|
||||||
|
assertEquals("Code 2d", target.getCode());
|
||||||
|
assertEquals("Display 2d", target.getDisplay());
|
||||||
|
assertEquals(ConceptMapEquivalence.EQUAL, target.getEquivalence());
|
||||||
|
assertEquals("2d This is a comment.", target.getComment());
|
||||||
|
|
||||||
|
group = conceptMap.getGroup().get(1);
|
||||||
|
assertEquals(CS_URL_1, group.getSource());
|
||||||
|
assertEquals("Version 1s", group.getSourceVersion());
|
||||||
|
assertEquals(CS_URL_3, group.getTarget());
|
||||||
|
assertEquals("Version 3t", group.getTargetVersion());
|
||||||
|
|
||||||
|
assertEquals(4, group.getElement().size());
|
||||||
|
|
||||||
|
source = group.getElement().get(0);
|
||||||
|
assertEquals("Code 1a", source.getCode());
|
||||||
|
assertEquals("Display 1a", source.getDisplay());
|
||||||
|
|
||||||
|
assertEquals(1, source.getTarget().size());
|
||||||
|
|
||||||
|
target = source.getTarget().get(0);
|
||||||
|
assertEquals("Code 3a", target.getCode());
|
||||||
|
assertEquals("Display 3a", target.getDisplay());
|
||||||
|
assertEquals(ConceptMapEquivalence.EQUAL, target.getEquivalence());
|
||||||
|
assertEquals("3a This is a comment.", target.getComment());
|
||||||
|
|
||||||
|
source = group.getElement().get(1);
|
||||||
|
assertEquals("Code 1b", source.getCode());
|
||||||
|
assertEquals("Display 1b", source.getDisplay());
|
||||||
|
|
||||||
|
assertEquals(1, source.getTarget().size());
|
||||||
|
|
||||||
|
target = source.getTarget().get(0);
|
||||||
|
assertEquals("Code 3b", target.getCode());
|
||||||
|
assertEquals("Display 3b", target.getDisplay());
|
||||||
|
assertEquals(ConceptMapEquivalence.EQUAL, target.getEquivalence());
|
||||||
|
assertEquals("3b This is a comment.", target.getComment());
|
||||||
|
|
||||||
|
source = group.getElement().get(2);
|
||||||
|
assertEquals("Code 1c", source.getCode());
|
||||||
|
assertEquals("Display 1c", source.getDisplay());
|
||||||
|
|
||||||
|
assertEquals(1, source.getTarget().size());
|
||||||
|
|
||||||
|
target = source.getTarget().get(0);
|
||||||
|
assertEquals("Code 3c", target.getCode());
|
||||||
|
assertEquals("Display 3c", target.getDisplay());
|
||||||
|
assertEquals(ConceptMapEquivalence.EQUAL, target.getEquivalence());
|
||||||
|
assertEquals("3c This is a comment.", target.getComment());
|
||||||
|
|
||||||
|
source = group.getElement().get(3);
|
||||||
|
assertEquals("Code 1d", source.getCode());
|
||||||
|
assertEquals("Display 1d", source.getDisplay());
|
||||||
|
|
||||||
|
assertEquals(1, source.getTarget().size());
|
||||||
|
|
||||||
|
target = source.getTarget().get(0);
|
||||||
|
assertEquals("Code 3d", target.getCode());
|
||||||
|
assertEquals("Display 3d", target.getDisplay());
|
||||||
|
assertEquals(ConceptMapEquivalence.EQUAL, target.getEquivalence());
|
||||||
|
assertEquals("3d This is a comment.", target.getComment());
|
||||||
|
|
||||||
|
group = conceptMap.getGroup().get(2);
|
||||||
|
assertEquals(CS_URL_2, group.getSource());
|
||||||
|
assertEquals("Version 2s", group.getSourceVersion());
|
||||||
|
assertEquals(CS_URL_3, group.getTarget());
|
||||||
|
assertEquals("Version 3t", group.getTargetVersion());
|
||||||
|
|
||||||
|
assertEquals(4, group.getElement().size());
|
||||||
|
|
||||||
|
source = group.getElement().get(0);
|
||||||
|
assertEquals("Code 2a", source.getCode());
|
||||||
|
assertEquals("Display 2a", source.getDisplay());
|
||||||
|
|
||||||
|
assertEquals(1, source.getTarget().size());
|
||||||
|
|
||||||
|
target = source.getTarget().get(0);
|
||||||
|
assertEquals("Code 3a", target.getCode());
|
||||||
|
assertEquals("Display 3a", target.getDisplay());
|
||||||
|
assertEquals(ConceptMapEquivalence.EQUAL, target.getEquivalence());
|
||||||
|
assertEquals("3a This is a comment.", target.getComment());
|
||||||
|
|
||||||
|
source = group.getElement().get(1);
|
||||||
|
assertEquals("Code 2b", source.getCode());
|
||||||
|
assertEquals("Display 2b", source.getDisplay());
|
||||||
|
|
||||||
|
assertEquals(1, source.getTarget().size());
|
||||||
|
|
||||||
|
target = source.getTarget().get(0);
|
||||||
|
assertEquals("Code 3b", target.getCode());
|
||||||
|
assertEquals("Display 3b", target.getDisplay());
|
||||||
|
assertEquals(ConceptMapEquivalence.EQUAL, target.getEquivalence());
|
||||||
|
assertEquals("3b This is a comment.", target.getComment());
|
||||||
|
|
||||||
|
source = group.getElement().get(2);
|
||||||
|
assertEquals("Code 2c", source.getCode());
|
||||||
|
assertEquals("Display 2c", source.getDisplay());
|
||||||
|
|
||||||
|
assertEquals(1, source.getTarget().size());
|
||||||
|
|
||||||
|
target = source.getTarget().get(0);
|
||||||
|
assertEquals("Code 3c", target.getCode());
|
||||||
|
assertEquals("Display 3c", target.getDisplay());
|
||||||
|
assertEquals(ConceptMapEquivalence.EQUAL, target.getEquivalence());
|
||||||
|
assertEquals("3c This is a comment.", target.getComment());
|
||||||
|
|
||||||
|
source = group.getElement().get(3);
|
||||||
|
assertEquals("Code 2d", source.getCode());
|
||||||
|
assertEquals("Display 2d", source.getDisplay());
|
||||||
|
|
||||||
|
assertEquals(1, source.getTarget().size());
|
||||||
|
|
||||||
|
target = source.getTarget().get(0);
|
||||||
|
assertEquals("Code 3d", target.getCode());
|
||||||
|
assertEquals("Display 3d", target.getDisplay());
|
||||||
|
assertEquals(ConceptMapEquivalence.EQUAL, target.getEquivalence());
|
||||||
|
assertEquals("3d This is a comment.", target.getComment());
|
||||||
|
|
||||||
|
App.main(new String[] {"import-csv-to-conceptmap",
|
||||||
|
"-v", "r4",
|
||||||
|
"-t", ourBase,
|
||||||
|
"-u", CM_URL,
|
||||||
|
"-i", VS_URL_1,
|
||||||
|
"-o", VS_URL_2,
|
||||||
|
"-f", file,
|
||||||
|
"-l"});
|
||||||
|
|
||||||
|
response = ourClient
|
||||||
|
.search()
|
||||||
|
.forResource(ConceptMap.class)
|
||||||
|
.where(ConceptMap.URL.matches().value(CM_URL))
|
||||||
|
.returnBundle(Bundle.class)
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
conceptMap = (ConceptMap) response.getEntryFirstRep().getResource();
|
||||||
|
|
||||||
|
assertEquals("http://localhost:" + ourPort + "/ConceptMap/1/_history/2", conceptMap.getId());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
SOURCE_CODE_SYSTEM,SOURCE_CODE_SYSTEM_VERSION,TARGET_CODE_SYSTEM,TARGET_CODE_SYSTEM_VERSION,SOURCE_CODE,SOURCE_DISPLAY,TARGET_CODE,TARGET_DISPLAY,EQUIVALENCE,COMMENT
|
||||||
|
http://example.com/codesystem/1,Version 1s,http://example.com/codesystem/2,Version 2t,Code 1a,Display 1a,Code 2a,Display 2a,equal,2a This is a comment.
|
||||||
|
http://example.com/codesystem/1,Version 1s,http://example.com/codesystem/2,Version 2t,Code 1b,Display 1b,Code 2b,Display 2b,equal,2b This is a comment.
|
||||||
|
http://example.com/codesystem/1,Version 1s,http://example.com/codesystem/2,Version 2t,Code 1c,Display 1c,Code 2c,Display 2c,equal,2c This is a comment.
|
||||||
|
http://example.com/codesystem/1,Version 1s,http://example.com/codesystem/2,Version 2t,Code 1d,Display 1d,Code 2d,Display 2d,equal,2d This is a comment.
|
||||||
|
http://example.com/codesystem/1,Version 1s,http://example.com/codesystem/3,Version 3t,Code 1a,Display 1a,Code 3a,Display 3a,equal,3a This is a comment.
|
||||||
|
http://example.com/codesystem/1,Version 1s,http://example.com/codesystem/3,Version 3t,Code 1b,Display 1b,Code 3b,Display 3b,equal,3b This is a comment.
|
||||||
|
http://example.com/codesystem/1,Version 1s,http://example.com/codesystem/3,Version 3t,Code 1c,Display 1c,Code 3c,Display 3c,equal,3c This is a comment.
|
||||||
|
http://example.com/codesystem/1,Version 1s,http://example.com/codesystem/3,Version 3t,Code 1d,Display 1d,Code 3d,Display 3d,equal,3d This is a comment.
|
||||||
|
http://example.com/codesystem/2,Version 2s,http://example.com/codesystem/3,Version 3t,Code 2a,Display 2a,Code 3a,Display 3a,equal,3a This is a comment.
|
||||||
|
http://example.com/codesystem/2,Version 2s,http://example.com/codesystem/3,Version 3t,Code 2b,Display 2b,Code 3b,Display 3b,equal,3b This is a comment.
|
||||||
|
http://example.com/codesystem/2,Version 2s,http://example.com/codesystem/3,Version 3t,Code 2c,Display 2c,Code 3c,Display 3c,equal,3c This is a comment.
|
||||||
|
http://example.com/codesystem/2,Version 2s,http://example.com/codesystem/3,Version 3t,Code 2d,Display 2d,Code 3d,Display 3d,equal,3d This is a comment.
|
|
|
@ -0,0 +1,198 @@
|
||||||
|
package ca.uhn.fhir.rest.server.provider;
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - Server Framework
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2018 University Health Network
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.rest.annotation.*;
|
||||||
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is a simple implementation of the resource provider
|
||||||
|
* interface that uses a HashMap to store all resources in memory.
|
||||||
|
* It is essentially a copy of {@link ca.uhn.fhir.rest.server.provider.HashMapResourceProvider}
|
||||||
|
* with the {@link Update} and {@link ResourceParam} annotations removed from method
|
||||||
|
* {@link ca.uhn.fhir.rest.server.provider.HashMapResourceProvider#update(IBaseResource)}.
|
||||||
|
* Non-generic subclasses of this abstract class may implement their own annotated methods (e.g. a conditional
|
||||||
|
* update method specifically for ConceptMap resources).
|
||||||
|
* <p>
|
||||||
|
* This class currently supports the following FHIR operations:
|
||||||
|
* </p>
|
||||||
|
* <ul>
|
||||||
|
* <li>Create</li>
|
||||||
|
* <li>Update existing resource</li>
|
||||||
|
* <li>Update non-existing resource (e.g. create with client-supplied ID)</li>
|
||||||
|
* <li>Delete</li>
|
||||||
|
* <li>Search by resource type with no parameters</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param <T> The resource type to support
|
||||||
|
*/
|
||||||
|
public class AbstractHashMapResourceProvider<T extends IBaseResource> implements IResourceProvider {
|
||||||
|
private static final Logger ourLog = LoggerFactory.getLogger(AbstractHashMapResourceProvider.class);
|
||||||
|
private final Class<T> myResourceType;
|
||||||
|
private final FhirContext myFhirContext;
|
||||||
|
private final String myResourceName;
|
||||||
|
protected Map<String, TreeMap<Long, T>> myIdToVersionToResourceMap = new HashMap<>();
|
||||||
|
private long myNextId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param theFhirContext The FHIR context
|
||||||
|
* @param theResourceType The resource type to support
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
|
public AbstractHashMapResourceProvider(FhirContext theFhirContext, Class<T> theResourceType) {
|
||||||
|
myFhirContext = theFhirContext;
|
||||||
|
myResourceType = theResourceType;
|
||||||
|
myResourceName = myFhirContext.getResourceDefinition(theResourceType).getName();
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear all data held in this resource provider
|
||||||
|
*/
|
||||||
|
public void clear() {
|
||||||
|
myNextId = 1;
|
||||||
|
myIdToVersionToResourceMap.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Create
|
||||||
|
public MethodOutcome create(@ResourceParam T theResource) {
|
||||||
|
long idPart = myNextId++;
|
||||||
|
String idPartAsString = Long.toString(idPart);
|
||||||
|
Long versionIdPart = 1L;
|
||||||
|
|
||||||
|
IIdType id = store(theResource, idPartAsString, versionIdPart);
|
||||||
|
|
||||||
|
return new MethodOutcome()
|
||||||
|
.setCreated(true)
|
||||||
|
.setId(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
public MethodOutcome delete(@IdParam IIdType theId) {
|
||||||
|
TreeMap<Long, T> versions = myIdToVersionToResourceMap.get(theId.getIdPart());
|
||||||
|
if (versions == null || versions.isEmpty()) {
|
||||||
|
throw new ResourceNotFoundException(theId);
|
||||||
|
}
|
||||||
|
|
||||||
|
long nextVersion = versions.lastEntry().getKey() + 1L;
|
||||||
|
IIdType id = store(null, theId.getIdPart(), nextVersion);
|
||||||
|
|
||||||
|
return new MethodOutcome()
|
||||||
|
.setId(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends IBaseResource> getResourceType() {
|
||||||
|
return myResourceType;
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized TreeMap<Long, T> getVersionToResource(String theIdPart) {
|
||||||
|
if (!myIdToVersionToResourceMap.containsKey(theIdPart)) {
|
||||||
|
myIdToVersionToResourceMap.put(theIdPart, new TreeMap<Long, T>());
|
||||||
|
}
|
||||||
|
return myIdToVersionToResourceMap.get(theIdPart);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Read(version = true)
|
||||||
|
public IBaseResource read(@IdParam IIdType theId) {
|
||||||
|
TreeMap<Long, T> versions = myIdToVersionToResourceMap.get(theId.getIdPart());
|
||||||
|
if (versions == null || versions.isEmpty()) {
|
||||||
|
throw new ResourceNotFoundException(theId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (theId.hasVersionIdPart()) {
|
||||||
|
Long versionId = theId.getVersionIdPartAsLong();
|
||||||
|
if (!versions.containsKey(versionId)) {
|
||||||
|
throw new ResourceNotFoundException(theId);
|
||||||
|
} else {
|
||||||
|
T resource = versions.get(versionId);
|
||||||
|
if (resource == null) {
|
||||||
|
throw new ResourceGoneException(theId);
|
||||||
|
}
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return versions.lastEntry().getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Search
|
||||||
|
public List<IBaseResource> search() {
|
||||||
|
List<IBaseResource> retVal = new ArrayList<>();
|
||||||
|
|
||||||
|
for (TreeMap<Long, T> next : myIdToVersionToResourceMap.values()) {
|
||||||
|
if (next.isEmpty() == false) {
|
||||||
|
retVal.add(next.lastEntry().getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IIdType store(@ResourceParam T theResource, String theIdPart, Long theVersionIdPart) {
|
||||||
|
IIdType id = myFhirContext.getVersion().newIdType();
|
||||||
|
id.setParts(null, myResourceName, theIdPart, Long.toString(theVersionIdPart));
|
||||||
|
if (theResource != null) {
|
||||||
|
theResource.setId(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeMap<Long, T> versionToResource = getVersionToResource(theIdPart);
|
||||||
|
versionToResource.put(theVersionIdPart, theResource);
|
||||||
|
|
||||||
|
ourLog.info("Storing resource with ID: {}", id.getValue());
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MethodOutcome update(T theResource) {
|
||||||
|
String idPartAsString = theResource.getIdElement().getIdPart();
|
||||||
|
TreeMap<Long, T> versionToResource = getVersionToResource(idPartAsString);
|
||||||
|
|
||||||
|
Long versionIdPart;
|
||||||
|
Boolean created;
|
||||||
|
if (versionToResource.isEmpty()) {
|
||||||
|
versionIdPart = 1L;
|
||||||
|
created = true;
|
||||||
|
} else {
|
||||||
|
versionIdPart = versionToResource.lastKey() + 1L;
|
||||||
|
created = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
IIdType id = store(theResource, idPartAsString, versionIdPart);
|
||||||
|
|
||||||
|
return new MethodOutcome()
|
||||||
|
.setCreated(created)
|
||||||
|
.setId(id);
|
||||||
|
}
|
||||||
|
}
|
|
@ -54,7 +54,7 @@ public class HashMapResourceProvider<T extends IBaseResource> implements IResour
|
||||||
private final Class<T> myResourceType;
|
private final Class<T> myResourceType;
|
||||||
private final FhirContext myFhirContext;
|
private final FhirContext myFhirContext;
|
||||||
private final String myResourceName;
|
private final String myResourceName;
|
||||||
private Map<String, TreeMap<Long, T>> myIdToVersionToResourceMap = new HashMap<>();
|
protected Map<String, TreeMap<Long, T>> myIdToVersionToResourceMap = new HashMap<>();
|
||||||
private long myNextId;
|
private long myNextId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -124,6 +124,11 @@
|
||||||
The <![CDATA[<code>ConceptMap</code>]] operation <![CDATA[<code>$translate</code>]]> has been
|
The <![CDATA[<code>ConceptMap</code>]] operation <![CDATA[<code>$translate</code>]]> has been
|
||||||
implemented.
|
implemented.
|
||||||
</action>
|
</action>
|
||||||
|
<action type="add" issue="927">
|
||||||
|
HAPI-FHIR_CLI now includes two new commands: one for importing and populating a
|
||||||
|
<![CDATA[<code>ConceptMap</code>]]> resource from a CSV; and one for exporting a
|
||||||
|
<![CDATA[<code>ConceptMap</code>]]> resource to a CSV.
|
||||||
|
</action>
|
||||||
</release>
|
</release>
|
||||||
<release version="3.3.0" date="2018-03-29">
|
<release version="3.3.0" date="2018-03-29">
|
||||||
<action type="add">
|
<action type="add">
|
||||||
|
|
Loading…
Reference in New Issue