Add method to upload SCT to JPA server

This commit is contained in:
jamesagnew 2016-05-27 08:56:36 -04:00
parent 69266dc7d0
commit cd2af98064
6 changed files with 228 additions and 13 deletions

View File

@ -40,6 +40,7 @@ public class App {
ourCommands.add(new ValidateCommand());
ourCommands.add(new ValidationDataUploader());
ourCommands.add(new WebsocketSubscribeCommand());
ourCommands.add(new UploadTerminologyCommand());
Collections.sort(ourCommands);
}

View File

@ -0,0 +1,159 @@
package ca.uhn.fhir.cli;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.fusesource.jansi.Ansi;
import org.hl7.fhir.dstu3.hapi.validation.DefaultProfileValidationSupport;
import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.dstu3.model.Attachment;
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.dstu3.model.Bundle.BundleType;
import org.hl7.fhir.dstu3.model.Bundle.HTTPVerb;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.Parameters;
import org.hl7.fhir.dstu3.model.Resource;
import org.hl7.fhir.dstu3.model.StringType;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.omg.Dynamic.Parameter;
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
import ca.uhn.fhir.model.dstu2.resource.Bundle.EntryRequest;
import ca.uhn.fhir.model.dstu2.valueset.HTTPVerbEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.client.IGenericClient;
import ca.uhn.fhir.rest.client.apache.GZipContentInterceptor;
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.util.BundleUtil;
import ca.uhn.fhir.util.ResourceReferenceInfo;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.ValidationResult;
public class UploadTerminologyCommand extends BaseCommand {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(UploadTerminologyCommand.class);
@Override
public String getCommandDescription() {
//@formatter:off
return "Uploads a terminology package (e.g. a SNOMED CT ZIP file) to a HAPI JPA server. "
+ "Note that this command uses a custom operation that is only implemented on HAPI "
+ "JPA servers that have been configured to accept it.";
//@formatter:on
}
@Override
public String getCommandName() {
return "upload-terminology";
}
@Override
public Options getOptions() {
Options options = new Options();
Option opt;
addFhirVersionOption(options);
opt = new Option("t", "target", true, "Base URL for the target server (e.g. \"http://example.com/fhir\")");
opt.setRequired(true);
options.addOption(opt);
opt = new Option("u", "url", true, "The code system URL associated with this upload (e.g. " + IHapiTerminologyLoaderSvc.SCT_URL + ")");
opt.setRequired(false);
options.addOption(opt);
opt = new Option("d", "data", true, "Local *.zip containing file to use to upload");
opt.setRequired(false);
options.addOption(opt);
return options;
}
@Override
public void run(CommandLine theCommandLine) throws Exception {
FhirContext ctx = getSpecVersionContext(theCommandLine);
String targetServer = theCommandLine.getOptionValue("t");
if (isBlank(targetServer)) {
throw new ParseException("No target server (-t) specified");
} else if (targetServer.startsWith("http") == false && targetServer.startsWith("file") == false) {
throw new ParseException("Invalid target server specified, must begin with 'http' or 'file'");
}
String termUrl = theCommandLine.getOptionValue("u");
if (isBlank(termUrl)) {
throw new ParseException("No URL provided");
}
String datafile = theCommandLine.getOptionValue("d");
if (isBlank(datafile)) {
throw new ParseException("No data file provided");
}
IGenericClient client = super.newClient(ctx, targetServer);
IBaseParameters inputParameters;
if (ctx.getVersion().getVersion() == FhirVersionEnum.DSTU3) {
Parameters p = new Parameters();
p.addParameter().setName("url").setValue(new StringType(termUrl));
p.addParameter().setName("localfile").setValue(new StringType(datafile));
inputParameters = p;
} else {
throw new ParseException("This command does not support FHIR version " + ctx.getVersion().getVersion());
}
ourLog.info("Beginning upload - This may take a while...");
IBaseParameters response = client
.operation()
.onServer()
.named("upload-external-code-system")
.withParameters(inputParameters)
.execute();
ourLog.info("Upload complete!");
}
}

View File

@ -210,6 +210,10 @@
</overlays>
<webXml>src/main/webapp/WEB-INF/web.xml</webXml>
<attachClasses>true</attachClasses>
<excludes>
WEB-INF/lib/Saxon-HE-*,
WEB-INF/lib/hapi-*
</excludes>
</configuration>
</plugin>

View File

@ -1,5 +1,6 @@
package ca.uhn.fhir.jpa.demo;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@ -19,6 +20,7 @@ import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu1;
import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2;
import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3;
import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3;
import ca.uhn.fhir.jpa.provider.dstu3.TerminologyUploaderProviderDstu3;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
import ca.uhn.fhir.model.dstu2.resource.Bundle;
@ -74,13 +76,14 @@ public class JpaServerDemo extends RestfulServer {
* The system provider implements non-resource-type methods, such as
* transaction, and global history.
*/
Object systemProvider;
List<Object> systemProvider = new ArrayList<Object>();
if (fhirVersion == FhirVersionEnum.DSTU1) {
systemProvider = myAppCtx.getBean("mySystemProviderDstu1", JpaSystemProviderDstu1.class);
systemProvider.add(myAppCtx.getBean("mySystemProviderDstu1", JpaSystemProviderDstu1.class));
} else if (fhirVersion == FhirVersionEnum.DSTU2) {
systemProvider = myAppCtx.getBean("mySystemProviderDstu2", JpaSystemProviderDstu2.class);
systemProvider.add(myAppCtx.getBean("mySystemProviderDstu2", JpaSystemProviderDstu2.class));
} else if (fhirVersion == FhirVersionEnum.DSTU3) {
systemProvider = myAppCtx.getBean("mySystemProviderDstu3", JpaSystemProviderDstu3.class);
systemProvider.add(myAppCtx.getBean("mySystemProviderDstu3", JpaSystemProviderDstu3.class));
systemProvider.add(myAppCtx.getBean(TerminologyUploaderProviderDstu3.class));
} else {
throw new IllegalStateException();
}

View File

@ -1,12 +1,20 @@
package ca.uhn.fhir.jpa.provider.dstu3;
import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.LockableFileWriter;
import org.hl7.fhir.dstu3.model.Attachment;
import org.hl7.fhir.dstu3.model.IntegerType;
import org.hl7.fhir.dstu3.model.Parameters;
import org.hl7.fhir.dstu3.model.StringType;
import org.hl7.fhir.dstu3.model.UriType;
import org.springframework.beans.factory.annotation.Autowired;
@ -16,9 +24,11 @@ import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc.UploadStatistics;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.method.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public class TerminologyUploaderProviderDstu3 extends BaseJpaProvider {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TerminologyUploaderProviderDstu3.class);
@Autowired
private IHapiTerminologyLoaderSvc myTerminologyLoaderSvc;
@ -30,18 +40,28 @@ public class TerminologyUploaderProviderDstu3 extends BaseJpaProvider {
public Parameters lookup(
HttpServletRequest theServletRequest,
@OperationParam(name="url", min=1) UriType theUrl,
@OperationParam(name="package", min=1) Attachment thePackage,
@OperationParam(name="package", min=0) Attachment thePackage,
@OperationParam(name="localfile", min=0) StringType theLocalFile,
RequestDetails theRequestDetails
) {
//@formatter:on
startRequest(theServletRequest);
try {
if (thePackage == null || thePackage.getData() == null || thePackage.getData().length == 0) {
throw new InvalidRequestException("Missing mandatory 'package' parameter, or package had no data");
byte[] data;
if (theLocalFile != null && isNotBlank(theLocalFile.getValue())) {
ourLog.info("Reading in local file: {}", theLocalFile.getValue());
try {
data = IOUtils.toByteArray(new FileInputStream(theLocalFile.getValue()));
} catch (IOException e) {
throw new InternalErrorException(e);
}
} else if (thePackage == null || thePackage.getData() == null || thePackage.getData().length == 0) {
throw new InvalidRequestException("No 'localfile' or 'package' parameter, or package had no data");
} else {
data = thePackage.getData();
}
byte[] data = thePackage.getData();
String url = theUrl != null ? theUrl.getValueAsString() : null;
url = defaultString(url);

View File

@ -1,9 +1,13 @@
package ca.uhn.fhir.jpa.provider.dstu3;
import static org.hamcrest.Matchers.greaterThan;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
@ -12,12 +16,10 @@ import java.util.zip.ZipOutputStream;
import org.apache.commons.io.IOUtils;
import org.hl7.fhir.dstu3.model.Attachment;
import org.hl7.fhir.dstu3.model.BooleanType;
import org.hl7.fhir.dstu3.model.CodeType;
import org.hl7.fhir.dstu3.model.IntegerType;
import org.hl7.fhir.dstu3.model.Parameters;
import org.hl7.fhir.dstu3.model.StringType;
import org.hl7.fhir.dstu3.model.UriType;
import org.hl7.fhir.dstu3.model.ValueSet;
import org.junit.AfterClass;
import org.junit.Test;
@ -56,6 +58,32 @@ public class TerminologyProviderDstu3Test extends BaseResourceProviderDstu3Test
assertThat(((IntegerType)respParam.getParameter().get(0).getValue()).getValue(), greaterThan(1));
}
@Test
public void testUploadSctLocalFile() throws Exception {
byte[] packageBytes = createSctZip();
File tempFile = File.createTempFile("tmp", ".zip");
tempFile.deleteOnExit();
FileOutputStream fos = new FileOutputStream(tempFile);
fos.write(packageBytes);
fos.close();
//@formatter:off
Parameters respParam = ourClient
.operation()
.onServer()
.named("upload-external-code-system")
.withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URL))
.andParameter("localfile", new StringType(tempFile.getAbsolutePath()))
.execute();
//@formatter:on
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp);
assertThat(((IntegerType)respParam.getParameter().get(0).getValue()).getValue(), greaterThan(1));
}
@Test
public void testUploadInvalidUrl() throws Exception {
byte[] packageBytes = createSctZip();
@ -107,7 +135,7 @@ public class TerminologyProviderDstu3Test extends BaseResourceProviderDstu3Test
.execute();
fail();
} catch (InvalidRequestException e) {
assertEquals("HTTP 400 Bad Request: Missing mandatory 'package' parameter, or package had no data", e.getMessage());
assertEquals("HTTP 400 Bad Request: No 'localfile' or 'package' parameter, or package had no data", e.getMessage());
}
//@formatter:on
}