Add package support (#1911)
* Begin rework of package management * Work on NPM * Work on package management * Work on NPM * NPM rework * Work on NPM * NPM package rework * Updates * Updates * Add license * Work on package server * Work on package importing * Work on package management * Package rework * Work on packages * Work on package manager * Work on pkgs * NPM work * NPM rework * Work on package cache * Work on NPM * Work on NPM * Package fixes * Add tests * Tweaks * Test fixes * Add changelog * Avoid snapshot dep
This commit is contained in:
parent
0d3ad622b5
commit
b8da4f0140
|
@ -142,21 +142,6 @@ public enum FhirVersionEnum {
|
|||
throw new IllegalStateException("Unknown version: " + this); // should not happen
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link FhirVersionEnum} which corresponds to a specific version of
|
||||
* FHIR. Partial version strings (e.g. "3.0") are acceptable.
|
||||
*
|
||||
* @return Returns null if no version exists matching the given string
|
||||
*/
|
||||
public static FhirVersionEnum forVersionString(String theVersionString) {
|
||||
for (FhirVersionEnum next : values()) {
|
||||
if (next.getFhirVersionString().startsWith(theVersionString)) {
|
||||
return next;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private interface IVersionProvider {
|
||||
String provideVersion();
|
||||
}
|
||||
|
@ -241,4 +226,44 @@ public enum FhirVersionEnum {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link FhirVersionEnum} which corresponds to a specific version of
|
||||
* FHIR. Partial version strings (e.g. "3.0") are acceptable. This method will
|
||||
* also accept version names such as "DSTU2", "STU3", "R5", etc.
|
||||
*
|
||||
* @return Returns null if no version exists matching the given string
|
||||
*/
|
||||
public static FhirVersionEnum forVersionString(String theVersionString) {
|
||||
|
||||
// Trim the point release
|
||||
String versionString = theVersionString;
|
||||
int firstDot = versionString.indexOf('.');
|
||||
if (firstDot > 0) {
|
||||
int secondDot = versionString.indexOf('.', firstDot + 1);
|
||||
if (secondDot > 0) {
|
||||
versionString = versionString.substring(0, secondDot);
|
||||
}
|
||||
}
|
||||
|
||||
for (FhirVersionEnum next : values()) {
|
||||
if (next.getFhirVersionString().startsWith(versionString)) {
|
||||
return next;
|
||||
}
|
||||
}
|
||||
|
||||
switch (theVersionString) {
|
||||
case "DSTU2":
|
||||
return FhirVersionEnum.DSTU2;
|
||||
case "DSTU3":
|
||||
case "STU3":
|
||||
return FhirVersionEnum.DSTU3;
|
||||
case "R4":
|
||||
return FhirVersionEnum.R4;
|
||||
case "R5":
|
||||
return FhirVersionEnum.R5;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
package ca.uhn.fhir.model.api.annotation;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 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 java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE})
|
||||
public @interface ExampleSupplier {
|
||||
|
||||
Class<? extends Supplier<?>>[] value();
|
||||
|
||||
}
|
|
@ -270,6 +270,7 @@ public class Constants {
|
|||
* key will be of type {@link ca.uhn.fhir.interceptor.model.RequestPartitionId}.
|
||||
*/
|
||||
public static final String RESOURCE_PARTITION_ID = Constants.class.getName() + "_RESOURCE_PARTITION_ID";
|
||||
public static final String CT_APPLICATION_GZIP = "application/gzip";
|
||||
|
||||
static {
|
||||
CHARSET_UTF8 = StandardCharsets.UTF_8;
|
||||
|
|
|
@ -20,8 +20,15 @@ package ca.uhn.fhir.util;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.context.*;
|
||||
import org.hl7.fhir.instance.model.api.*;
|
||||
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
||||
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
import org.hl7.fhir.instance.model.api.IBaseBinary;
|
||||
import org.hl7.fhir.instance.model.api.IBaseReference;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
@ -84,4 +91,22 @@ public class BinaryUtil {
|
|||
reference.setReference(theSecurityContext);
|
||||
}
|
||||
|
||||
public static void setData(FhirContext theCtx, IBaseBinary theBinary, byte[] theBytes, String theContentType) {
|
||||
getOrCreateData(theCtx, theBinary).setValue(theBytes);
|
||||
|
||||
String elementName = "contentType";
|
||||
BaseRuntimeChildDefinition entryChild = AttachmentUtil.getChild(theCtx, theBinary, elementName);
|
||||
List<IBase> entries = entryChild.getAccessor().getValues(theBinary);
|
||||
IPrimitiveType<String> contentTypeElement = entries
|
||||
.stream()
|
||||
.map(t -> (IPrimitiveType<String>) t)
|
||||
.findFirst()
|
||||
.orElseGet(() -> {
|
||||
IPrimitiveType<String> stringType = AttachmentUtil.newPrimitive(theCtx, "code", null);
|
||||
entryChild.getMutator().setValue(theBinary, stringType);
|
||||
return stringType;
|
||||
});
|
||||
contentTypeElement.setValue(theContentType);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
package ca.uhn.fhir.util;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 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.BaseRuntimeChildDefinition;
|
||||
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
public class ResourceUtil {
|
||||
|
||||
/**
|
||||
* This method removes the narrative from the resource, or if the resource is a bundle, removes the narrative from
|
||||
* all of the resources in the bundle
|
||||
*
|
||||
* @param theContext The fhir context
|
||||
* @param theInput The resource to remove the narrative from
|
||||
*/
|
||||
public static void removeNarrative(FhirContext theContext, IBaseResource theInput) {
|
||||
if (theInput instanceof IBaseBundle) {
|
||||
for (IBaseResource next : BundleUtil.toListOfResources(theContext, (IBaseBundle) theInput)) {
|
||||
removeNarrative(theContext, next);
|
||||
}
|
||||
}
|
||||
|
||||
BaseRuntimeElementCompositeDefinition<?> element = theContext.getResourceDefinition(theInput.getClass());
|
||||
BaseRuntimeChildDefinition textElement = element.getChildByName("text");
|
||||
if (textElement != null) {
|
||||
textElement.getMutator().setValue(theInput, null);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,10 +21,17 @@ package ca.uhn.fhir.util;
|
|||
*/
|
||||
|
||||
import java.io.CharArrayWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.Normalizer;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class StringUtil {
|
||||
|
||||
public class StringNormalizer {
|
||||
public static String normalizeStringForSearchIndexing(String theString) {
|
||||
if (theString == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
CharArrayWriter outBuffer = new CharArrayWriter(theString.length());
|
||||
|
||||
/*
|
||||
|
@ -51,4 +58,14 @@ public class StringNormalizer {
|
|||
}
|
||||
|
||||
|
||||
public static String toUtf8String(byte[] theBytes) {
|
||||
byte[] bytes = theBytes;
|
||||
if (theBytes.length >= 3) {
|
||||
if (theBytes[0] == -17 && theBytes[1] == -69 && theBytes[2] == -65) {
|
||||
bytes = Arrays.copyOfRange(theBytes, 3, theBytes.length);
|
||||
}
|
||||
}
|
||||
return new String(bytes, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package ca.uhn.fhir.context;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class FhirVersionEnumTest {
|
||||
|
||||
@Test
|
||||
public void testGetVersionPermissive() {
|
||||
assertEquals(FhirVersionEnum.DSTU2, FhirVersionEnum.forVersionString("1.0.0"));
|
||||
assertEquals(FhirVersionEnum.DSTU2, FhirVersionEnum.forVersionString("1.0.1"));
|
||||
|
||||
assertEquals(FhirVersionEnum.DSTU3, FhirVersionEnum.forVersionString("3.0.1"));
|
||||
assertEquals(FhirVersionEnum.DSTU3, FhirVersionEnum.forVersionString("3.0.2"));
|
||||
assertEquals(FhirVersionEnum.DSTU3, FhirVersionEnum.forVersionString("DSTU3"));
|
||||
assertEquals(FhirVersionEnum.DSTU3, FhirVersionEnum.forVersionString("STU3"));
|
||||
|
||||
assertEquals(FhirVersionEnum.R4, FhirVersionEnum.forVersionString("4.0.0"));
|
||||
assertEquals(FhirVersionEnum.R4, FhirVersionEnum.forVersionString("4.0.1"));
|
||||
assertEquals(FhirVersionEnum.R4, FhirVersionEnum.forVersionString("R4"));
|
||||
|
||||
assertEquals(FhirVersionEnum.R5, FhirVersionEnum.forVersionString("R5"));
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -173,6 +173,7 @@ public abstract class BaseApp {
|
|||
commands.add(new ExportConceptMapToCsvCommand());
|
||||
commands.add(new ImportCsvToConceptMapCommand());
|
||||
commands.add(new HapiFlywayMigrateDatabaseCommand());
|
||||
commands.add(new CreatePackageCommand());
|
||||
return commands;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,210 @@
|
|||
package ca.uhn.fhir.cli;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR - Command Line Client - API
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 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.parser.DataFormatException;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import com.google.common.io.Files;
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.apache.commons.cli.Options;
|
||||
import org.apache.commons.cli.ParseException;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.io.filefilter.FalseFileFilter;
|
||||
import org.apache.commons.io.filefilter.IOFileFilter;
|
||||
import org.apache.commons.io.filefilter.WildcardFileFilter;
|
||||
import org.hl7.fhir.utilities.cache.NpmPackage;
|
||||
import org.hl7.fhir.utilities.cache.PackageGenerator;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.print.attribute.standard.MediaSize;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
public class CreatePackageCommand extends BaseCommand {
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(CreatePackageCommand.class);
|
||||
public static final String TARGET_DIRECTORY_OPT = "target-directory";
|
||||
public static final String DEPENDENCY_OPT = "dependency";
|
||||
public static final String INCLUDE_EXAMPLE_OPT = "include-example";
|
||||
public static final String INCLUDE_PACKAGE_OPT = "include-package";
|
||||
public static final String VERSION_OPT = "version";
|
||||
public static final String NAME_OPT = "name";
|
||||
public static final String DESCRIPTION_OPT = "description";
|
||||
private File myWorkDirectory;
|
||||
private String myPackageName;
|
||||
private String myPackageVersion;
|
||||
private NpmPackage myPackage;
|
||||
private String myPackageDescription;
|
||||
|
||||
@Override
|
||||
public String getCommandDescription() {
|
||||
return "Create an NPM package using the FHIR packging format";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return "create-package";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Options getOptions() {
|
||||
Options options = new Options();
|
||||
addFhirVersionOption(options);
|
||||
|
||||
addRequiredOption(options, null, NAME_OPT, "Package Name", "The name/id of the package, e.g. \"com.example.fhir.myapp\"");
|
||||
addRequiredOption(options, null, VERSION_OPT, "Package Version", "The package version. FHIR packages use SemVer, e.g. \"1.0.0\"");
|
||||
addOptionalOption(options, null, DESCRIPTION_OPT, "Description", "A description for this package");
|
||||
addOptionalOption(options, null, INCLUDE_PACKAGE_OPT, "File Spec", "A file spec to include in the package as a package resource/artifact");
|
||||
addOptionalOption(options, null, INCLUDE_EXAMPLE_OPT, "File Spec", "A file spec to include in the package as an example resource/artifact");
|
||||
addOptionalOption(options, null, TARGET_DIRECTORY_OPT, "Directory", "The directory in which to place the final package");
|
||||
addOptionalOption(options, null, DEPENDENCY_OPT, "name:version", "Include this dependency, in the form \"name:version\"");
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanup() {
|
||||
try {
|
||||
if (myWorkDirectory != null) {
|
||||
FileUtils.deleteDirectory(myWorkDirectory);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new InternalErrorException("Failed to delete temporary directory \"" + myWorkDirectory.getAbsolutePath() + "\"", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(CommandLine theCommandLine) throws ParseException, ExecutionException {
|
||||
|
||||
parseFhirContext(theCommandLine);
|
||||
|
||||
myPackageName = theCommandLine.getOptionValue(NAME_OPT);
|
||||
if (isBlank(myPackageName)) {
|
||||
throw new ParseException("No package name supplied (--" + NAME_OPT + ")");
|
||||
}
|
||||
if (!NpmPackage.isValidName(myPackageName)) {
|
||||
throw new ParseException("Invalid package name: " + myPackageName);
|
||||
}
|
||||
|
||||
myPackageVersion = theCommandLine.getOptionValue(VERSION_OPT);
|
||||
if (isBlank(myPackageVersion)) {
|
||||
throw new ParseException("No package version supplied (--"+VERSION_OPT+")");
|
||||
}
|
||||
if (!NpmPackage.isValidVersion(myPackageVersion)) {
|
||||
throw new ParseException("Invalid package version: " + myPackageVersion);
|
||||
}
|
||||
|
||||
ourLog.info("Creating FHIR package {}#{}", myPackageName, myPackageVersion);
|
||||
|
||||
PackageGenerator manifestGenerator = new PackageGenerator();
|
||||
manifestGenerator.name(myPackageName);
|
||||
manifestGenerator.version(myPackageVersion);
|
||||
manifestGenerator.description(myPackageDescription);
|
||||
if (isNotBlank(myPackageDescription)) {
|
||||
manifestGenerator.description(myPackageDescription);
|
||||
}
|
||||
|
||||
String[] dependencies = theCommandLine.getOptionValues(DEPENDENCY_OPT);
|
||||
for (String nextDependencyString : dependencies) {
|
||||
int colonIdx = nextDependencyString.indexOf(":");
|
||||
if (colonIdx == -1) {
|
||||
throw new ParseException("Invalid dependency spec: " + nextDependencyString);
|
||||
}
|
||||
String depName = nextDependencyString.substring(0, colonIdx);
|
||||
String depVersion = nextDependencyString.substring(colonIdx+1);
|
||||
manifestGenerator.dependency(depName, depVersion);
|
||||
}
|
||||
|
||||
myWorkDirectory = Files.createTempDir();
|
||||
myPackage = NpmPackage.empty(manifestGenerator);
|
||||
|
||||
ourLog.info("Using temporary directory: {}", myWorkDirectory.getAbsolutePath());
|
||||
|
||||
// Package
|
||||
String[] packageValues = theCommandLine.getOptionValues(INCLUDE_PACKAGE_OPT);
|
||||
String folder = "package";
|
||||
addFiles(packageValues, folder);
|
||||
|
||||
// Example
|
||||
packageValues = theCommandLine.getOptionValues(INCLUDE_EXAMPLE_OPT);
|
||||
folder = "example";
|
||||
addFiles(packageValues, folder);
|
||||
|
||||
|
||||
String targetDirectory = theCommandLine.getOptionValue(TARGET_DIRECTORY_OPT);
|
||||
if (isBlank(targetDirectory)) {
|
||||
targetDirectory = ".";
|
||||
}
|
||||
File targetFile = new File(new File(targetDirectory), myPackageName + "-" + myPackageVersion + ".tgz");
|
||||
|
||||
ourLog.info("Writing NPM file: {}", targetFile.toString());
|
||||
|
||||
try (FileOutputStream os = new FileOutputStream(targetFile, false)) {
|
||||
myPackage.save(os);
|
||||
} catch (IOException e) {
|
||||
throw new ExecutionException("Failed to write file " + targetFile, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void addFiles(String[] thePackageValues, String theFolder) throws ParseException, ExecutionException {
|
||||
if (thePackageValues != null) {
|
||||
for (String nextPackageValue : thePackageValues) {
|
||||
if (!nextPackageValue.contains("/")) {
|
||||
throw new ParseException("Invalid file expression: " + nextPackageValue);
|
||||
}
|
||||
|
||||
int endIndex = nextPackageValue.lastIndexOf("/");
|
||||
String path = nextPackageValue.substring(0, endIndex);
|
||||
String expression = nextPackageValue.substring(endIndex + 1);
|
||||
IOFileFilter filter = new WildcardFileFilter(expression);
|
||||
Collection<File> files = FileUtils.listFiles(new File(path), filter, FalseFileFilter.INSTANCE);
|
||||
|
||||
for (File next : files) {
|
||||
|
||||
byte[] contentBytes;
|
||||
String type;
|
||||
try {
|
||||
String contents = IOUtils.toString(new FileInputStream(next), StandardCharsets.UTF_8);
|
||||
contentBytes = contents.getBytes(StandardCharsets.UTF_8);
|
||||
type = EncodingEnum.detectEncoding(contents).newParser(myFhirCtx).parseResource(contents).fhirType();
|
||||
} catch (IOException | DataFormatException e) {
|
||||
throw new ExecutionException("Failed to load/parse file: " + next.getName(), e);
|
||||
}
|
||||
|
||||
ourLog.info("Adding {} file of type {}: {}", theFolder, type, next.getName());
|
||||
myPackage.addFile(theFolder, next.getName(), contentBytes, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
package ca.uhn.fhir.cli;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.test.BaseTest;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.io.Files;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.io.filefilter.TrueFileFilter;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4.model.StructureDefinition;
|
||||
import org.hl7.fhir.r4.model.ValueSet;
|
||||
import org.hl7.fhir.utilities.cache.NpmPackage;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.rauschig.jarchivelib.Archiver;
|
||||
import org.rauschig.jarchivelib.ArchiverFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class CreatePackageCommandTest extends BaseTest {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(CreatePackageCommandTest.class);
|
||||
private File myWorkDirectory;
|
||||
private FhirContext myContext = FhirContext.forR4();
|
||||
private File myTargetDirectory;
|
||||
private File myExtractDirectory;
|
||||
|
||||
static {
|
||||
System.setProperty("test", "true");
|
||||
}
|
||||
|
||||
@Before
|
||||
public void start() {
|
||||
myWorkDirectory = Files.createTempDir();
|
||||
myTargetDirectory = Files.createTempDir();
|
||||
myExtractDirectory = Files.createTempDir();
|
||||
}
|
||||
|
||||
@After
|
||||
public void stop() {
|
||||
try {
|
||||
FileUtils.deleteDirectory(myWorkDirectory);
|
||||
FileUtils.deleteDirectory(myTargetDirectory);
|
||||
FileUtils.deleteDirectory(myExtractDirectory);
|
||||
} catch (IOException e) {
|
||||
throw new InternalErrorException("Failed to delete temporary directory \"" + myWorkDirectory.getAbsolutePath() + "\"", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatePackage() throws IOException {
|
||||
|
||||
StructureDefinition sd = new StructureDefinition();
|
||||
sd.setUrl("http://foo/1");
|
||||
writeFile(sd, "foo1.json");
|
||||
|
||||
ValueSet vs = new ValueSet();
|
||||
vs.setUrl("http://foo/2");
|
||||
writeFile(vs, "foo2.json");
|
||||
|
||||
App.main(new String[]{
|
||||
"create-package",
|
||||
"--fhir-version", "R4",
|
||||
"--name", "com.example.ig",
|
||||
"--version", "1.0.1",
|
||||
"--include-package", myWorkDirectory.getAbsolutePath() + "/*.json",
|
||||
"--target-directory", myTargetDirectory.getAbsolutePath(),
|
||||
"--dependency", "hl7.fhir.core:4.0.1",
|
||||
"--dependency", "foo.bar:1.2.3"
|
||||
});
|
||||
|
||||
Archiver archiver = ArchiverFactory.createArchiver("tar", "gz");
|
||||
|
||||
File igArchive = new File(myTargetDirectory, "com.example.ig-1.0.1.tgz");
|
||||
archiver.extract(igArchive, myExtractDirectory);
|
||||
|
||||
List<String> allFiles = FileUtils.listFiles(myExtractDirectory, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE)
|
||||
.stream()
|
||||
.map(t -> t.getPath())
|
||||
.sorted()
|
||||
.collect(Collectors.toList());
|
||||
ourLog.info("Archive contains files:\n * {}", allFiles.stream().collect(Collectors.joining("\n * ")));
|
||||
|
||||
// Verify package.json
|
||||
String packageJsonContents = IOUtils.toString(new FileInputStream(new File(myExtractDirectory, "package/package.json")), Charsets.UTF_8);
|
||||
ourLog.info("Package.json:\n{}", packageJsonContents);
|
||||
|
||||
String expectedPackageJson = "{\n" +
|
||||
" \"name\": \"com.example.ig\",\n" +
|
||||
" \"version\": \"1.0.1\",\n" +
|
||||
" \"dependencies\": {\n" +
|
||||
" \"hl7.fhir.core\": \"4.0.1\",\n" +
|
||||
" \"foo.bar\": \"1.2.3\"\n" +
|
||||
" }\n" +
|
||||
"}";
|
||||
assertEquals(expectedPackageJson, packageJsonContents);
|
||||
|
||||
// Try parsing the module again to make sure we can
|
||||
NpmPackage loadedPackage = NpmPackage.fromPackage(new FileInputStream(igArchive));
|
||||
assertEquals("com.example.ig", loadedPackage.name());
|
||||
|
||||
}
|
||||
|
||||
public void writeFile(IBaseResource theResource, String theFileName) throws IOException {
|
||||
try (FileWriter w = new FileWriter(new File(myWorkDirectory, theFileName), false)) {
|
||||
myContext.newJsonParser().encodeResourceToWriter(theResource, w);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
type: add
|
||||
issue: 1911
|
||||
title: "Support for FHIR NPM Packages has been added to the JPA server. This new functionality allows packages to
|
||||
be installed to special tables within the FHIR JPA schema, and conformance resources used for validation."
|
|
@ -163,7 +163,7 @@ public interface IFhirResourceDao<T extends IBaseResource> extends IDao {
|
|||
/**
|
||||
* Read a resource by its internal PID
|
||||
*/
|
||||
IBaseResource readByPid(ResourcePersistentId thePid);
|
||||
T readByPid(ResourcePersistentId thePid);
|
||||
|
||||
/**
|
||||
* @param theId
|
||||
|
|
|
@ -182,6 +182,10 @@
|
|||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.swagger</groupId>
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.helger</groupId>
|
||||
|
|
|
@ -21,7 +21,11 @@ import ca.uhn.fhir.jpa.entity.Search;
|
|||
import ca.uhn.fhir.jpa.graphql.JpaStorageServices;
|
||||
import ca.uhn.fhir.jpa.interceptor.JpaConsentContextServices;
|
||||
import ca.uhn.fhir.jpa.model.sched.ISchedulerService;
|
||||
import ca.uhn.fhir.jpa.packages.IgInstallerSvc;
|
||||
import ca.uhn.fhir.jpa.packages.IHapiPackageCacheManager;
|
||||
import ca.uhn.fhir.jpa.packages.IPackageInstallerSvc;
|
||||
import ca.uhn.fhir.jpa.packages.JpaPackageCache;
|
||||
import ca.uhn.fhir.jpa.packages.PackageInstallerSvcImpl;
|
||||
import ca.uhn.fhir.jpa.packages.NpmJpaValidationSupport;
|
||||
import ca.uhn.fhir.jpa.partition.IPartitionLookupSvc;
|
||||
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc;
|
||||
import ca.uhn.fhir.jpa.partition.PartitionLookupSvcImpl;
|
||||
|
@ -53,6 +57,7 @@ import ca.uhn.fhir.rest.server.interceptor.consent.IConsentContextServices;
|
|||
import ca.uhn.fhir.rest.server.interceptor.partition.RequestTenantPartitionInterceptor;
|
||||
import org.hibernate.jpa.HibernatePersistenceProvider;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.utilities.cache.FilesystemPackageCacheManager;
|
||||
import org.hl7.fhir.utilities.graphql.IGraphQLStorageServices;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
@ -120,10 +125,9 @@ public abstract class BaseConfig {
|
|||
public static final String PERSISTED_JPA_BUNDLE_PROVIDER = "PersistedJpaBundleProvider";
|
||||
public static final String PERSISTED_JPA_BUNDLE_PROVIDER_BY_SEARCH = "PersistedJpaBundleProvider_BySearch";
|
||||
public static final String PERSISTED_JPA_SEARCH_FIRST_PAGE_BUNDLE_PROVIDER = "PersistedJpaSearchFirstPageBundleProvider";
|
||||
private static final String HAPI_DEFAULT_SCHEDULER_GROUP = "HAPI";
|
||||
public static final String SEARCH_BUILDER = "SearchBuilder";
|
||||
public static final String HISTORY_BUILDER = "HistoryBuilder";
|
||||
|
||||
private static final String HAPI_DEFAULT_SCHEDULER_GROUP = "HAPI";
|
||||
@Autowired
|
||||
protected Environment myEnv;
|
||||
@Autowired
|
||||
|
@ -195,6 +199,20 @@ public abstract class BaseConfig {
|
|||
return new DaoResourceLinkResolver();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IHapiPackageCacheManager packageCacheManager() {
|
||||
JpaPackageCache retVal = new JpaPackageCache();
|
||||
retVal.getPackageServers().clear();
|
||||
retVal.getPackageServers().add(FilesystemPackageCacheManager.PRIMARY_SERVER);
|
||||
retVal.getPackageServers().add(FilesystemPackageCacheManager.SECONDARY_SERVER);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public NpmJpaValidationSupport npmJpaValidationSupport() {
|
||||
return new NpmJpaValidationSupport();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ISearchCacheSvc searchCacheSvc() {
|
||||
return new DatabaseSearchCacheSvcImpl();
|
||||
|
@ -259,7 +277,9 @@ public abstract class BaseConfig {
|
|||
}
|
||||
|
||||
@Bean
|
||||
public IgInstallerSvc igInstallerSvc() { return new IgInstallerSvc(); }
|
||||
public IPackageInstallerSvc npmInstallerSvc() {
|
||||
return new PackageInstallerSvcImpl();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IConsentContextServices consentContextServices() {
|
||||
|
|
|
@ -932,7 +932,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
}
|
||||
|
||||
@Override
|
||||
public IBaseResource readByPid(ResourcePersistentId thePid) {
|
||||
public T readByPid(ResourcePersistentId thePid) {
|
||||
StopWatch w = new StopWatch();
|
||||
|
||||
Optional<ResourceTable> entity = myResourceTableDao.findById(thePid.getIdAsLong());
|
||||
|
|
|
@ -99,6 +99,7 @@ import java.util.Map;
|
|||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import static ca.uhn.fhir.util.StringUtil.toUtf8String;
|
||||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
@ -768,14 +769,14 @@ public abstract class BaseTransactionProcessor {
|
|||
String matchUrl = toMatchUrl(nextReqEntry);
|
||||
matchUrl = performIdSubstitutionsInMatchUrl(theIdSubstitutions, matchUrl);
|
||||
String patchBody = null;
|
||||
String contentType = null;
|
||||
String contentType;
|
||||
IBaseParameters patchBodyParameters = null;
|
||||
PatchTypeEnum patchType = null;
|
||||
|
||||
if (res instanceof IBaseBinary) {
|
||||
IBaseBinary binary = (IBaseBinary) res;
|
||||
if (binary.getContent() != null && binary.getContent().length > 0) {
|
||||
patchBody = new String(binary.getContent(), Charsets.UTF_8);
|
||||
patchBody = toUtf8String(binary.getContent());
|
||||
}
|
||||
contentType = binary.getContentType();
|
||||
patchType = PatchTypeEnum.forContentTypeOrThrowInvalidRequestException(myContext, contentType);
|
||||
|
|
|
@ -1377,7 +1377,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
return ResourcePersistentId.fromLongList(query.getResultList());
|
||||
}
|
||||
|
||||
static Predicate[] toPredicateArray(List<Predicate> thePredicates) {
|
||||
public static Predicate[] toPredicateArray(List<Predicate> thePredicates) {
|
||||
return thePredicates.toArray(new Predicate[0]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
package ca.uhn.fhir.jpa.dao.data;
|
||||
|
||||
import ca.uhn.fhir.jpa.model.entity.NpmPackageEntity;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 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%
|
||||
*/
|
||||
|
||||
public interface INpmPackageDao extends JpaRepository<NpmPackageEntity, Long> {
|
||||
|
||||
@Query("SELECT p FROM NpmPackageEntity p WHERE p.myPackageId = :id")
|
||||
Optional<NpmPackageEntity> findByPackageId(@Param("id") String thePackageId);
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package ca.uhn.fhir.jpa.dao.data;
|
||||
|
||||
import ca.uhn.fhir.jpa.model.entity.NpmPackageVersionEntity;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 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%
|
||||
*/
|
||||
|
||||
public interface INpmPackageVersionDao extends JpaRepository<NpmPackageVersionEntity, Long> {
|
||||
|
||||
@Query("SELECT p FROM NpmPackageVersionEntity p WHERE p.myPackageId = :id")
|
||||
Collection<NpmPackageVersionEntity> findByPackageId(@Param("id") String thePackageId);
|
||||
|
||||
@Query("SELECT p FROM NpmPackageVersionEntity p WHERE p.myPackageId = :id AND p.myVersionId = :version")
|
||||
Optional<NpmPackageVersionEntity> findByPackageIdAndVersion(@Param("id") String thePackageId, @Param("version") String thePackageVersion);
|
||||
|
||||
/**
|
||||
* Uses a "like" expression on the version ID
|
||||
*/
|
||||
@Query("SELECT p.myVersionId FROM NpmPackageVersionEntity p WHERE p.myPackageId = :id AND p.myVersionId like :version")
|
||||
List<String> findVersionIdsByPackageIdAndLikeVersion(@Param("id") String theId, @Param("version") String thePartialVersionString);
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package ca.uhn.fhir.jpa.dao.data;
|
||||
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.jpa.model.entity.NpmPackageVersionResourceEntity;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 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%
|
||||
*/
|
||||
|
||||
public interface INpmPackageVersionResourceDao extends JpaRepository<NpmPackageVersionResourceEntity, Long> {
|
||||
|
||||
@Query("SELECT e FROM NpmPackageVersionResourceEntity e WHERE e.myCanonicalUrl = :url AND e.myFhirVersion = :fhirVersion AND e.myPackageVersion.myCurrentVersion = true")
|
||||
Slice<NpmPackageVersionResourceEntity> findCurrentVersionByCanonicalUrl(Pageable thePage, @Param("fhirVersion") FhirVersionEnum theFhirVersion, @Param("url") String theCanonicalUrl);
|
||||
|
||||
@Query("SELECT e FROM NpmPackageVersionResourceEntity e WHERE e.myCanonicalUrl = :url AND e.myCanonicalVersion = :version AND e.myFhirVersion = :fhirVersion AND e.myPackageVersion.myCurrentVersion = true")
|
||||
Slice<NpmPackageVersionResourceEntity> findCurrentVersionByCanonicalUrlAndVersion(Pageable theOf, @Param("fhirVersion") FhirVersionEnum theFhirVersion, @Param("url") String theCanonicalUrl, @Param("version") String theCanonicalVersion);
|
||||
}
|
|
@ -41,6 +41,9 @@ import ca.uhn.fhir.jpa.entity.TermValueSet;
|
|||
import ca.uhn.fhir.jpa.entity.TermValueSetConcept;
|
||||
import ca.uhn.fhir.jpa.entity.TermValueSetConceptDesignation;
|
||||
import ca.uhn.fhir.jpa.model.entity.ForcedId;
|
||||
import ca.uhn.fhir.jpa.model.entity.NpmPackageEntity;
|
||||
import ca.uhn.fhir.jpa.model.entity.NpmPackageVersionEntity;
|
||||
import ca.uhn.fhir.jpa.model.entity.NpmPackageVersionResourceEntity;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryProvenanceEntity;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTag;
|
||||
|
@ -114,6 +117,9 @@ public class ExpungeEverythingService {
|
|||
counter.addAndGet(doExpungeEverythingQuery("UPDATE " + TermCodeSystem.class.getSimpleName() + " d SET d.myCurrentVersion = null"));
|
||||
return null;
|
||||
});
|
||||
counter.addAndGet(expungeEverythingByType(NpmPackageVersionResourceEntity.class));
|
||||
counter.addAndGet(expungeEverythingByType(NpmPackageVersionEntity.class));
|
||||
counter.addAndGet(expungeEverythingByType(NpmPackageEntity.class));
|
||||
counter.addAndGet(expungeEverythingByType(SearchParamPresent.class));
|
||||
counter.addAndGet(expungeEverythingByType(ForcedId.class));
|
||||
counter.addAndGet(expungeEverythingByType(ResourceIndexedSearchParamDate.class));
|
||||
|
|
|
@ -30,7 +30,7 @@ import ca.uhn.fhir.rest.param.StringParam;
|
|||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
|
||||
import ca.uhn.fhir.util.StringNormalizer;
|
||||
import ca.uhn.fhir.util.StringUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
@ -131,7 +131,7 @@ class PredicateBuilderString extends BasePredicateBuilder implements IPredicateB
|
|||
}
|
||||
|
||||
if (myDontUseHashesForSearch) {
|
||||
String likeExpression = StringNormalizer.normalizeStringForSearchIndexing(rawSearchTerm);
|
||||
String likeExpression = StringUtil.normalizeStringForSearchIndexing(rawSearchTerm);
|
||||
if (myDaoConfig.isAllowContainsSearches()) {
|
||||
if (theParameter instanceof StringParam) {
|
||||
if (((StringParam) theParameter).isContains()) {
|
||||
|
@ -161,7 +161,7 @@ class PredicateBuilderString extends BasePredicateBuilder implements IPredicateB
|
|||
return theBuilder.equal(theFrom.get("myHashExact").as(Long.class), hash);
|
||||
} else {
|
||||
// Normalized Match
|
||||
String normalizedString = StringNormalizer.normalizeStringForSearchIndexing(rawSearchTerm);
|
||||
String normalizedString = StringUtil.normalizeStringForSearchIndexing(rawSearchTerm);
|
||||
String likeExpression;
|
||||
if ((theParameter instanceof StringParam) &&
|
||||
(((((StringParam) theParameter).isContains()) &&
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
package ca.uhn.fhir.jpa.packages;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 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.ResourceNotFoundException;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.utilities.cache.IPackageCacheManager;
|
||||
import org.hl7.fhir.utilities.cache.NpmPackage;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
|
||||
public interface IHapiPackageCacheManager extends IPackageCacheManager {
|
||||
|
||||
NpmPackage installPackage(PackageInstallationSpec theInstallationSpec) throws IOException;
|
||||
|
||||
IBaseResource loadPackageAssetByUrl(FhirVersionEnum theFhirVersion, String theCanonicalUrl);
|
||||
|
||||
NpmPackageMetadataJson loadPackageMetadata(String thePackageId) throws ResourceNotFoundException;
|
||||
|
||||
PackageContents loadPackageContents(String thePackageId, String theVersion);
|
||||
|
||||
NpmPackageSearchResultJson search(PackageSearchSpec thePackageSearchSpec);
|
||||
|
||||
PackageDeleteOutcomeJson uninstallPackage(String thePackageId, String theVersion);
|
||||
|
||||
|
||||
class PackageContents {
|
||||
|
||||
private byte[] myBytes;
|
||||
private String myPackageId;
|
||||
private String myVersion;
|
||||
private Date myLastModified;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public PackageContents() {
|
||||
super();
|
||||
}
|
||||
|
||||
public byte[] getBytes() {
|
||||
return myBytes;
|
||||
}
|
||||
|
||||
public PackageContents setBytes(byte[] theBytes) {
|
||||
myBytes = theBytes;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getPackageId() {
|
||||
return myPackageId;
|
||||
}
|
||||
|
||||
public PackageContents setPackageId(String thePackageId) {
|
||||
myPackageId = thePackageId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return myVersion;
|
||||
}
|
||||
|
||||
public PackageContents setVersion(String theVersion) {
|
||||
myVersion = theVersion;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Date getLastModified() {
|
||||
return myLastModified;
|
||||
}
|
||||
|
||||
public PackageContents setLastModified(Date theLastModified) {
|
||||
myLastModified = theLastModified;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package ca.uhn.fhir.jpa.packages;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 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%
|
||||
*/
|
||||
|
||||
public interface IPackageInstallerSvc {
|
||||
|
||||
PackageInstallOutcomeJson install(PackageInstallationSpec theSpec);
|
||||
|
||||
}
|
|
@ -0,0 +1,653 @@
|
|||
package ca.uhn.fhir.jpa.packages;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 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.BaseRuntimeChildDefinition;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.api.model.ExpungeOptions;
|
||||
import ca.uhn.fhir.jpa.dao.data.INpmPackageDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.INpmPackageVersionDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.INpmPackageVersionResourceDao;
|
||||
import ca.uhn.fhir.jpa.model.entity.NpmPackageEntity;
|
||||
import ca.uhn.fhir.jpa.model.entity.NpmPackageVersionEntity;
|
||||
import ca.uhn.fhir.jpa.model.entity.NpmPackageVersionResourceEntity;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.util.BinaryUtil;
|
||||
import ca.uhn.fhir.util.ClasspathUtil;
|
||||
import ca.uhn.fhir.util.ResourceUtil;
|
||||
import ca.uhn.fhir.util.StringUtil;
|
||||
import org.apache.commons.collections4.comparators.ReverseComparator;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.conn.HttpClientConnectionManager;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.instance.model.api.IBaseBinary;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
import org.hl7.fhir.utilities.cache.BasePackageCacheManager;
|
||||
import org.hl7.fhir.utilities.cache.NpmPackage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
import javax.persistence.TypedQuery;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Join;
|
||||
import javax.persistence.criteria.JoinType;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
import javax.transaction.Transactional;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static ca.uhn.fhir.jpa.dao.SearchBuilder.toPredicateArray;
|
||||
import static ca.uhn.fhir.util.StringUtil.toUtf8String;
|
||||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public class JpaPackageCache extends BasePackageCacheManager implements IHapiPackageCacheManager {
|
||||
|
||||
public static final String UTF8_BOM = "\uFEFF";
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(JpaPackageCache.class);
|
||||
private final Map<FhirVersionEnum, FhirContext> myVersionToContext = Collections.synchronizedMap(new HashMap<>());
|
||||
@PersistenceContext
|
||||
protected EntityManager myEntityManager;
|
||||
@Autowired
|
||||
private INpmPackageDao myPackageDao;
|
||||
@Autowired
|
||||
private INpmPackageVersionDao myPackageVersionDao;
|
||||
@Autowired
|
||||
private INpmPackageVersionResourceDao myPackageVersionResourceDao;
|
||||
@Autowired
|
||||
private DaoRegistry myDaoRegistry;
|
||||
@Autowired
|
||||
private FhirContext myCtx;
|
||||
@Autowired
|
||||
private PlatformTransactionManager myTxManager;
|
||||
|
||||
@Override
|
||||
public NpmPackage loadPackageFromCacheOnly(String theId, @Nullable String theVersion) {
|
||||
Optional<NpmPackageVersionEntity> packageVersion = loadPackageVersionEntity(theId, theVersion);
|
||||
if (!packageVersion.isPresent() && theVersion.endsWith(".x")) {
|
||||
String lookupVersion = theVersion;
|
||||
do {
|
||||
lookupVersion = lookupVersion.substring(0, lookupVersion.length() - 2);
|
||||
} while (lookupVersion.endsWith(".x"));
|
||||
|
||||
List<String> candidateVersionIds = myPackageVersionDao.findVersionIdsByPackageIdAndLikeVersion(theId, lookupVersion + ".%");
|
||||
if (candidateVersionIds.size() > 0) {
|
||||
candidateVersionIds.sort(PackageVersionComparator.INSTANCE);
|
||||
packageVersion = loadPackageVersionEntity(theId, candidateVersionIds.get(candidateVersionIds.size() - 1));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return packageVersion.map(t -> loadPackage(t)).orElse(null);
|
||||
}
|
||||
|
||||
private Optional<NpmPackageVersionEntity> loadPackageVersionEntity(String theId, @Nullable String theVersion) {
|
||||
Validate.notBlank(theId, "theId must be populated");
|
||||
|
||||
Optional<NpmPackageVersionEntity> packageVersion = Optional.empty();
|
||||
if (isNotBlank(theVersion) && !"latest".equals(theVersion)) {
|
||||
packageVersion = myPackageVersionDao.findByPackageIdAndVersion(theId, theVersion);
|
||||
} else {
|
||||
Optional<NpmPackageEntity> pkg = myPackageDao.findByPackageId(theId);
|
||||
if (pkg.isPresent()) {
|
||||
packageVersion = myPackageVersionDao.findByPackageIdAndVersion(theId, pkg.get().getCurrentVersionId());
|
||||
}
|
||||
}
|
||||
return packageVersion;
|
||||
}
|
||||
|
||||
private NpmPackage loadPackage(NpmPackageVersionEntity thePackageVersion) {
|
||||
PackageContents content = loadPackageContents(thePackageVersion);
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(content.getBytes());
|
||||
try {
|
||||
return NpmPackage.fromPackage(inputStream);
|
||||
} catch (IOException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private IHapiPackageCacheManager.PackageContents loadPackageContents(NpmPackageVersionEntity thePackageVersion) {
|
||||
IFhirResourceDao<? extends IBaseBinary> binaryDao = getBinaryDao();
|
||||
IBaseBinary binary = binaryDao.readByPid(new ResourcePersistentId(thePackageVersion.getPackageBinary().getId()));
|
||||
|
||||
PackageContents retVal = new PackageContents()
|
||||
.setBytes(binary.getContent())
|
||||
.setPackageId(thePackageVersion.getPackageId())
|
||||
.setVersion(thePackageVersion.getVersionId())
|
||||
.setLastModified(thePackageVersion.getUpdatedTime());
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private IFhirResourceDao<IBaseBinary> getBinaryDao() {
|
||||
return myDaoRegistry.getResourceDao("Binary");
|
||||
}
|
||||
|
||||
@Override
|
||||
public NpmPackage addPackageToCache(String thePackageId, String thePackageVersionId, InputStream thePackageTgzInputStream, String theSourceDesc) throws IOException {
|
||||
Validate.notBlank(thePackageId, "thePackageId must not be null");
|
||||
Validate.notBlank(thePackageVersionId, "thePackageVersionId must not be null");
|
||||
Validate.notNull(thePackageTgzInputStream, "thePackageTgzInputStream must not be null");
|
||||
|
||||
byte[] bytes = IOUtils.toByteArray(thePackageTgzInputStream);
|
||||
|
||||
NpmPackage npmPackage = NpmPackage.fromPackage(new ByteArrayInputStream(bytes));
|
||||
if (!npmPackage.id().equals(thePackageId)) {
|
||||
throw new InvalidRequestException("Package ID " + npmPackage.id() + " doesn't match expected: " + thePackageId);
|
||||
}
|
||||
if (!PackageVersionComparator.isEquivalent(thePackageVersionId, npmPackage.version())) {
|
||||
throw new InvalidRequestException("Package ID " + npmPackage.version() + " doesn't match expected: " + thePackageVersionId);
|
||||
}
|
||||
|
||||
String packageVersionId = npmPackage.version();
|
||||
FhirVersionEnum fhirVersion = FhirVersionEnum.forVersionString(npmPackage.fhirVersion());
|
||||
if (fhirVersion == null) {
|
||||
throw new InvalidRequestException("Unknown FHIR version: " + npmPackage.fhirVersion());
|
||||
}
|
||||
FhirContext packageContext = getFhirContext(fhirVersion);
|
||||
|
||||
IBaseBinary binary = createPackageBinary(bytes);
|
||||
|
||||
return newTxTemplate().execute(tx -> {
|
||||
|
||||
ResourceTable persistedPackage = (ResourceTable) getBinaryDao().create(binary).getEntity();
|
||||
NpmPackageEntity pkg = myPackageDao.findByPackageId(thePackageId).orElseGet(() -> createPackage(npmPackage));
|
||||
NpmPackageVersionEntity packageVersion = myPackageVersionDao.findByPackageIdAndVersion(thePackageId, packageVersionId).orElse(null);
|
||||
if (packageVersion != null) {
|
||||
NpmPackage existingPackage = loadPackageFromCacheOnly(packageVersion.getPackageId(), packageVersion.getVersionId());
|
||||
String msg = "Package version already exists in local storage, no action taken: " + thePackageId + "#" + packageVersionId;
|
||||
getProcessingMessages(existingPackage).add(msg);
|
||||
ourLog.info(msg);
|
||||
return existingPackage;
|
||||
}
|
||||
|
||||
boolean currentVersion = updateCurrentVersionFlagForAllPackagesBasedOnNewIncomingVersion(thePackageId, packageVersionId);
|
||||
if (currentVersion) {
|
||||
getProcessingMessages(npmPackage).add("Marking package " + thePackageId + "#" + thePackageVersionId + " as current version");
|
||||
pkg.setCurrentVersionId(packageVersionId);
|
||||
pkg.setDescription(npmPackage.description());
|
||||
myPackageDao.save(pkg);
|
||||
} else {
|
||||
getProcessingMessages(npmPackage).add("Package " + thePackageId + "#" + thePackageVersionId + " is not the newest version");
|
||||
}
|
||||
|
||||
packageVersion = new NpmPackageVersionEntity();
|
||||
packageVersion.setPackageId(thePackageId);
|
||||
packageVersion.setVersionId(packageVersionId);
|
||||
packageVersion.setPackage(pkg);
|
||||
packageVersion.setPackageBinary(persistedPackage);
|
||||
packageVersion.setSavedTime(new Date());
|
||||
packageVersion.setDescription(npmPackage.description());
|
||||
packageVersion.setFhirVersionId(npmPackage.fhirVersion());
|
||||
packageVersion.setFhirVersion(fhirVersion);
|
||||
packageVersion.setCurrentVersion(currentVersion);
|
||||
packageVersion.setPackageSizeBytes(bytes.length);
|
||||
packageVersion.setName(npmPackage.name());
|
||||
packageVersion = myPackageVersionDao.save(packageVersion);
|
||||
|
||||
String dirName = "package";
|
||||
NpmPackage.NpmPackageFolder packageFolder = npmPackage.getFolders().get(dirName);
|
||||
for (Map.Entry<String, List<String>> nextTypeToFiles : packageFolder.getTypes().entrySet()) {
|
||||
String nextType = nextTypeToFiles.getKey();
|
||||
for (String nextFile : nextTypeToFiles.getValue()) {
|
||||
|
||||
byte[] contents;
|
||||
String contentsString;
|
||||
try {
|
||||
contents = packageFolder.fetchFile(nextFile);
|
||||
contentsString = toUtf8String(contents);
|
||||
} catch (IOException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
|
||||
IBaseResource resource;
|
||||
if (nextFile.toLowerCase().endsWith(".xml")) {
|
||||
resource = packageContext.newXmlParser().parseResource(contentsString);
|
||||
} else if (nextFile.toLowerCase().endsWith(".json")) {
|
||||
resource = packageContext.newJsonParser().parseResource(contentsString);
|
||||
} else {
|
||||
getProcessingMessages(npmPackage).add("Not indexing file: " + nextFile);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Re-encode the resource as JSON with the narrative removed in order to reduce the footprint.
|
||||
* This is useful since we'll be loading these resources back and hopefully keeping lots of
|
||||
* them in memory in order to speed up validation activities.
|
||||
*/
|
||||
String contentType = Constants.CT_FHIR_JSON_NEW;
|
||||
ResourceUtil.removeNarrative(packageContext, resource);
|
||||
byte[] minimizedContents = packageContext.newJsonParser().encodeResourceToString(resource).getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
IBaseBinary resourceBinary = createPackageResourceBinary(nextFile, minimizedContents, contentType);
|
||||
ResourceTable persistedResource = (ResourceTable) getBinaryDao().create(resourceBinary).getEntity();
|
||||
|
||||
NpmPackageVersionResourceEntity resourceEntity = new NpmPackageVersionResourceEntity();
|
||||
resourceEntity.setPackageVersion(packageVersion);
|
||||
resourceEntity.setResourceBinary(persistedResource);
|
||||
resourceEntity.setDirectory(dirName);
|
||||
resourceEntity.setFhirVersionId(npmPackage.fhirVersion());
|
||||
resourceEntity.setFhirVersion(fhirVersion);
|
||||
resourceEntity.setFilename(nextFile);
|
||||
resourceEntity.setResourceType(nextType);
|
||||
resourceEntity.setResSizeBytes(contents.length);
|
||||
BaseRuntimeChildDefinition urlChild = packageContext.getResourceDefinition(nextType).getChildByName("url");
|
||||
BaseRuntimeChildDefinition versionChild = packageContext.getResourceDefinition(nextType).getChildByName("version");
|
||||
String url = null;
|
||||
String version = null;
|
||||
if (urlChild != null) {
|
||||
url = urlChild.getAccessor().getFirstValueOrNull(resource).map(t -> ((IPrimitiveType<?>) t).getValueAsString()).orElse(null);
|
||||
resourceEntity.setCanonicalUrl(url);
|
||||
version = versionChild.getAccessor().getFirstValueOrNull(resource).map(t -> ((IPrimitiveType<?>) t).getValueAsString()).orElse(null);
|
||||
resourceEntity.setCanonicalVersion(version);
|
||||
}
|
||||
myPackageVersionResourceDao.save(resourceEntity);
|
||||
|
||||
String msg = "Indexing Resource[" + dirName + '/' + nextFile + "] with URL: " + defaultString(url) + "|" + defaultString(version);
|
||||
getProcessingMessages(npmPackage).add(msg);
|
||||
ourLog.info("Package[{}#{}] " + msg, thePackageId, packageVersionId);
|
||||
}
|
||||
}
|
||||
|
||||
getProcessingMessages(npmPackage).add("Successfully added package " + npmPackage.id() + "#" + npmPackage.version() + " to registry");
|
||||
|
||||
return npmPackage;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private boolean updateCurrentVersionFlagForAllPackagesBasedOnNewIncomingVersion(String thePackageId, String thePackageVersion) {
|
||||
Collection<NpmPackageVersionEntity> existingVersions = myPackageVersionDao.findByPackageId(thePackageId);
|
||||
boolean retVal = true;
|
||||
|
||||
for (NpmPackageVersionEntity next : existingVersions) {
|
||||
int cmp = PackageVersionComparator.INSTANCE.compare(next.getVersionId(), thePackageVersion);
|
||||
assert cmp != 0;
|
||||
if (cmp < 0) {
|
||||
if (next.isCurrentVersion()) {
|
||||
next.setCurrentVersion(false);
|
||||
myPackageVersionDao.save(next);
|
||||
}
|
||||
} else {
|
||||
retVal = false;
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public FhirContext getFhirContext(FhirVersionEnum theFhirVersion) {
|
||||
return myVersionToContext.computeIfAbsent(theFhirVersion, v -> new FhirContext(v));
|
||||
}
|
||||
|
||||
private IBaseBinary createPackageBinary(byte[] theBytes) {
|
||||
IBaseBinary binary = BinaryUtil.newBinary(myCtx);
|
||||
BinaryUtil.setData(myCtx, binary, theBytes, Constants.CT_APPLICATION_GZIP);
|
||||
return binary;
|
||||
}
|
||||
|
||||
private IBaseBinary createPackageResourceBinary(String theFileName, byte[] theBytes, String theContentType) {
|
||||
IBaseBinary binary = BinaryUtil.newBinary(myCtx);
|
||||
BinaryUtil.setData(myCtx, binary, theBytes, theContentType);
|
||||
return binary;
|
||||
}
|
||||
|
||||
private NpmPackageEntity createPackage(NpmPackage theNpmPackage) {
|
||||
NpmPackageEntity entity = new NpmPackageEntity();
|
||||
entity.setPackageId(theNpmPackage.id());
|
||||
entity.setCurrentVersionId(theNpmPackage.version());
|
||||
return myPackageDao.save(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NpmPackage loadPackage(String thePackageId, String thePackageVersion) throws FHIRException, IOException {
|
||||
NpmPackage cachedPackage = loadPackageFromCacheOnly(thePackageId, thePackageVersion);
|
||||
if (cachedPackage != null) {
|
||||
return cachedPackage;
|
||||
}
|
||||
|
||||
InputStreamWithSrc pkg = super.loadFromPackageServer(thePackageId, thePackageVersion);
|
||||
if (pkg == null) {
|
||||
throw new ResourceNotFoundException("Unable to locate package " + thePackageId + "#" + thePackageVersion);
|
||||
}
|
||||
|
||||
try {
|
||||
NpmPackage retVal = addPackageToCache(thePackageId, thePackageVersion == null ? pkg.version : thePackageVersion, pkg.stream, pkg.url);
|
||||
getProcessingMessages(retVal).add(0, "Package fetched from server at: " + pkg.url);
|
||||
return retVal;
|
||||
} finally {
|
||||
pkg.stream.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private TransactionTemplate newTxTemplate() {
|
||||
return new TransactionTemplate(myTxManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NpmPackage installPackage(PackageInstallationSpec theInstallationSpec) throws IOException {
|
||||
Validate.notBlank(theInstallationSpec.getName(), "thePackageId must not be blank");
|
||||
Validate.notBlank(theInstallationSpec.getVersion(), "thePackageVersion must not be blank");
|
||||
|
||||
if (isNotBlank(theInstallationSpec.getPackageUrl())) {
|
||||
byte[] contents = loadPackageUrlContents(theInstallationSpec.getPackageUrl());
|
||||
theInstallationSpec.setPackageContents(contents);
|
||||
}
|
||||
|
||||
if (theInstallationSpec.getPackageContents() != null) {
|
||||
return addPackageToCache(theInstallationSpec.getName(), theInstallationSpec.getVersion(), new ByteArrayInputStream(theInstallationSpec.getPackageContents()), "Manually added");
|
||||
}
|
||||
|
||||
return loadPackage(theInstallationSpec.getName(), theInstallationSpec.getVersion());
|
||||
}
|
||||
|
||||
protected byte[] loadPackageUrlContents(String thePackageUrl) {
|
||||
if (thePackageUrl.startsWith("classpath:")) {
|
||||
return ClasspathUtil.loadResourceAsByteArray(thePackageUrl.substring("classpath:".length()));
|
||||
} else {
|
||||
HttpClientConnectionManager connManager = new BasicHttpClientConnectionManager();
|
||||
try (CloseableHttpResponse request = HttpClientBuilder
|
||||
.create()
|
||||
.setConnectionManager(connManager)
|
||||
.build()
|
||||
.execute(new HttpGet(thePackageUrl))) {
|
||||
if (request.getStatusLine().getStatusCode() != 200) {
|
||||
throw new IOException("Received HTTP " + request.getStatusLine().getStatusCode());
|
||||
}
|
||||
return IOUtils.toByteArray(request.getEntity().getContent());
|
||||
} catch (IOException e) {
|
||||
throw new InternalErrorException("Error loading \"" + thePackageUrl + "\": " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public IBaseResource loadPackageAssetByUrl(FhirVersionEnum theFhirVersion, String theCanonicalUrl) {
|
||||
|
||||
String canonicalUrl = theCanonicalUrl;
|
||||
|
||||
int versionSeparator = canonicalUrl.lastIndexOf('|');
|
||||
Slice<NpmPackageVersionResourceEntity> slice;
|
||||
if (versionSeparator != -1) {
|
||||
String canonicalVersion = canonicalUrl.substring(versionSeparator + 1);
|
||||
canonicalUrl = canonicalUrl.substring(0, versionSeparator);
|
||||
slice = myPackageVersionResourceDao.findCurrentVersionByCanonicalUrlAndVersion(PageRequest.of(0, 1), theFhirVersion, canonicalUrl, canonicalVersion);
|
||||
} else {
|
||||
slice = myPackageVersionResourceDao.findCurrentVersionByCanonicalUrl(PageRequest.of(0, 1), theFhirVersion, canonicalUrl);
|
||||
}
|
||||
|
||||
if (slice.isEmpty()) {
|
||||
return null;
|
||||
} else {
|
||||
NpmPackageVersionResourceEntity contents = slice.getContent().get(0);
|
||||
ResourcePersistentId binaryPid = new ResourcePersistentId(contents.getResourceBinary().getId());
|
||||
IBaseBinary binary = getBinaryDao().readByPid(binaryPid);
|
||||
byte[] resourceContentsBytes = BinaryUtil.getOrCreateData(myCtx, binary).getValue();
|
||||
String resourceContents = new String(resourceContentsBytes, StandardCharsets.UTF_8);
|
||||
|
||||
FhirContext packageContext = getFhirContext(contents.getFhirVersion());
|
||||
return EncodingEnum.detectEncoding(resourceContents).newParser(packageContext).parseResource(resourceContents);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public NpmPackageMetadataJson loadPackageMetadata(String thePackageId) {
|
||||
NpmPackageMetadataJson retVal = new NpmPackageMetadataJson();
|
||||
|
||||
Optional<NpmPackageEntity> pkg = myPackageDao.findByPackageId(thePackageId);
|
||||
if (!pkg.isPresent()) {
|
||||
throw new ResourceNotFoundException("Unknown package ID: " + thePackageId);
|
||||
}
|
||||
|
||||
List<NpmPackageVersionEntity> packageVersions = new ArrayList<>(myPackageVersionDao.findByPackageId(thePackageId));
|
||||
packageVersions.sort(new ReverseComparator<>((o1, o2) -> PackageVersionComparator.INSTANCE.compare(o1.getVersionId(), o2.getVersionId())));
|
||||
|
||||
for (NpmPackageVersionEntity next : packageVersions) {
|
||||
if (next.isCurrentVersion()) {
|
||||
retVal.setDistTags(new NpmPackageMetadataJson.DistTags().setLatest(next.getVersionId()));
|
||||
}
|
||||
|
||||
NpmPackageMetadataJson.Version version = new NpmPackageMetadataJson.Version();
|
||||
version.setFhirVersion(next.getFhirVersionId());
|
||||
version.setDescription(next.getDescription());
|
||||
version.setName(next.getName());
|
||||
version.setVersion(next.getVersionId());
|
||||
version.setBytes(next.getPackageSizeBytes());
|
||||
retVal.addVersion(version);
|
||||
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public PackageContents loadPackageContents(String thePackageId, String theVersion) {
|
||||
Optional<NpmPackageVersionEntity> entity = loadPackageVersionEntity(thePackageId, theVersion);
|
||||
return entity.map(t -> loadPackageContents(t)).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public NpmPackageSearchResultJson search(PackageSearchSpec thePackageSearchSpec) {
|
||||
NpmPackageSearchResultJson retVal = new NpmPackageSearchResultJson();
|
||||
|
||||
CriteriaBuilder cb = myEntityManager.getCriteriaBuilder();
|
||||
|
||||
// Query for total
|
||||
{
|
||||
CriteriaQuery<Long> countCriteriaQuery = cb.createQuery(Long.class);
|
||||
Root<NpmPackageVersionEntity> countCriteriaRoot = countCriteriaQuery.from(NpmPackageVersionEntity.class);
|
||||
countCriteriaQuery.multiselect(cb.countDistinct(countCriteriaRoot.get("myPackageId")));
|
||||
|
||||
List<Predicate> predicates = createSearchPredicates(thePackageSearchSpec, cb, countCriteriaRoot);
|
||||
|
||||
countCriteriaQuery.where(toPredicateArray(predicates));
|
||||
Long total = myEntityManager.createQuery(countCriteriaQuery).getSingleResult();
|
||||
retVal.setTotal(Math.toIntExact(total));
|
||||
}
|
||||
|
||||
// Query for results
|
||||
{
|
||||
CriteriaQuery<NpmPackageVersionEntity> criteriaQuery = cb.createQuery(NpmPackageVersionEntity.class);
|
||||
Root<NpmPackageVersionEntity> root = criteriaQuery.from(NpmPackageVersionEntity.class);
|
||||
|
||||
List<Predicate> predicates = createSearchPredicates(thePackageSearchSpec, cb, root);
|
||||
|
||||
criteriaQuery.where(toPredicateArray(predicates));
|
||||
criteriaQuery.orderBy(cb.asc(root.get("myPackageId")));
|
||||
TypedQuery<NpmPackageVersionEntity> query = myEntityManager.createQuery(criteriaQuery);
|
||||
query.setFirstResult(thePackageSearchSpec.getStart());
|
||||
query.setMaxResults(thePackageSearchSpec.getSize());
|
||||
|
||||
List<NpmPackageVersionEntity> resultList = query.getResultList();
|
||||
for (NpmPackageVersionEntity next : resultList) {
|
||||
|
||||
if (!retVal.hasPackageWithId(next.getPackageId())) {
|
||||
retVal
|
||||
.addObject()
|
||||
.getPackage()
|
||||
.setName(next.getPackageId())
|
||||
.setDescription(next.getPackage().getDescription())
|
||||
.setVersion(next.getVersionId())
|
||||
.addFhirVersion(next.getFhirVersionId())
|
||||
.setBytes(next.getPackageSizeBytes());
|
||||
} else {
|
||||
NpmPackageSearchResultJson.Package retPackage = retVal.getPackageWithId(next.getPackageId());
|
||||
retPackage.addFhirVersion(next.getFhirVersionId());
|
||||
|
||||
int cmp = PackageVersionComparator.INSTANCE.compare(next.getVersionId(), retPackage.getVersion());
|
||||
if (cmp > 0) {
|
||||
retPackage.setVersion(next.getVersionId());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public PackageDeleteOutcomeJson uninstallPackage(String thePackageId, String theVersion) {
|
||||
PackageDeleteOutcomeJson retVal = new PackageDeleteOutcomeJson();
|
||||
|
||||
Optional<NpmPackageVersionEntity> packageVersion = myPackageVersionDao.findByPackageIdAndVersion(thePackageId, theVersion);
|
||||
if (packageVersion.isPresent()) {
|
||||
|
||||
String msg = "Deleting package " + thePackageId + "#" + theVersion;
|
||||
ourLog.info(msg);
|
||||
retVal.getMessage().add(msg);
|
||||
|
||||
for (NpmPackageVersionResourceEntity next : packageVersion.get().getResources()) {
|
||||
msg = "Deleting package +" + thePackageId + "#" + theVersion + "resource: " + next.getCanonicalUrl();
|
||||
ourLog.info(msg);
|
||||
retVal.getMessage().add(msg);
|
||||
|
||||
myPackageVersionResourceDao.delete(next);
|
||||
|
||||
ExpungeOptions options = new ExpungeOptions();
|
||||
options.setExpungeDeletedResources(true).setExpungeOldVersions(true);
|
||||
getBinaryDao().delete(next.getResourceBinary().getIdDt().toVersionless());
|
||||
getBinaryDao().forceExpungeInExistingTransaction(next.getResourceBinary().getIdDt().toVersionless(), options, null);
|
||||
}
|
||||
|
||||
myPackageVersionDao.delete(packageVersion.get());
|
||||
|
||||
ExpungeOptions options = new ExpungeOptions();
|
||||
options.setExpungeDeletedResources(true).setExpungeOldVersions(true);
|
||||
getBinaryDao().delete(packageVersion.get().getPackageBinary().getIdDt().toVersionless());
|
||||
getBinaryDao().forceExpungeInExistingTransaction(packageVersion.get().getPackageBinary().getIdDt().toVersionless(), options, null);
|
||||
|
||||
Collection<NpmPackageVersionEntity> remainingVersions = myPackageVersionDao.findByPackageId(thePackageId);
|
||||
if (remainingVersions.size() == 0) {
|
||||
msg = "No versions of package " + thePackageId + " remain";
|
||||
ourLog.info(msg);
|
||||
retVal.getMessage().add(msg);
|
||||
Optional<NpmPackageEntity> pkgEntity = myPackageDao.findByPackageId(thePackageId);
|
||||
myPackageDao.delete(pkgEntity.get());
|
||||
} else {
|
||||
|
||||
List<NpmPackageVersionEntity> versions = remainingVersions
|
||||
.stream()
|
||||
.sorted((o1, o2) -> PackageVersionComparator.INSTANCE.compare(o1.getVersionId(), o2.getVersionId()))
|
||||
.collect(Collectors.toList());
|
||||
for (int i = 0; i < versions.size(); i++) {
|
||||
boolean isCurrent = i == versions.size() - 1;
|
||||
if (isCurrent != versions.get(i).isCurrentVersion()) {
|
||||
versions.get(i).setCurrentVersion(isCurrent);
|
||||
myPackageVersionDao.save(versions.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
String msg = "No package found with the given ID";
|
||||
retVal.getMessage().add(msg);
|
||||
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public List<Predicate> createSearchPredicates(PackageSearchSpec thePackageSearchSpec, CriteriaBuilder theCb, Root<NpmPackageVersionEntity> theRoot) {
|
||||
List<Predicate> predicates = new ArrayList<>();
|
||||
|
||||
if (isNotBlank(thePackageSearchSpec.getResourceUrl())) {
|
||||
Join<NpmPackageVersionEntity, NpmPackageVersionResourceEntity> resources = theRoot.join("myResources", JoinType.LEFT);
|
||||
|
||||
predicates.add(theCb.equal(resources.get("myCanonicalUrl").as(String.class), thePackageSearchSpec.getResourceUrl()));
|
||||
}
|
||||
|
||||
if (isNotBlank(thePackageSearchSpec.getDescription())) {
|
||||
String searchTerm = "%" + thePackageSearchSpec.getDescription() + "%";
|
||||
searchTerm = StringUtil.normalizeStringForSearchIndexing(searchTerm);
|
||||
predicates.add(theCb.like(theRoot.get("myDescriptionUpper").as(String.class), searchTerm));
|
||||
}
|
||||
|
||||
if (isNotBlank(thePackageSearchSpec.getFhirVersion())) {
|
||||
if (!thePackageSearchSpec.getFhirVersion().matches("([0-9]+\\.)+[0-9]+")) {
|
||||
FhirVersionEnum versionEnum = FhirVersionEnum.forVersionString(thePackageSearchSpec.getFhirVersion());
|
||||
if (versionEnum != null) {
|
||||
predicates.add(theCb.equal(theRoot.get("myFhirVersion").as(FhirVersionEnum.class), versionEnum));
|
||||
}
|
||||
} else {
|
||||
predicates.add(theCb.like(theRoot.get("myFhirVersionId").as(String.class), thePackageSearchSpec.getFhirVersion() + "%"));
|
||||
}
|
||||
}
|
||||
|
||||
return predicates;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static List<String> getProcessingMessages(NpmPackage thePackage) {
|
||||
return (List<String>) thePackage.getUserData().computeIfAbsent("JpPackageCache_ProcessingMessages", t -> new ArrayList<>());
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
package ca.uhn.fhir.jpa.packages;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 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.context.support.IValidationSupport;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class NpmJpaValidationSupport implements IValidationSupport {
|
||||
|
||||
@Autowired
|
||||
private FhirContext myFhirContext;
|
||||
|
||||
@Autowired
|
||||
private IHapiPackageCacheManager myHapiPackageCacheManager;
|
||||
|
||||
@Override
|
||||
public FhirContext getFhirContext() {
|
||||
return myFhirContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBaseResource fetchValueSet(String theUri) {
|
||||
return fetchResource("ValueSet", theUri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBaseResource fetchCodeSystem(String theUri) {
|
||||
return fetchResource("CodeSystem", theUri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBaseResource fetchStructureDefinition(String theUri) {
|
||||
return fetchResource("StructureDefinition", theUri);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public IBaseResource fetchResource(String theResourceType, String theUri) {
|
||||
FhirVersionEnum fhirVersion = myFhirContext.getVersion().getVersion();
|
||||
IBaseResource asset = myHapiPackageCacheManager.loadPackageAssetByUrl(fhirVersion, theUri);
|
||||
if (asset != null) {
|
||||
Class<? extends IBaseResource> type = myFhirContext.getResourceDefinition(theResourceType).getImplementingClass();
|
||||
if (type.isAssignableFrom(asset.getClass())) {
|
||||
return asset;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
package ca.uhn.fhir.jpa.packages;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 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.jpa.util.JsonDateDeserializer;
|
||||
import ca.uhn.fhir.jpa.util.JsonDateSerializer;
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@ApiModel("Represents an NPM package metadata response")
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@JsonAutoDetect(creatorVisibility = JsonAutoDetect.Visibility.NONE, fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
public class NpmPackageMetadataJson {
|
||||
|
||||
@JsonProperty("dist-tags")
|
||||
private DistTags myDistTags;
|
||||
@JsonProperty("modified")
|
||||
@JsonSerialize(using = JsonDateSerializer.class)
|
||||
@JsonDeserialize(using = JsonDateDeserializer.class)
|
||||
private Date myModified;
|
||||
@JsonProperty("name")
|
||||
private String myName;
|
||||
@JsonProperty("versions")
|
||||
private Map<String, Version> myVersionIdToVersion;
|
||||
|
||||
public void addVersion(Version theVersion) {
|
||||
getVersions().put(theVersion.getVersion(), theVersion);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public Map<String, Version> getVersions() {
|
||||
if (myVersionIdToVersion == null) {
|
||||
myVersionIdToVersion = new LinkedHashMap<>();
|
||||
}
|
||||
return myVersionIdToVersion;
|
||||
}
|
||||
|
||||
public DistTags getDistTags() {
|
||||
return myDistTags;
|
||||
}
|
||||
|
||||
public void setDistTags(DistTags theDistTags) {
|
||||
myDistTags = theDistTags;
|
||||
}
|
||||
|
||||
public void setModified(Date theModified) {
|
||||
myModified = theModified;
|
||||
}
|
||||
|
||||
public void setName(String theName) {
|
||||
myName = theName;
|
||||
}
|
||||
|
||||
|
||||
public static class DistTags {
|
||||
|
||||
@JsonProperty("latest")
|
||||
private String myLatest;
|
||||
|
||||
public String getLatest() {
|
||||
return myLatest;
|
||||
}
|
||||
|
||||
public DistTags setLatest(String theLatest) {
|
||||
myLatest = theLatest;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@JsonAutoDetect(creatorVisibility = JsonAutoDetect.Visibility.NONE, fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
public static class Version {
|
||||
|
||||
@JsonProperty("name")
|
||||
private String myName;
|
||||
@JsonProperty("version")
|
||||
private String myVersion;
|
||||
@JsonProperty("description")
|
||||
private String myDescription;
|
||||
@JsonProperty("fhirVersion")
|
||||
private String myFhirVersion;
|
||||
@ApiModelProperty(value = "The size of this package in bytes", example = "1000")
|
||||
@JsonProperty("_bytes")
|
||||
private long myBytes;
|
||||
|
||||
public String getName() {
|
||||
return myName;
|
||||
}
|
||||
|
||||
public void setName(String theName) {
|
||||
myName = theName;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return myDescription;
|
||||
}
|
||||
|
||||
public void setDescription(String theDescription) {
|
||||
myDescription = theDescription;
|
||||
}
|
||||
|
||||
public String getFhirVersion() {
|
||||
return myFhirVersion;
|
||||
}
|
||||
|
||||
public void setFhirVersion(String theFhirVersion) {
|
||||
myFhirVersion = theFhirVersion;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return myVersion;
|
||||
}
|
||||
|
||||
public void setVersion(String theVersion) {
|
||||
myVersion = theVersion;
|
||||
}
|
||||
|
||||
public long getBytes() {
|
||||
return myBytes;
|
||||
}
|
||||
|
||||
public void setBytes(long theBytes) {
|
||||
myBytes = theBytes;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
package ca.uhn.fhir.jpa.packages;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 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 com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@ApiModel("Represents an NPM package search response")
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@JsonAutoDetect(creatorVisibility = JsonAutoDetect.Visibility.NONE, fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
public class NpmPackageSearchResultJson {
|
||||
|
||||
@JsonProperty("objects")
|
||||
private List<ObjectElement> myObjects;
|
||||
@JsonProperty("total")
|
||||
private int myTotal;
|
||||
|
||||
public List<ObjectElement> getObjects() {
|
||||
if (myObjects == null) {
|
||||
myObjects = new ArrayList<>();
|
||||
}
|
||||
return myObjects;
|
||||
}
|
||||
|
||||
public ObjectElement addObject() {
|
||||
ObjectElement object = new ObjectElement();
|
||||
getObjects().add(object);
|
||||
return object;
|
||||
}
|
||||
|
||||
public int getTotal() {
|
||||
return myTotal;
|
||||
}
|
||||
|
||||
public void setTotal(int theTotal) {
|
||||
myTotal = theTotal;
|
||||
}
|
||||
|
||||
public boolean hasPackageWithId(String thePackageId) {
|
||||
return getObjects().stream().anyMatch(t -> t.getPackage().getName().equals(thePackageId));
|
||||
}
|
||||
|
||||
public Package getPackageWithId(String thePackageId) {
|
||||
return getObjects().stream().map(t -> t.getPackage()).filter(t -> t.getName().equals(thePackageId)).findFirst().orElseThrow(() -> new IllegalArgumentException());
|
||||
}
|
||||
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@JsonAutoDetect(creatorVisibility = JsonAutoDetect.Visibility.NONE, fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
public static class ObjectElement {
|
||||
|
||||
@JsonProperty("package")
|
||||
private Package myPackage;
|
||||
|
||||
public Package getPackage() {
|
||||
if (myPackage == null) {
|
||||
myPackage = new Package();
|
||||
}
|
||||
return myPackage;
|
||||
}
|
||||
}
|
||||
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@JsonAutoDetect(creatorVisibility = JsonAutoDetect.Visibility.NONE, fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
public static class Package {
|
||||
|
||||
@JsonProperty("name")
|
||||
private String myName;
|
||||
@JsonProperty("version")
|
||||
private String myVersion;
|
||||
@JsonProperty("description")
|
||||
private String myDescription;
|
||||
@JsonProperty("fhirVersion")
|
||||
private List<String> myFhirVersion;
|
||||
@ApiModelProperty(value = "The size of this package in bytes", example = "1000")
|
||||
@JsonProperty("_bytes")
|
||||
private long myBytes;
|
||||
|
||||
public long getBytes() {
|
||||
return myBytes;
|
||||
}
|
||||
|
||||
public Package setBytes(long theBytes) {
|
||||
myBytes = theBytes;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return myName;
|
||||
}
|
||||
|
||||
public Package setName(String theName) {
|
||||
myName = theName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return myDescription;
|
||||
}
|
||||
|
||||
public Package setDescription(String theDescription) {
|
||||
myDescription = theDescription;
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<String> getFhirVersion() {
|
||||
if (myFhirVersion == null) {
|
||||
myFhirVersion = new ArrayList<>();
|
||||
}
|
||||
return myFhirVersion;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return myVersion;
|
||||
}
|
||||
|
||||
public Package setVersion(String theVersion) {
|
||||
myVersion = theVersion;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Package addFhirVersion(String theFhirVersionId) {
|
||||
if (!getFhirVersion().contains(theFhirVersionId)) {
|
||||
getFhirVersion().add(theFhirVersionId);
|
||||
getFhirVersion().sort(PackageVersionComparator.INSTANCE);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package ca.uhn.fhir.jpa.packages;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 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 com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@ApiModel("Represents an NPM package deletion response")
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@JsonAutoDetect(creatorVisibility = JsonAutoDetect.Visibility.NONE, fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
public class PackageDeleteOutcomeJson {
|
||||
|
||||
@JsonProperty("messages")
|
||||
private List<String> myMessage;
|
||||
|
||||
public List<String> getMessage() {
|
||||
if (myMessage == null) {
|
||||
myMessage = new ArrayList<>();
|
||||
}
|
||||
return myMessage;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package ca.uhn.fhir.jpa.packages;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 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 com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@ApiModel("Represents an NPM package installation response")
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@JsonAutoDetect(creatorVisibility = JsonAutoDetect.Visibility.NONE, fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
public class PackageInstallOutcomeJson {
|
||||
|
||||
@JsonProperty("messages")
|
||||
private List<String> myMessage;
|
||||
|
||||
public List<String> getMessage() {
|
||||
if (myMessage == null) {
|
||||
myMessage = new ArrayList<>();
|
||||
}
|
||||
return myMessage;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,196 @@
|
|||
package ca.uhn.fhir.jpa.packages;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 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.model.api.annotation.ExampleSupplier;
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@ApiModel(
|
||||
value = "PackageInstallationSpec",
|
||||
description =
|
||||
"Defines a set of instructions for package installation"
|
||||
)
|
||||
@JsonPropertyOrder({
|
||||
"name", "version", "packageUrl", "installMode", "installResourceTypes", "validationMode"
|
||||
})
|
||||
@ExampleSupplier({PackageInstallationSpec.ExampleSupplier.class, PackageInstallationSpec.ExampleSupplier2.class})
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@JsonAutoDetect(creatorVisibility = JsonAutoDetect.Visibility.NONE, fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
public class PackageInstallationSpec {
|
||||
|
||||
@ApiModelProperty("The direct package URL")
|
||||
@JsonProperty("packageUrl")
|
||||
private String myPackageUrl;
|
||||
@ApiModelProperty("The NPM package Name")
|
||||
@JsonProperty("name")
|
||||
private String myName;
|
||||
@ApiModelProperty("The direct package version")
|
||||
@JsonProperty("version")
|
||||
private String myVersion;
|
||||
@ApiModelProperty("Should resources from this package be extracted from the package and installed into the repository individually")
|
||||
@JsonProperty("installMode")
|
||||
private InstallModeEnum myInstallMode;
|
||||
@ApiModelProperty("If resources are being installed individually, this is list provides the resource types to install. By default, all conformance resources will be installed.")
|
||||
@JsonProperty("installResourceTypes")
|
||||
private List<String> myInstallResourceTypes;
|
||||
@ApiModelProperty("Should dependencies be automatically resolved, fetched and installed with the same settings")
|
||||
@JsonProperty("fetchDependencies")
|
||||
private boolean myFetchDependencies;
|
||||
@ApiModelProperty("Any values provided here will be interpreted as a regex. Dependencies with an ID matching any regex will be skipped.")
|
||||
private List<String> myDependencyExcludes;
|
||||
@JsonIgnore
|
||||
private byte[] myPackageContents;
|
||||
|
||||
public List<String> getDependencyExcludes() {
|
||||
if (myDependencyExcludes == null) {
|
||||
myDependencyExcludes = new ArrayList<>();
|
||||
}
|
||||
return myDependencyExcludes;
|
||||
}
|
||||
|
||||
public void setDependencyExcludes(List<String> theDependencyExcludes) {
|
||||
myDependencyExcludes = theDependencyExcludes;
|
||||
}
|
||||
|
||||
public boolean isFetchDependencies() {
|
||||
return myFetchDependencies;
|
||||
}
|
||||
|
||||
public PackageInstallationSpec setFetchDependencies(boolean theFetchDependencies) {
|
||||
myFetchDependencies = theFetchDependencies;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getPackageUrl() {
|
||||
return myPackageUrl;
|
||||
}
|
||||
|
||||
public PackageInstallationSpec setPackageUrl(String thePackageUrl) {
|
||||
myPackageUrl = thePackageUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
public InstallModeEnum getInstallMode() {
|
||||
return myInstallMode;
|
||||
}
|
||||
|
||||
public PackageInstallationSpec setInstallMode(InstallModeEnum theInstallMode) {
|
||||
myInstallMode = theInstallMode;
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<String> getInstallResourceTypes() {
|
||||
if (myInstallResourceTypes == null) {
|
||||
myInstallResourceTypes = new ArrayList<>();
|
||||
}
|
||||
return myInstallResourceTypes;
|
||||
}
|
||||
|
||||
public void setInstallResourceTypes(List<String> theInstallResourceTypes) {
|
||||
myInstallResourceTypes = theInstallResourceTypes;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return myName;
|
||||
}
|
||||
|
||||
public PackageInstallationSpec setName(String theName) {
|
||||
myName = theName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return myVersion;
|
||||
}
|
||||
|
||||
public PackageInstallationSpec setVersion(String theVersion) {
|
||||
myVersion = theVersion;
|
||||
return this;
|
||||
}
|
||||
|
||||
public byte[] getPackageContents() {
|
||||
return myPackageContents;
|
||||
}
|
||||
|
||||
public PackageInstallationSpec setPackageContents(byte[] thePackageContents) {
|
||||
myPackageContents = thePackageContents;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PackageInstallationSpec addDependencyExclude(String theExclude) {
|
||||
getDependencyExcludes().add(theExclude);
|
||||
return this;
|
||||
}
|
||||
|
||||
public PackageInstallationSpec addInstallResourceTypes(String... theResourceTypes) {
|
||||
for (String next : theResourceTypes) {
|
||||
getInstallResourceTypes().add(next);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public enum InstallModeEnum {
|
||||
STORE_ONLY,
|
||||
STORE_AND_INSTALL
|
||||
}
|
||||
|
||||
public enum ValidationModeEnum {
|
||||
NOT_AVAILABLE,
|
||||
AVAILABLE
|
||||
}
|
||||
|
||||
public static class ExampleSupplier implements Supplier<PackageInstallationSpec> {
|
||||
|
||||
@Override
|
||||
public PackageInstallationSpec get() {
|
||||
return new PackageInstallationSpec()
|
||||
.setName("hl7.fhir.us.core")
|
||||
.setVersion("3.1.0")
|
||||
.setInstallMode(InstallModeEnum.STORE_ONLY)
|
||||
.setFetchDependencies(true);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ExampleSupplier2 implements Supplier<PackageInstallationSpec> {
|
||||
|
||||
@Override
|
||||
public PackageInstallationSpec get() {
|
||||
return new PackageInstallationSpec()
|
||||
.setName("com.example.my-resources")
|
||||
.setVersion("1.0")
|
||||
.setPackageUrl("classpath:/my-resources.tgz")
|
||||
.setInstallMode(InstallModeEnum.STORE_AND_INSTALL)
|
||||
.addInstallResourceTypes("Organization", "Medication", "PlanDefinition", "SearchParameter");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.packages;
|
|||
*/
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||
|
@ -31,48 +32,60 @@ import ca.uhn.fhir.rest.param.StringParam;
|
|||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
import ca.uhn.fhir.rest.param.UriParam;
|
||||
import ca.uhn.fhir.util.FhirTerser;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
import org.hl7.fhir.utilities.cache.IPackageCacheManager;
|
||||
import org.hl7.fhir.utilities.cache.NpmPackage;
|
||||
import org.hl7.fhir.utilities.cache.PackageCacheManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class IgInstallerSvc {
|
||||
/**
|
||||
* @since 5.1.0
|
||||
*/
|
||||
public class PackageInstallerSvcImpl implements IPackageInstallerSvc {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(IgInstallerSvc.class);
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(PackageInstallerSvcImpl.class);
|
||||
public static List<String> DEFAULT_INSTALL_TYPES = Collections.unmodifiableList(Lists.newArrayList(
|
||||
"NamingSystem",
|
||||
"CodeSystem",
|
||||
"ValueSet",
|
||||
"StructureDefinition",
|
||||
"ConceptMap",
|
||||
"SearchParameter",
|
||||
"Subscription"
|
||||
));
|
||||
|
||||
boolean enabled = true;
|
||||
|
||||
@Autowired
|
||||
private FhirContext fhirContext;
|
||||
@Autowired
|
||||
private DaoRegistry daoRegistry;
|
||||
@Autowired
|
||||
private IValidationSupport validationSupport;
|
||||
@Autowired
|
||||
private IHapiPackageCacheManager packageCacheManager;
|
||||
|
||||
private PackageCacheManager packageCacheManager;
|
||||
|
||||
private String[] SUPPORTED_RESOURCE_TYPES = new String[]
|
||||
{ "NamingSystem",
|
||||
"CodeSystem",
|
||||
"ValueSet",
|
||||
"StructureDefinition",
|
||||
"ConceptMap",
|
||||
"SearchParameter",
|
||||
"Subscription" };
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public PackageInstallerSvcImpl() {
|
||||
super();
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void initialize() {
|
||||
|
@ -90,91 +103,76 @@ public class IgInstallerSvc {
|
|||
enabled = false;
|
||||
}
|
||||
}
|
||||
try {
|
||||
packageCacheManager = new PackageCacheManager(true, 1);
|
||||
} catch (IOException e) {
|
||||
ourLog.error("Unable to initialize PackageCacheManager: {}", e);
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads and installs an IG tarball (with its dependencies) from the specified url.
|
||||
*
|
||||
* Installs the IG by persisting instances of the following types of resources:
|
||||
*
|
||||
* - NamingSystem, CodeSystem, ValueSet, StructureDefinition (with snapshots),
|
||||
* ConceptMap, SearchParameter, Subscription
|
||||
*
|
||||
* Creates the resources if non-existent, updates them otherwise.
|
||||
*
|
||||
* @param url of IG tarball
|
||||
* @throws ImplementationGuideInstallationException if installation fails
|
||||
*/
|
||||
public void install(String url) throws ImplementationGuideInstallationException {
|
||||
if (enabled) {
|
||||
try {
|
||||
install(NpmPackage.fromPackage(toInputStream(url)));
|
||||
} catch (IOException e) {
|
||||
ourLog.error("Could not load implementation guide from URL {}", url, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private InputStream toInputStream(String url) throws IOException {
|
||||
URL u = new URL(url);
|
||||
URLConnection c = u.openConnection();
|
||||
return c.getInputStream();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads and installs an IG from a file on disk or the Simplifier repo using
|
||||
* the {@link PackageCacheManager}.
|
||||
*
|
||||
* the {@link IPackageCacheManager}.
|
||||
* <p>
|
||||
* Installs the IG by persisting instances of the following types of resources:
|
||||
*
|
||||
* <p>
|
||||
* - NamingSystem, CodeSystem, ValueSet, StructureDefinition (with snapshots),
|
||||
* ConceptMap, SearchParameter, Subscription
|
||||
*
|
||||
* ConceptMap, SearchParameter, Subscription
|
||||
* <p>
|
||||
* Creates the resources if non-existent, updates them otherwise.
|
||||
*
|
||||
* @param id of the package, or name of folder in filesystem
|
||||
* @param version of package, or path to folder in filesystem
|
||||
* @throws ImplementationGuideInstallationException if installation fails
|
||||
* @param theInstallationSpec The details about what should be installed
|
||||
*/
|
||||
public void install(String id, String version) throws ImplementationGuideInstallationException {
|
||||
@Override
|
||||
public PackageInstallOutcomeJson install(PackageInstallationSpec theInstallationSpec) throws ImplementationGuideInstallationException {
|
||||
PackageInstallOutcomeJson retVal = new PackageInstallOutcomeJson();
|
||||
if (enabled) {
|
||||
try {
|
||||
install(packageCacheManager.loadPackage(id, version));
|
||||
NpmPackage npmPackage = packageCacheManager.installPackage(theInstallationSpec);
|
||||
if (npmPackage == null) {
|
||||
throw new IOException("Package not found");
|
||||
}
|
||||
|
||||
retVal.getMessage().addAll(JpaPackageCache.getProcessingMessages(npmPackage));
|
||||
|
||||
if (theInstallationSpec.isFetchDependencies()) {
|
||||
fetchAndInstallDependencies(npmPackage, theInstallationSpec, retVal);
|
||||
}
|
||||
|
||||
if (theInstallationSpec.getInstallMode() == PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL) {
|
||||
install(npmPackage, theInstallationSpec);
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
ourLog.error("Could not load implementation guide from packages.fhir.org or " +
|
||||
"file on disk using ID {} and version {}", id, version, e);
|
||||
throw new ImplementationGuideInstallationException("Could not load NPM package " + theInstallationSpec.getName() + "#" + theInstallationSpec.getVersion(), e);
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs a package and its dependencies.
|
||||
*
|
||||
* <p>
|
||||
* Fails fast if one of its dependencies could not be installed.
|
||||
*
|
||||
* @throws ImplementationGuideInstallationException if installation fails
|
||||
*/
|
||||
private void install(NpmPackage npmPackage) throws ImplementationGuideInstallationException {
|
||||
private void install(NpmPackage npmPackage, PackageInstallationSpec theInstallationSpec) throws ImplementationGuideInstallationException {
|
||||
String name = npmPackage.getNpm().get("name").getAsString();
|
||||
String version = npmPackage.getNpm().get("version").getAsString();
|
||||
|
||||
String fhirVersion = npmPackage.fhirVersion();
|
||||
String currentFhirVersion = fhirContext.getVersion().getVersion().getFhirVersionString();
|
||||
|
||||
assertFhirVersionsAreCompatible(fhirVersion, currentFhirVersion);
|
||||
|
||||
fetchAndInstallDependencies(npmPackage);
|
||||
List<String> installTypes;
|
||||
if (!theInstallationSpec.getInstallResourceTypes().isEmpty()) {
|
||||
installTypes = theInstallationSpec.getInstallResourceTypes();
|
||||
} else {
|
||||
installTypes = DEFAULT_INSTALL_TYPES;
|
||||
}
|
||||
|
||||
ourLog.info("Installing package: {}#{}", name, version);
|
||||
int[] count = new int[SUPPORTED_RESOURCE_TYPES.length];
|
||||
int[] count = new int[installTypes.size()];
|
||||
|
||||
for (int i = 0; i < SUPPORTED_RESOURCE_TYPES.length; i++) {
|
||||
Collection<IBaseResource> resources = parseResourcesOfType(SUPPORTED_RESOURCE_TYPES[i], npmPackage);
|
||||
for (int i = 0; i < installTypes.size(); i++) {
|
||||
Collection<IBaseResource> resources = parseResourcesOfType(installTypes.get(i), npmPackage);
|
||||
count[i] = resources.size();
|
||||
|
||||
try {
|
||||
|
@ -182,37 +180,48 @@ public class IgInstallerSvc {
|
|||
.map(r -> isStructureDefinitionWithoutSnapshot(r) ? generateSnapshot(r) : r)
|
||||
.forEach(r -> createOrUpdate(r));
|
||||
} catch (Exception e) {
|
||||
throw new ImplementationGuideInstallationException(String.format(
|
||||
"Error installing IG %s#%s: ", name, version), e);
|
||||
throw new ImplementationGuideInstallationException(String.format("Error installing IG %s#%s: %s", name, version, e.toString()), e);
|
||||
}
|
||||
}
|
||||
ourLog.info(String.format("Finished installation of package %s#%s:", name, version));
|
||||
|
||||
for (int i = 0; i < count.length; i++) {
|
||||
ourLog.info(String.format("-- Created or updated %s resources of type %s", count[i], SUPPORTED_RESOURCE_TYPES[i]));
|
||||
ourLog.info(String.format("-- Created or updated %s resources of type %s", count[i], installTypes.get(i)));
|
||||
}
|
||||
}
|
||||
|
||||
private void fetchAndInstallDependencies(NpmPackage npmPackage) throws ImplementationGuideInstallationException {
|
||||
private void fetchAndInstallDependencies(NpmPackage npmPackage, PackageInstallationSpec theInstallationSpec, PackageInstallOutcomeJson theOutcome) throws ImplementationGuideInstallationException {
|
||||
if (npmPackage.getNpm().has("dependencies")) {
|
||||
Map<String, String> dependencies = new Gson().fromJson(npmPackage.getNpm().get("dependencies"), HashMap.class);
|
||||
JsonElement dependenciesElement = npmPackage.getNpm().get("dependencies");
|
||||
Map<String, String> dependencies = new Gson().fromJson(dependenciesElement, HashMap.class);
|
||||
for (Map.Entry<String, String> d : dependencies.entrySet()) {
|
||||
String id = d.getKey();
|
||||
String ver = d.getValue();
|
||||
if (id.startsWith("hl7.fhir")) {
|
||||
continue; // todo : which packages to ignore?
|
||||
}
|
||||
if (packageCacheManager == null) {
|
||||
throw new ImplementationGuideInstallationException(String.format(
|
||||
"Cannot install dependency %s#%s due to PacketCacheManager initialization error", id, ver));
|
||||
}
|
||||
try {
|
||||
theOutcome.getMessage().add("Package " + npmPackage.id() + "#" + npmPackage.version() + " depends on package " + id + "#" + ver);
|
||||
|
||||
boolean skip = false;
|
||||
for (String next : theInstallationSpec.getDependencyExcludes()) {
|
||||
if (id.matches(next)) {
|
||||
theOutcome.getMessage().add("Not installing dependency " + id + " because it matches exclude criteria: " + next);
|
||||
skip = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (skip) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// resolve in local cache or on packages.fhir.org
|
||||
NpmPackage dependency = packageCacheManager.loadPackage(id, ver);
|
||||
// recursive call to install dependencies of a package before
|
||||
// installing the package
|
||||
fetchAndInstallDependencies(dependency);
|
||||
install(dependency);
|
||||
fetchAndInstallDependencies(dependency, theInstallationSpec, theOutcome);
|
||||
|
||||
if (theInstallationSpec.getInstallMode() == PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL) {
|
||||
install(dependency, theInstallationSpec);
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new ImplementationGuideInstallationException(String.format(
|
||||
"Cannot resolve dependency %s#%s", id, ver), e);
|
||||
|
@ -228,8 +237,11 @@ public class IgInstallerSvc {
|
|||
private void assertFhirVersionsAreCompatible(String fhirVersion, String currentFhirVersion)
|
||||
throws ImplementationGuideInstallationException {
|
||||
|
||||
boolean compatible = fhirVersion.charAt(0) == currentFhirVersion.charAt(0) &&
|
||||
currentFhirVersion.compareTo(fhirVersion) >= 0;
|
||||
FhirVersionEnum fhirVersionEnum = FhirVersionEnum.forVersionString(fhirVersion);
|
||||
FhirVersionEnum currentFhirVersionEnum = FhirVersionEnum.forVersionString(currentFhirVersion);
|
||||
Validate.notNull(fhirVersionEnum, "Invalid FHIR version string: %s", fhirVersion);
|
||||
Validate.notNull(currentFhirVersionEnum, "Invalid FHIR version string: %s", currentFhirVersion);
|
||||
boolean compatible = fhirVersionEnum.equals(currentFhirVersionEnum);
|
||||
if (!compatible) {
|
||||
throw new ImplementationGuideInstallationException(String.format(
|
||||
"Cannot install implementation guide: FHIR versions mismatch (expected <=%s, package uses %s)",
|
||||
|
@ -292,7 +304,6 @@ public class IgInstallerSvc {
|
|||
}
|
||||
|
||||
private SearchParameterMap createSearchParameterMapFor(IBaseResource resource) {
|
||||
FhirTerser terser = fhirContext.newTerser();
|
||||
if (resource.getClass().getSimpleName().equals("NamingSystem")) {
|
||||
String uniqueId = extractUniqeIdFromNamingSystem(resource);
|
||||
return new SearchParameterMap().add("value", new StringParam(uniqueId).setExact(true));
|
||||
|
@ -332,16 +343,6 @@ public class IgInstallerSvc {
|
|||
}
|
||||
}
|
||||
|
||||
private static IBaseResource getFirstResourceFrom(IBundleProvider searchResult) {
|
||||
try {
|
||||
return searchResult.getResources(0, 0).get(0);
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
ourLog.warn("Error when extracting resource from search result " +
|
||||
"(search result should have been non-empty))", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String extractUniqeIdFromNamingSystem(IBaseResource resource) {
|
||||
FhirTerser terser = fhirContext.newTerser();
|
||||
IBase uniqueIdComponent = (IBase) terser.getSingleValueOrNull(resource, "uniqueId");
|
||||
|
@ -363,4 +364,14 @@ public class IgInstallerSvc {
|
|||
IPrimitiveType asPrimitiveType = (IPrimitiveType) terser.getSingleValueOrNull(resource, "url");
|
||||
return (String) asPrimitiveType.getValue();
|
||||
}
|
||||
|
||||
private static IBaseResource getFirstResourceFrom(IBundleProvider searchResult) {
|
||||
try {
|
||||
return searchResult.getResources(0, 0).get(0);
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
ourLog.warn("Error when extracting resource from search result " +
|
||||
"(search result should have been non-empty))", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package ca.uhn.fhir.jpa.packages;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 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 org.apache.commons.lang3.Validate;
|
||||
|
||||
public class PackageSearchSpec {
|
||||
private int myStart;
|
||||
private int mySize = 50;
|
||||
private String myResourceUrl;
|
||||
private CharSequence myDescription;
|
||||
private String myFhirVersion;
|
||||
|
||||
public String getFhirVersion() {
|
||||
return myFhirVersion;
|
||||
}
|
||||
|
||||
public void setFhirVersion(String theFhirVersion) {
|
||||
myFhirVersion = theFhirVersion;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return mySize;
|
||||
}
|
||||
|
||||
public void setSize(int theSize) {
|
||||
Validate.inclusiveBetween(1, 250, theSize, "Number must be between 1-250");
|
||||
mySize = theSize;
|
||||
}
|
||||
|
||||
public int getStart() {
|
||||
return myStart;
|
||||
}
|
||||
|
||||
public void setStart(int theStart) {
|
||||
Validate.inclusiveBetween(0, Integer.MAX_VALUE, theStart, "Number must not be negative");
|
||||
myStart = theStart;
|
||||
}
|
||||
|
||||
public String getResourceUrl() {
|
||||
return myResourceUrl;
|
||||
}
|
||||
|
||||
public void setResourceUrl(String theResourceUrl) {
|
||||
myResourceUrl = theResourceUrl;
|
||||
}
|
||||
|
||||
public CharSequence getDescription() {
|
||||
return myDescription;
|
||||
}
|
||||
|
||||
public void setDescription(CharSequence theDescription) {
|
||||
myDescription = theDescription;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package ca.uhn.fhir.jpa.packages;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 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 java.math.BigDecimal;
|
||||
import java.util.Comparator;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNumeric;
|
||||
|
||||
public class PackageVersionComparator implements Comparator<String> {
|
||||
public static final PackageVersionComparator INSTANCE = new PackageVersionComparator();
|
||||
|
||||
@Override
|
||||
public int compare(String o1, String o2) {
|
||||
|
||||
String[] o1parts = o1.split("\\.");
|
||||
String[] o2parts = o2.split("\\.");
|
||||
|
||||
for (int i = 0; i < o1parts.length && i < o2parts.length; i++) {
|
||||
String i1part = o1parts[i];
|
||||
String i2part = o2parts[i];
|
||||
|
||||
if (isNumeric(i1part)) {
|
||||
if (isNumeric(i2part)) {
|
||||
int cmp = new BigDecimal(i1part).compareTo(new BigDecimal(i2part));
|
||||
if (cmp != 0) {
|
||||
return cmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int cmp = i1part.compareTo(i2part);
|
||||
if (cmp != 0) {
|
||||
return cmp;
|
||||
}
|
||||
}
|
||||
|
||||
return o1parts.length - o2parts.length;
|
||||
}
|
||||
|
||||
public static boolean isEquivalent(String theSpec, String thePackageVersion) {
|
||||
String[] o1parts = theSpec.split("\\.");
|
||||
String[] o2parts = thePackageVersion.split("\\.");
|
||||
|
||||
for (int i = 0; i < o1parts.length; i++ ) {
|
||||
if (!o1parts[i].equals("x")) {
|
||||
if (!o1parts[i].equals(o2parts[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -10,6 +10,8 @@ import java.io.ByteArrayInputStream;
|
|||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import static ca.uhn.fhir.util.StringUtil.toUtf8String;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
|
@ -46,7 +48,7 @@ public class XmlPatchUtils {
|
|||
throw new InternalErrorException(e);
|
||||
}
|
||||
|
||||
String resultString = new String(result.toByteArray(), Constants.CHARSET_UTF8);
|
||||
String resultString = toUtf8String(result.toByteArray());
|
||||
T retVal = theCtx.newXmlParser().parseResource(clazz, resultString);
|
||||
|
||||
return retVal;
|
||||
|
|
|
@ -22,6 +22,7 @@ package ca.uhn.fhir.jpa.validation;
|
|||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.jpa.packages.NpmJpaValidationSupport;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
|
||||
import org.hl7.fhir.common.hapi.validation.support.CommonCodeSystemsTerminologyService;
|
||||
import org.hl7.fhir.common.hapi.validation.support.SnapshotGeneratingValidationSupport;
|
||||
|
@ -46,6 +47,8 @@ public class JpaValidationSupportChain extends ValidationSupportChain {
|
|||
private IValidationSupport myDefaultProfileValidationSupport;
|
||||
@Autowired
|
||||
private ITermReadSvc myTerminologyService;
|
||||
@Autowired
|
||||
private NpmJpaValidationSupport myNpmJpaValidationSupport;
|
||||
|
||||
public JpaValidationSupportChain(FhirContext theFhirContext) {
|
||||
myFhirContext = theFhirContext;
|
||||
|
@ -69,6 +72,7 @@ public class JpaValidationSupportChain extends ValidationSupportChain {
|
|||
addValidationSupport(myTerminologyService);
|
||||
addValidationSupport(new SnapshotGeneratingValidationSupport(myFhirContext));
|
||||
addValidationSupport(new InMemoryTerminologyServerValidationSupport(myFhirContext));
|
||||
addValidationSupport(myNpmJpaValidationSupport);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Binary file not shown.
|
@ -437,11 +437,15 @@ public abstract class BaseJpaTest extends BaseTest {
|
|||
}
|
||||
|
||||
public static String loadClasspath(String resource) throws IOException {
|
||||
return new String(loadClasspathBytes(resource), Constants.CHARSET_UTF8);
|
||||
}
|
||||
|
||||
public static byte[] loadClasspathBytes(String resource) throws IOException {
|
||||
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream(resource);
|
||||
if (bundleRes == null) {
|
||||
throw new NullPointerException("Can not load " + resource);
|
||||
}
|
||||
return IOUtils.toString(bundleRes, Constants.CHARSET_UTF8);
|
||||
return IOUtils.toByteArray(bundleRes);
|
||||
}
|
||||
|
||||
protected static void purgeDatabase(DaoConfig theDaoConfig, IFhirSystemDao<?, ?> theSystemDao, IResourceReindexingSvc theResourceReindexingSvc, ISearchCoordinatorSvc theSearchCoordinatorSvc, ISearchParamRegistry theSearchParamRegistry, IBulkDataExportSvc theBulkDataExportSvc) {
|
||||
|
|
|
@ -120,6 +120,7 @@ import org.springframework.transaction.support.TransactionTemplate;
|
|||
import javax.persistence.EntityManager;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.hl7.fhir.convertors.conv30_40.ConceptMap30_40.convertConceptMap;
|
||||
|
@ -408,7 +409,7 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
|
|||
if (stream == null) {
|
||||
fail("Unable to load resource: " + resourceName);
|
||||
}
|
||||
String string = IOUtils.toString(stream, "UTF-8");
|
||||
String string = IOUtils.toString(stream, StandardCharsets.UTF_8);
|
||||
IParser newJsonParser = EncodingEnum.detectEncodingNoDefault(string).newParser(myFhirCtx);
|
||||
return newJsonParser.parseResource(type, string);
|
||||
}
|
||||
|
|
|
@ -1,53 +1,215 @@
|
|||
package ca.uhn.fhir.jpa.packages;
|
||||
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.dao.data.INpmPackageVersionDao;
|
||||
import ca.uhn.fhir.jpa.dao.dstu3.BaseJpaDstu3Test;
|
||||
import org.hl7.fhir.utilities.cache.PackageCacheManager;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.test.utilities.JettyUtil;
|
||||
import ca.uhn.fhir.test.utilities.ProxyUtil;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.servlet.ServletHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.hl7.fhir.utilities.cache.IPackageCacheManager;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static ca.uhn.fhir.util.ClasspathUtil.loadResourceAsByteArray;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.hasItem;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class IgInstallerTestDstu3 extends BaseJpaDstu3Test {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(IgInstallerTestDstu3.class);
|
||||
@Autowired
|
||||
private DaoConfig daoConfig;
|
||||
@Autowired
|
||||
private IgInstallerSvc igInstaller;
|
||||
|
||||
@Rule
|
||||
public final ExpectedException expectedException = ExpectedException.none();
|
||||
private PackageInstallerSvcImpl igInstaller;
|
||||
@Autowired
|
||||
private IPackageCacheManager myPackageCacheManager;
|
||||
private Server myServer;
|
||||
private NpmTestR4.FakeNpmServlet myFakeNpmServlet;
|
||||
@Autowired
|
||||
private INpmPackageVersionDao myPackageVersionDao;
|
||||
private int myPort;
|
||||
|
||||
@Before
|
||||
public void before() throws IOException {
|
||||
PackageCacheManager packageCacheManager = new PackageCacheManager(true, 1);
|
||||
public void before() throws Exception {
|
||||
JpaPackageCache jpaPackageCache = ProxyUtil.getSingletonTarget(myPackageCacheManager, JpaPackageCache.class);
|
||||
|
||||
myServer = new Server(0);
|
||||
ServletHandler proxyHandler = new ServletHandler();
|
||||
myFakeNpmServlet = new NpmTestR4.FakeNpmServlet();
|
||||
ServletHolder servletHolder = new ServletHolder(myFakeNpmServlet);
|
||||
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
||||
myServer.setHandler(proxyHandler);
|
||||
myServer.start();
|
||||
|
||||
myPort = JettyUtil.getPortForStartedServer(myServer);
|
||||
jpaPackageCache.getPackageServers().clear();
|
||||
jpaPackageCache.addPackageServer("http://localhost:" + myPort);
|
||||
|
||||
myFakeNpmServlet.getResponses().clear();
|
||||
|
||||
InputStream stream;
|
||||
stream = IgInstallerTestDstu3.class.getResourceAsStream("erroneous-ig.tar.gz");
|
||||
packageCacheManager.addPackageToCache("erroneous-ig", "1.0.0", stream, "erroneous-ig");
|
||||
stream = IgInstallerTestDstu3.class.getResourceAsStream("NHSD.Assets.STU3.tar.gz");
|
||||
packageCacheManager.addPackageToCache("NHSD.Assets.STU3", "1.0.0", stream, "NHSD.Assets.STU3");
|
||||
stream = IgInstallerTestDstu3.class.getResourceAsStream("basisprofil.de.tar.gz");
|
||||
packageCacheManager.addPackageToCache("basisprofil.de", "0.2.40", stream, "basisprofil.de");
|
||||
}
|
||||
|
||||
@Test(expected = ImplementationGuideInstallationException.class)
|
||||
public void negativeTestInstallFromCache() {
|
||||
// Unknown base of StructureDefinitions
|
||||
igInstaller.install("erroneous-ig", "1.0.0");
|
||||
@After
|
||||
public void after() throws Exception {
|
||||
JettyUtil.closeServer(myServer);
|
||||
daoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void installFromCache() {
|
||||
public void testNegativeInstallFromCache() {
|
||||
daoConfig.setAllowExternalReferences(true);
|
||||
igInstaller.install("NHSD.Assets.STU3", "1.2.0");
|
||||
|
||||
byte[] bytes = loadResourceAsByteArray("/packages/erroneous-ig.tar.gz");
|
||||
|
||||
// Unknown base of StructureDefinitions
|
||||
try {
|
||||
igInstaller.install(new PackageInstallationSpec().setName("erroneous-ig").setVersion("1.0.2").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL).setPackageContents(bytes));
|
||||
fail();
|
||||
} catch (ImplementationGuideInstallationException e) {
|
||||
Assert.assertThat(e.getMessage(), containsString("Failure when generating snapshot of StructureDefinition"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInstallPackageWithDependencies() {
|
||||
byte[] bytes;
|
||||
|
||||
bytes = loadResourceAsByteArray("/packages/nictiz.fhir.nl.stu3.questionnaires-1.0.2.tgz");
|
||||
myFakeNpmServlet.getResponses().put("/nictiz.fhir.nl.stu3.questionnaires/1.0.2", bytes);
|
||||
|
||||
bytes = loadResourceAsByteArray("/packages/nictiz.fhir.nl.stu3.zib2017-1.3.10.tgz");
|
||||
myFakeNpmServlet.getResponses().put("/nictiz.fhir.nl.stu3.zib2017/1.3.x", bytes);
|
||||
|
||||
daoConfig.setAllowExternalReferences(true);
|
||||
PackageInstallationSpec spec = new PackageInstallationSpec()
|
||||
.setName("nictiz.fhir.nl.stu3.questionnaires")
|
||||
.setVersion("1.0.2")
|
||||
.setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL)
|
||||
.setFetchDependencies(true)
|
||||
.addDependencyExclude("hl7\\.fhir\\.[a-zA-Z0-9]+\\.core");
|
||||
PackageInstallOutcomeJson outcome = igInstaller.install(spec);
|
||||
ourLog.info("Install messages:\n * {}", outcome.getMessage().stream().collect(Collectors.joining("\n * ")));
|
||||
assertThat(outcome.getMessage(), hasItem("Indexing Resource[package/vl-QuestionnaireProvisioningTask.json] with URL: http://nictiz.nl/fhir/StructureDefinition/vl-QuestionnaireProvisioningTask|1.0.1"));
|
||||
|
||||
runInTransaction(() -> {
|
||||
assertTrue(myPackageVersionDao.findByPackageIdAndVersion("nictiz.fhir.nl.stu3.questionnaires", "1.0.2").isPresent());
|
||||
assertTrue(myPackageVersionDao.findByPackageIdAndVersion("nictiz.fhir.nl.stu3.zib2017", "1.3.10").isPresent());
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInstallPackageWithoutDependencies() {
|
||||
byte[] bytes;
|
||||
|
||||
bytes = loadResourceAsByteArray("/packages/nictiz.fhir.nl.stu3.questionnaires-1.0.2.tgz");
|
||||
myFakeNpmServlet.getResponses().put("/nictiz.fhir.nl.stu3.questionnaires/1.0.2", bytes);
|
||||
|
||||
bytes = loadResourceAsByteArray("/packages/nictiz.fhir.nl.stu3.zib2017-1.3.10.tgz");
|
||||
myFakeNpmServlet.getResponses().put("/nictiz.fhir.nl.stu3.zib2017/1.3.x", bytes);
|
||||
|
||||
daoConfig.setAllowExternalReferences(true);
|
||||
igInstaller.install(new PackageInstallationSpec().setName("nictiz.fhir.nl.stu3.questionnaires").setVersion("1.0.2").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL).setFetchDependencies(false));
|
||||
|
||||
runInTransaction(() -> {
|
||||
assertTrue(myPackageVersionDao.findByPackageIdAndVersion("nictiz.fhir.nl.stu3.questionnaires", "1.0.2").isPresent());
|
||||
assertFalse(myPackageVersionDao.findByPackageIdAndVersion("nictiz.fhir.nl.stu3.zib2017", "1.3.10").isPresent());
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInstallPackageByUrl_Http() {
|
||||
byte[] bytes;
|
||||
|
||||
bytes = loadResourceAsByteArray("/packages/nictiz.fhir.nl.stu3.questionnaires-1.0.2.tgz");
|
||||
myFakeNpmServlet.getResponses().put("/foo.tgz", bytes);
|
||||
|
||||
igInstaller.install(new PackageInstallationSpec()
|
||||
.setName("nictiz.fhir.nl.stu3.questionnaires")
|
||||
.setVersion("1.0.2")
|
||||
.setPackageUrl("http://localhost:" + myPort + "/foo.tgz")
|
||||
);
|
||||
|
||||
runInTransaction(() -> {
|
||||
assertTrue(myPackageVersionDao.findByPackageIdAndVersion("nictiz.fhir.nl.stu3.questionnaires", "1.0.2").isPresent());
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInstallPackageByUrl_Classpath() {
|
||||
byte[] bytes;
|
||||
|
||||
igInstaller.install(new PackageInstallationSpec()
|
||||
.setName("nictiz.fhir.nl.stu3.questionnaires")
|
||||
.setVersion("1.0.2")
|
||||
.setPackageUrl("classpath:/packages/nictiz.fhir.nl.stu3.questionnaires-1.0.2.tgz")
|
||||
);
|
||||
|
||||
runInTransaction(() -> {
|
||||
assertTrue(myPackageVersionDao.findByPackageIdAndVersion("nictiz.fhir.nl.stu3.questionnaires", "1.0.2").isPresent());
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInstallPackageByUrl_WrongPackageId() {
|
||||
byte[] bytes;
|
||||
|
||||
bytes = loadResourceAsByteArray("/packages/nictiz.fhir.nl.stu3.questionnaires-1.0.2.tgz");
|
||||
myFakeNpmServlet.getResponses().put("/foo.tgz", bytes);
|
||||
|
||||
try {
|
||||
igInstaller.install(new PackageInstallationSpec()
|
||||
.setName("blah")
|
||||
.setVersion("1.0.2")
|
||||
.setPackageUrl("http://localhost:" + myPort + "/foo.tgz")
|
||||
);
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("", e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInstallPackageByUrl_FailingUrl() {
|
||||
try {
|
||||
igInstaller.install(new PackageInstallationSpec()
|
||||
.setName("blah")
|
||||
.setVersion("1.0.2")
|
||||
.setPackageUrl("http://localhost:" + myPort + "/foo.tgz")
|
||||
);
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("", e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void installFromCache2() {
|
||||
igInstaller.install("basisprofil.de", "0.2.40");
|
||||
byte[] bytes = loadResourceAsByteArray("/packages/basisprofil.de.tar.gz");
|
||||
myFakeNpmServlet.getResponses().put("/basisprofil.de/0.2.40", bytes);
|
||||
|
||||
igInstaller.install(new PackageInstallationSpec().setName("basisprofil.de").setVersion("0.2.40").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
package ca.uhn.fhir.jpa.packages;
|
||||
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test;
|
||||
import org.hl7.fhir.utilities.cache.PackageCacheManager;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class IgInstallerTestR4 extends BaseJpaR4Test {
|
||||
|
||||
@Autowired
|
||||
public DaoConfig daoConfig;
|
||||
@Autowired
|
||||
public IgInstallerSvc igInstaller;
|
||||
|
||||
@Before
|
||||
public void before() throws IOException {
|
||||
PackageCacheManager packageCacheManager = new PackageCacheManager(true, 1);
|
||||
InputStream stream;
|
||||
stream = IgInstallerTestDstu3.class.getResourceAsStream("NHSD.Assets.STU3.tar.gz");
|
||||
packageCacheManager.addPackageToCache("NHSD.Assets.STU3", "1.0.0", stream, "NHSD.Assets.STU3");
|
||||
}
|
||||
|
||||
@Test(expected = ImplementationGuideInstallationException.class)
|
||||
public void negativeTestInstallFromCacheVersionMismatch() {
|
||||
daoConfig.setAllowExternalReferences(true);
|
||||
igInstaller.install("NHSD.Assets.STU3", "1.2.0");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package ca.uhn.fhir.jpa.packages;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import org.hl7.fhir.utilities.cache.IPackageCacheManager;
|
||||
import org.hl7.fhir.utilities.cache.NpmPackage;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class JpaPackageCacheTest extends BaseJpaR4Test {
|
||||
|
||||
@Autowired
|
||||
private IPackageCacheManager myPackageCacheManager;
|
||||
|
||||
@Test
|
||||
public void testSavePackage() throws IOException {
|
||||
try (InputStream stream = IgInstallerTestDstu3.class.getResourceAsStream("/packages/basisprofil.de.tar.gz")) {
|
||||
myPackageCacheManager.addPackageToCache("basisprofil.de", "0.2.40", stream, "basisprofil.de");
|
||||
}
|
||||
|
||||
NpmPackage pkg;
|
||||
|
||||
pkg = myPackageCacheManager.loadPackage("basisprofil.de", null);
|
||||
assertEquals("0.2.40", pkg.version());
|
||||
|
||||
pkg = myPackageCacheManager.loadPackage("basisprofil.de", "0.2.40");
|
||||
assertEquals("0.2.40", pkg.version());
|
||||
|
||||
try {
|
||||
myPackageCacheManager.loadPackage("basisprofil.de", "99");
|
||||
fail();
|
||||
} catch (ResourceNotFoundException e) {
|
||||
assertEquals("Unable to locate package basisprofil.de#99", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSavePackageCorrectFhirVersion() {
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,227 @@
|
|||
package ca.uhn.fhir.jpa.packages;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.data.INpmPackageDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.INpmPackageVersionDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.INpmPackageVersionResourceDao;
|
||||
import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test;
|
||||
import ca.uhn.fhir.test.utilities.ProxyUtil;
|
||||
import ca.uhn.fhir.util.JsonUtil;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class NpmSearchTestR4 extends BaseJpaR4Test {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(NpmSearchTestR4.class);
|
||||
@Autowired
|
||||
public IPackageInstallerSvc igInstaller;
|
||||
@Autowired
|
||||
private IHapiPackageCacheManager myPackageCacheManager;
|
||||
@Autowired
|
||||
private NpmJpaValidationSupport myNpmJpaValidationSupport;
|
||||
@Autowired
|
||||
private INpmPackageDao myPackageDao;
|
||||
@Autowired
|
||||
private INpmPackageVersionDao myPackageVersionDao;
|
||||
@Autowired
|
||||
private INpmPackageVersionResourceDao myPackageVersionResourceDao;
|
||||
|
||||
@Before
|
||||
public void before() throws Exception {
|
||||
JpaPackageCache jpaPackageCache = ProxyUtil.getSingletonTarget(myPackageCacheManager, JpaPackageCache.class);
|
||||
jpaPackageCache.getPackageServers().clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearch() throws IOException {
|
||||
PackageInstallationSpec spec;
|
||||
byte[] bytes;
|
||||
|
||||
bytes = loadClasspathBytes("/packages/hl7.fhir.uv.shorthand-0.11.1.tgz");
|
||||
spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.11.1").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY).setPackageContents(bytes);
|
||||
igInstaller.install(spec);
|
||||
|
||||
bytes = loadClasspathBytes("/packages/hl7.fhir.uv.shorthand-0.12.0.tgz");
|
||||
spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.12.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY).setPackageContents(bytes);
|
||||
igInstaller.install(spec);
|
||||
|
||||
bytes = loadClasspathBytes("/packages/nictiz.fhir.nl.stu3.questionnaires-1.0.2.tgz");
|
||||
spec = new PackageInstallationSpec().setName("nictiz.fhir.nl.stu3.questionnaires").setVersion("1.0.2").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY).setPackageContents(bytes);
|
||||
igInstaller.install(spec);
|
||||
|
||||
NpmPackageSearchResultJson search = myPackageCacheManager.search(new PackageSearchSpec());
|
||||
ourLog.info("Search rersults:\r{}", JsonUtil.serialize(search));
|
||||
assertEquals(2, search.getTotal());
|
||||
|
||||
assertEquals(2, search.getObjects().size());
|
||||
assertEquals("hl7.fhir.uv.shorthand", search.getObjects().get(0).getPackage().getName());
|
||||
assertEquals("Describes FHIR Shorthand (FSH), a domain-specific language (DSL) for defining the content of FHIR Implementation Guides (IG). (built Wed, Apr 1, 2020 17:24+0000+00:00)", search.getObjects().get(0).getPackage().getDescription());
|
||||
assertEquals("0.12.0", search.getObjects().get(0).getPackage().getVersion());
|
||||
assertEquals(3115, search.getObjects().get(0).getPackage().getBytes());
|
||||
assertThat(search.getObjects().get(0).getPackage().getFhirVersion().toString(), search.getObjects().get(0).getPackage().getFhirVersion(), Matchers.contains("4.0.1"));
|
||||
|
||||
assertEquals("nictiz.fhir.nl.stu3.questionnaires", search.getObjects().get(1).getPackage().getName());
|
||||
assertEquals("Nictiz NL package of FHIR STU3 conformance resources for MedMij information standard Questionnaires. Includes dependency on Zib2017 and SDC.\\n\\nHCIMs: https://zibs.nl/wiki/HCIM_Release_2017(EN)", search.getObjects().get(1).getPackage().getDescription());
|
||||
assertEquals("1.0.2", search.getObjects().get(1).getPackage().getVersion());
|
||||
assertThat(search.getObjects().get(1).getPackage().getFhirVersion().toString(), search.getObjects().get(0).getPackage().getFhirVersion(), Matchers.contains("4.0.1"));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearchByResourceUrl() throws IOException {
|
||||
PackageInstallationSpec spec;
|
||||
byte[] bytes;
|
||||
|
||||
bytes = loadClasspathBytes("/packages/hl7.fhir.uv.shorthand-0.11.1.tgz");
|
||||
spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.11.1").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY).setPackageContents(bytes);
|
||||
igInstaller.install(spec);
|
||||
|
||||
bytes = loadClasspathBytes("/packages/hl7.fhir.uv.shorthand-0.12.0.tgz");
|
||||
spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.12.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY).setPackageContents(bytes);
|
||||
igInstaller.install(spec);
|
||||
|
||||
PackageSearchSpec searchSpec;
|
||||
NpmPackageSearchResultJson search;
|
||||
|
||||
// Matching URL
|
||||
myCaptureQueriesListener.clear();
|
||||
searchSpec = new PackageSearchSpec();
|
||||
searchSpec.setResourceUrl("http://hl7.org/fhir/uv/shorthand/ImplementationGuide/hl7.fhir.uv.shorthand");
|
||||
search = myPackageCacheManager.search(searchSpec);
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||
|
||||
ourLog.info("Search rersults:\r{}", JsonUtil.serialize(search));
|
||||
assertEquals(1, search.getTotal());
|
||||
assertEquals(1, search.getObjects().size());
|
||||
assertEquals("hl7.fhir.uv.shorthand", search.getObjects().get(0).getPackage().getName());
|
||||
assertEquals("0.12.0", search.getObjects().get(0).getPackage().getVersion());
|
||||
assertEquals("Describes FHIR Shorthand (FSH), a domain-specific language (DSL) for defining the content of FHIR Implementation Guides (IG). (built Wed, Apr 1, 2020 17:24+0000+00:00)", search.getObjects().get(0).getPackage().getDescription());
|
||||
assertThat(search.getObjects().get(0).getPackage().getFhirVersion(), Matchers.contains("4.0.1"));
|
||||
|
||||
// Non Matching URL
|
||||
searchSpec = new PackageSearchSpec();
|
||||
searchSpec.setResourceUrl("http://foo");
|
||||
search = myPackageCacheManager.search(searchSpec);
|
||||
|
||||
ourLog.info("Search rersults:\r{}", JsonUtil.serialize(search));
|
||||
assertEquals(0, search.getTotal());
|
||||
assertEquals(0, search.getObjects().size());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearchByFhirVersion() throws IOException {
|
||||
PackageInstallationSpec spec;
|
||||
byte[] bytes;
|
||||
bytes = loadClasspathBytes("/packages/hl7.fhir.uv.shorthand-0.12.0.tgz");
|
||||
spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.12.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY).setPackageContents(bytes);
|
||||
igInstaller.install(spec);
|
||||
|
||||
PackageSearchSpec searchSpec;
|
||||
NpmPackageSearchResultJson search;
|
||||
|
||||
// Matching by name
|
||||
myCaptureQueriesListener.clear();
|
||||
searchSpec = new PackageSearchSpec();
|
||||
searchSpec.setFhirVersion("R4");
|
||||
search = myPackageCacheManager.search(searchSpec);
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||
|
||||
ourLog.info("Search rersults:\r{}", JsonUtil.serialize(search));
|
||||
assertEquals(1, search.getTotal());
|
||||
assertEquals("hl7.fhir.uv.shorthand", search.getObjects().get(0).getPackage().getName());
|
||||
assertEquals("4.0.1", search.getObjects().get(0).getPackage().getFhirVersion().get(0));
|
||||
|
||||
// Matching FHIR version
|
||||
myCaptureQueriesListener.clear();
|
||||
searchSpec = new PackageSearchSpec();
|
||||
searchSpec.setFhirVersion("4.0.1");
|
||||
search = myPackageCacheManager.search(searchSpec);
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||
|
||||
ourLog.info("Search rersults:\r{}", JsonUtil.serialize(search));
|
||||
assertEquals(1, search.getTotal());
|
||||
assertEquals("hl7.fhir.uv.shorthand", search.getObjects().get(0).getPackage().getName());
|
||||
|
||||
// Partial Matching FHIR version
|
||||
myCaptureQueriesListener.clear();
|
||||
searchSpec = new PackageSearchSpec();
|
||||
searchSpec.setFhirVersion("4.0");
|
||||
search = myPackageCacheManager.search(searchSpec);
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||
|
||||
ourLog.info("Search rersults:\r{}", JsonUtil.serialize(search));
|
||||
assertEquals(1, search.getTotal());
|
||||
assertEquals("hl7.fhir.uv.shorthand", search.getObjects().get(0).getPackage().getName());
|
||||
|
||||
// Non Matching URL
|
||||
searchSpec = new PackageSearchSpec();
|
||||
searchSpec.setResourceUrl("http://foo");
|
||||
search = myPackageCacheManager.search(searchSpec);
|
||||
|
||||
ourLog.info("Search rersults:\r{}", JsonUtil.serialize(search));
|
||||
assertEquals(0, search.getTotal());
|
||||
assertEquals(0, search.getObjects().size());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearchByDescription() throws IOException {
|
||||
PackageInstallationSpec spec;
|
||||
byte[] bytes;
|
||||
|
||||
bytes = loadClasspathBytes("/packages/hl7.fhir.uv.shorthand-0.11.1.tgz");
|
||||
spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.11.1").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY).setPackageContents(bytes);
|
||||
igInstaller.install(spec);
|
||||
|
||||
bytes = loadClasspathBytes("/packages/hl7.fhir.uv.shorthand-0.12.0.tgz");
|
||||
spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.12.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY).setPackageContents(bytes);
|
||||
igInstaller.install(spec);
|
||||
|
||||
PackageSearchSpec searchSpec;
|
||||
NpmPackageSearchResultJson search;
|
||||
|
||||
// Matching URL
|
||||
myCaptureQueriesListener.clear();
|
||||
searchSpec = new PackageSearchSpec();
|
||||
searchSpec.setDescription("shorthand");
|
||||
search = myPackageCacheManager.search(searchSpec);
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||
|
||||
|
||||
runInTransaction(()->{
|
||||
ourLog.info("Versions:\n * {}", myPackageVersionDao.findAll().stream().map(t->t.toString()).collect(Collectors.joining("\n * ")));
|
||||
});
|
||||
|
||||
ourLog.info("Search rersults:\r{}", JsonUtil.serialize(search));
|
||||
assertEquals(1, search.getTotal());
|
||||
assertEquals(1, search.getObjects().size());
|
||||
assertEquals("hl7.fhir.uv.shorthand", search.getObjects().get(0).getPackage().getName());
|
||||
assertEquals("0.12.0", search.getObjects().get(0).getPackage().getVersion());
|
||||
assertEquals("Describes FHIR Shorthand (FSH), a domain-specific language (DSL) for defining the content of FHIR Implementation Guides (IG). (built Wed, Apr 1, 2020 17:24+0000+00:00)", search.getObjects().get(0).getPackage().getDescription());
|
||||
assertThat(search.getObjects().get(0).getPackage().getFhirVersion(), Matchers.contains("4.0.1"));
|
||||
|
||||
// Non Matching URL
|
||||
searchSpec = new PackageSearchSpec();
|
||||
searchSpec.setResourceUrl("http://foo");
|
||||
search = myPackageCacheManager.search(searchSpec);
|
||||
|
||||
ourLog.info("Search rersults:\r{}", JsonUtil.serialize(search));
|
||||
assertEquals(0, search.getTotal());
|
||||
assertEquals(0, search.getObjects().size());
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
package ca.uhn.fhir.jpa.packages;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.dstu3.BaseJpaDstu3Test;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.ValidationModeEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||
import ca.uhn.fhir.test.utilities.JettyUtil;
|
||||
import ca.uhn.fhir.test.utilities.ProxyUtil;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.servlet.ServletHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.hl7.fhir.dstu3.model.CodeSystem;
|
||||
import org.hl7.fhir.dstu3.model.Condition;
|
||||
import org.hl7.fhir.dstu3.model.OperationOutcome;
|
||||
import org.hl7.fhir.dstu3.model.StructureDefinition;
|
||||
import org.hl7.fhir.dstu3.model.ValueSet;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class NpmTestDstu3 extends BaseJpaDstu3Test {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(FakeNpmServlet.class);
|
||||
@Autowired
|
||||
public PackageInstallerSvcImpl igInstaller;
|
||||
@Autowired
|
||||
private IHapiPackageCacheManager myPackageCacheManager;
|
||||
@Autowired
|
||||
private NpmJpaValidationSupport myNpmJpaValidationSupport;
|
||||
|
||||
private Server myServer;
|
||||
private final Map<String, byte[]> myResponses = new HashMap<>();
|
||||
|
||||
@Before
|
||||
public void before() throws Exception {
|
||||
JpaPackageCache jpaPackageCache = ProxyUtil.getSingletonTarget(myPackageCacheManager, JpaPackageCache.class);
|
||||
|
||||
myServer = new Server(0);
|
||||
ServletHandler proxyHandler = new ServletHandler();
|
||||
FakeNpmServlet fakeNpmServlet = new FakeNpmServlet();
|
||||
ServletHolder servletHolder = new ServletHolder(fakeNpmServlet);
|
||||
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
||||
myServer.setHandler(proxyHandler);
|
||||
myServer.start();
|
||||
|
||||
int port = JettyUtil.getPortForStartedServer(myServer);
|
||||
jpaPackageCache.getPackageServers().clear();
|
||||
jpaPackageCache.addPackageServer("http://localhost:" + port);
|
||||
|
||||
myResponses.clear();
|
||||
}
|
||||
|
||||
@After
|
||||
public void after() throws Exception {
|
||||
JettyUtil.closeServer(myServer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void installDstu3Package() throws Exception {
|
||||
byte[] bytes = loadClasspathBytes("/packages/basisprofil.de.tar.gz");
|
||||
myResponses.put("/basisprofil.de/0.2.40", bytes);
|
||||
|
||||
PackageInstallationSpec spec = new PackageInstallationSpec().setName("basisprofil.de").setVersion("0.2.40").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY);
|
||||
igInstaller.install(spec);
|
||||
|
||||
// Be sure no further communication with the server
|
||||
JettyUtil.closeServer(myServer);
|
||||
|
||||
StructureDefinition sd = (StructureDefinition) myNpmJpaValidationSupport.fetchStructureDefinition("http://fhir.de/StructureDefinition/condition-de-basis/0.2");
|
||||
assertEquals("http://fhir.de/StructureDefinition/condition-de-basis/0.2", sd.getUrl());
|
||||
|
||||
ValueSet vs = (ValueSet) myNpmJpaValidationSupport.fetchValueSet("http://fhir.de/ValueSet/ifa/pzn");
|
||||
assertEquals("http://fhir.de/ValueSet/ifa/pzn", vs.getUrl());
|
||||
|
||||
CodeSystem cs = (CodeSystem) myNpmJpaValidationSupport.fetchCodeSystem("http://fhir.de/CodeSystem/deuev/anlage-8-laenderkennzeichen");
|
||||
assertEquals("http://fhir.de/CodeSystem/deuev/anlage-8-laenderkennzeichen", cs.getUrl());
|
||||
|
||||
// Try and validate using a profile from the IG
|
||||
Condition condition = new Condition();
|
||||
condition.setClinicalStatus(Condition.ConditionClinicalStatus.RESOLVED);
|
||||
condition.getMeta().addProfile("http://fhir.de/StructureDefinition/condition-de-basis/0.2");
|
||||
try {
|
||||
myConditionDao.validate(condition, null, null, null, ValidationModeEnum.CREATE, null, mySrd);
|
||||
fail();
|
||||
} catch (PreconditionFailedException e) {
|
||||
ourLog.info("Fail Outcome: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(e.getOperationOutcome()));
|
||||
|
||||
OperationOutcome oo = (OperationOutcome) e.getOperationOutcome();
|
||||
assertEquals("Profile http://fhir.de/StructureDefinition/condition-de-basis/0.2, Element 'Condition.subject': minimum required = 1, but only found 0", oo.getIssueFirstRep().getDiagnostics());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private class FakeNpmServlet extends HttpServlet {
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
|
||||
String requestUrl = req.getRequestURI();
|
||||
if (myResponses.containsKey(requestUrl)) {
|
||||
ourLog.info("Responding to request: {}", requestUrl);
|
||||
|
||||
resp.setStatus(200);
|
||||
resp.setHeader(Constants.HEADER_CONTENT_TYPE, "application/gzip");
|
||||
resp.getOutputStream().write(myResponses.get(requestUrl));
|
||||
resp.getOutputStream().close();
|
||||
} else {
|
||||
ourLog.warn("Unknown request: {}", requestUrl);
|
||||
|
||||
resp.sendError(404);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,474 @@
|
|||
package ca.uhn.fhir.jpa.packages;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.dao.data.INpmPackageDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.INpmPackageVersionDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.INpmPackageVersionResourceDao;
|
||||
import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test;
|
||||
import ca.uhn.fhir.jpa.model.entity.NpmPackageEntity;
|
||||
import ca.uhn.fhir.jpa.model.entity.NpmPackageVersionEntity;
|
||||
import ca.uhn.fhir.jpa.model.entity.NpmPackageVersionResourceEntity;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.param.UriParam;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.test.utilities.JettyUtil;
|
||||
import ca.uhn.fhir.test.utilities.ProxyUtil;
|
||||
import ca.uhn.fhir.util.JsonUtil;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.servlet.ServletHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4.model.ImplementationGuide;
|
||||
import org.hl7.fhir.r4.model.StructureDefinition;
|
||||
import org.hl7.fhir.utilities.cache.NpmPackage;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.hasItem;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class NpmTestR4 extends BaseJpaR4Test {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(NpmTestR4.class);
|
||||
@Autowired
|
||||
public IPackageInstallerSvc igInstaller;
|
||||
@Autowired
|
||||
private IHapiPackageCacheManager myPackageCacheManager;
|
||||
@Autowired
|
||||
private NpmJpaValidationSupport myNpmJpaValidationSupport;
|
||||
private Server myServer;
|
||||
@Autowired
|
||||
private INpmPackageDao myPackageDao;
|
||||
@Autowired
|
||||
private INpmPackageVersionDao myPackageVersionDao;
|
||||
@Autowired
|
||||
private INpmPackageVersionResourceDao myPackageVersionResourceDao;
|
||||
private FakeNpmServlet myFakeNpmServlet;
|
||||
|
||||
@Before
|
||||
public void before() throws Exception {
|
||||
JpaPackageCache jpaPackageCache = ProxyUtil.getSingletonTarget(myPackageCacheManager, JpaPackageCache.class);
|
||||
|
||||
myServer = new Server(0);
|
||||
ServletHandler proxyHandler = new ServletHandler();
|
||||
myFakeNpmServlet = new FakeNpmServlet();
|
||||
ServletHolder servletHolder = new ServletHolder(myFakeNpmServlet);
|
||||
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
||||
myServer.setHandler(proxyHandler);
|
||||
myServer.start();
|
||||
|
||||
int port = JettyUtil.getPortForStartedServer(myServer);
|
||||
jpaPackageCache.getPackageServers().clear();
|
||||
jpaPackageCache.addPackageServer("http://localhost:" + port);
|
||||
|
||||
myFakeNpmServlet.myResponses.clear();
|
||||
}
|
||||
|
||||
@After
|
||||
public void after() throws Exception {
|
||||
JettyUtil.closeServer(myServer);
|
||||
myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCacheDstu3Package() throws Exception {
|
||||
byte[] bytes = loadClasspathBytes("/packages/nictiz.fhir.nl.stu3.questionnaires-1.0.2.tgz");
|
||||
myFakeNpmServlet.myResponses.put("/nictiz.fhir.nl.stu3.questionnaires/1.0.2", bytes);
|
||||
|
||||
PackageInstallationSpec spec = new PackageInstallationSpec().setName("nictiz.fhir.nl.stu3.questionnaires").setVersion("1.0.2").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY);
|
||||
igInstaller.install(spec);
|
||||
|
||||
// Be sure no further communication with the server
|
||||
JettyUtil.closeServer(myServer);
|
||||
|
||||
// Make sure we can fetch the package by ID and Version
|
||||
NpmPackage pkg = myPackageCacheManager.loadPackage("nictiz.fhir.nl.stu3.questionnaires", "1.0.2");
|
||||
assertEquals("Nictiz NL package of FHIR STU3 conformance resources for MedMij information standard Questionnaires. Includes dependency on Zib2017 and SDC.\\n\\nHCIMs: https://zibs.nl/wiki/HCIM_Release_2017(EN)", pkg.description());
|
||||
|
||||
// Make sure we can fetch the package by ID
|
||||
pkg = myPackageCacheManager.loadPackage("nictiz.fhir.nl.stu3.questionnaires", null);
|
||||
assertEquals("1.0.2", pkg.version());
|
||||
assertEquals("Nictiz NL package of FHIR STU3 conformance resources for MedMij information standard Questionnaires. Includes dependency on Zib2017 and SDC.\\n\\nHCIMs: https://zibs.nl/wiki/HCIM_Release_2017(EN)", pkg.description());
|
||||
|
||||
// Fetch resource by URL
|
||||
FhirContext fhirContext = FhirContext.forDstu3();
|
||||
runInTransaction(() -> {
|
||||
IBaseResource asset = myPackageCacheManager.loadPackageAssetByUrl(FhirVersionEnum.DSTU3, "http://nictiz.nl/fhir/StructureDefinition/vl-QuestionnaireResponse");
|
||||
assertThat(fhirContext.newJsonParser().encodeResourceToString(asset), containsString("\"url\":\"http://nictiz.nl/fhir/StructureDefinition/vl-QuestionnaireResponse\",\"version\":\"1.0.1\""));
|
||||
});
|
||||
|
||||
// Fetch resource by URL with version
|
||||
runInTransaction(() -> {
|
||||
IBaseResource asset = myPackageCacheManager.loadPackageAssetByUrl(FhirVersionEnum.DSTU3, "http://nictiz.nl/fhir/StructureDefinition/vl-QuestionnaireResponse|1.0.1");
|
||||
assertThat(fhirContext.newJsonParser().encodeResourceToString(asset), containsString("\"url\":\"http://nictiz.nl/fhir/StructureDefinition/vl-QuestionnaireResponse\",\"version\":\"1.0.1\""));
|
||||
});
|
||||
|
||||
// This was saved but is the wrong version of FHIR for this server
|
||||
assertNull(myNpmJpaValidationSupport.fetchStructureDefinition("http://fhir.de/StructureDefinition/condition-de-basis/0.2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInstallR4Package() throws Exception {
|
||||
myDaoConfig.setAllowExternalReferences(true);
|
||||
|
||||
byte[] bytes = loadClasspathBytes("/packages/hl7.fhir.uv.shorthand-0.12.0.tgz");
|
||||
myFakeNpmServlet.myResponses.put("/hl7.fhir.uv.shorthand/0.12.0", bytes);
|
||||
|
||||
PackageInstallationSpec spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.12.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL);
|
||||
igInstaller.install(spec);
|
||||
|
||||
// Be sure no further communication with the server
|
||||
JettyUtil.closeServer(myServer);
|
||||
|
||||
// Make sure we can fetch the package by ID and Version
|
||||
NpmPackage pkg = myPackageCacheManager.loadPackage("hl7.fhir.uv.shorthand", "0.12.0");
|
||||
assertEquals("Describes FHIR Shorthand (FSH), a domain-specific language (DSL) for defining the content of FHIR Implementation Guides (IG). (built Wed, Apr 1, 2020 17:24+0000+00:00)", pkg.description());
|
||||
|
||||
// Make sure we can fetch the package by ID
|
||||
pkg = myPackageCacheManager.loadPackage("hl7.fhir.uv.shorthand", null);
|
||||
assertEquals("0.12.0", pkg.version());
|
||||
assertEquals("Describes FHIR Shorthand (FSH), a domain-specific language (DSL) for defining the content of FHIR Implementation Guides (IG). (built Wed, Apr 1, 2020 17:24+0000+00:00)", pkg.description());
|
||||
|
||||
// Make sure DB rows were saved
|
||||
runInTransaction(() -> {
|
||||
NpmPackageEntity pkgEntity = myPackageDao.findByPackageId("hl7.fhir.uv.shorthand").orElseThrow(() -> new IllegalArgumentException());
|
||||
assertEquals("hl7.fhir.uv.shorthand", pkgEntity.getPackageId());
|
||||
|
||||
NpmPackageVersionEntity versionEntity = myPackageVersionDao.findByPackageIdAndVersion("hl7.fhir.uv.shorthand", "0.12.0").orElseThrow(() -> new IllegalArgumentException());
|
||||
assertEquals("hl7.fhir.uv.shorthand", versionEntity.getPackageId());
|
||||
assertEquals("0.12.0", versionEntity.getVersionId());
|
||||
assertEquals(3001, versionEntity.getPackageSizeBytes());
|
||||
assertEquals(true, versionEntity.isCurrentVersion());
|
||||
assertEquals("hl7.fhir.uv.shorthand", versionEntity.getName());
|
||||
assertEquals("4.0.1", versionEntity.getFhirVersionId());
|
||||
assertEquals(FhirVersionEnum.R4, versionEntity.getFhirVersion());
|
||||
|
||||
NpmPackageVersionResourceEntity resource = myPackageVersionResourceDao.findCurrentVersionByCanonicalUrl(Pageable.unpaged(), FhirVersionEnum.R4, "http://hl7.org/fhir/uv/shorthand/ImplementationGuide/hl7.fhir.uv.shorthand").getContent().get(0);
|
||||
assertEquals("http://hl7.org/fhir/uv/shorthand/ImplementationGuide/hl7.fhir.uv.shorthand", resource.getCanonicalUrl());
|
||||
assertEquals("0.12.0", resource.getCanonicalVersion());
|
||||
assertEquals("ImplementationGuide-hl7.fhir.uv.shorthand.json", resource.getFilename());
|
||||
assertEquals("4.0.1", resource.getFhirVersionId());
|
||||
assertEquals(FhirVersionEnum.R4, resource.getFhirVersion());
|
||||
assertEquals(6155, resource.getResSizeBytes());
|
||||
});
|
||||
|
||||
// Fetch resource by URL
|
||||
runInTransaction(() -> {
|
||||
IBaseResource asset = myPackageCacheManager.loadPackageAssetByUrl(FhirVersionEnum.R4, "http://hl7.org/fhir/uv/shorthand/ImplementationGuide/hl7.fhir.uv.shorthand");
|
||||
assertThat(myFhirCtx.newJsonParser().encodeResourceToString(asset), containsString("\"url\":\"http://hl7.org/fhir/uv/shorthand/ImplementationGuide/hl7.fhir.uv.shorthand\",\"version\":\"0.12.0\""));
|
||||
});
|
||||
|
||||
// Fetch resource by URL with version
|
||||
runInTransaction(() -> {
|
||||
IBaseResource asset = myPackageCacheManager.loadPackageAssetByUrl(FhirVersionEnum.R4, "http://hl7.org/fhir/uv/shorthand/ImplementationGuide/hl7.fhir.uv.shorthand|0.12.0");
|
||||
assertThat(myFhirCtx.newJsonParser().encodeResourceToString(asset), containsString("\"url\":\"http://hl7.org/fhir/uv/shorthand/ImplementationGuide/hl7.fhir.uv.shorthand\",\"version\":\"0.12.0\""));
|
||||
});
|
||||
|
||||
// Search for the installed resource
|
||||
runInTransaction(() -> {
|
||||
SearchParameterMap map = SearchParameterMap.newSynchronous();
|
||||
map.add(StructureDefinition.SP_URL, new UriParam("http://hl7.org/fhir/uv/shorthand/CodeSystem/shorthand-code-system"));
|
||||
IBundleProvider outcome = myCodeSystemDao.search(map);
|
||||
assertEquals(1, outcome.sizeOrThrowNpe());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testLoadPackageMetadata() throws Exception {
|
||||
myDaoConfig.setAllowExternalReferences(true);
|
||||
|
||||
myFakeNpmServlet.myResponses.put("/hl7.fhir.uv.shorthand/0.12.0", loadClasspathBytes("/packages/hl7.fhir.uv.shorthand-0.12.0.tgz"));
|
||||
myFakeNpmServlet.myResponses.put("/hl7.fhir.uv.shorthand/0.11.1", loadClasspathBytes("/packages/hl7.fhir.uv.shorthand-0.11.1.tgz"));
|
||||
|
||||
PackageInstallationSpec spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.12.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY);
|
||||
igInstaller.install(spec);
|
||||
spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.11.1").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY);
|
||||
igInstaller.install(spec);
|
||||
|
||||
runInTransaction(() -> {
|
||||
NpmPackageMetadataJson metadata = myPackageCacheManager.loadPackageMetadata("hl7.fhir.uv.shorthand");
|
||||
try {
|
||||
ourLog.info(JsonUtil.serialize(metadata));
|
||||
|
||||
assertEquals("0.12.0", metadata.getDistTags().getLatest());
|
||||
|
||||
assertThat(metadata.getVersions().keySet(), contains("0.12.0", "0.11.1"));
|
||||
|
||||
NpmPackageMetadataJson.Version version0120 = metadata.getVersions().get("0.12.0");
|
||||
assertEquals(3001, version0120.getBytes());
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testLoadPackageUsingImpreciseId() throws Exception {
|
||||
myDaoConfig.setAllowExternalReferences(true);
|
||||
|
||||
myFakeNpmServlet.myResponses.put("/hl7.fhir.uv.shorthand/0.12.0", loadClasspathBytes("/packages/hl7.fhir.uv.shorthand-0.12.0.tgz"));
|
||||
myFakeNpmServlet.myResponses.put("/hl7.fhir.uv.shorthand/0.11.1", loadClasspathBytes("/packages/hl7.fhir.uv.shorthand-0.11.1.tgz"));
|
||||
myFakeNpmServlet.myResponses.put("/hl7.fhir.uv.shorthand/0.11.0", loadClasspathBytes("/packages/hl7.fhir.uv.shorthand-0.11.0.tgz"));
|
||||
|
||||
PackageInstallationSpec spec;
|
||||
spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.12.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY);
|
||||
PackageInstallOutcomeJson outcome = igInstaller.install(spec);
|
||||
ourLog.info("Install messages:\n * {}", outcome.getMessage().stream().collect(Collectors.joining("\n * ")));
|
||||
assertThat(outcome.getMessage(), hasItem("Marking package hl7.fhir.uv.shorthand#0.12.0 as current version"));
|
||||
assertThat(outcome.getMessage(), hasItem("Indexing Resource[package/CodeSystem-shorthand-code-system.json] with URL: http://hl7.org/fhir/uv/shorthand/CodeSystem/shorthand-code-system|0.12.0"));
|
||||
|
||||
spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.11.1").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY);
|
||||
outcome = igInstaller.install(spec);
|
||||
ourLog.info("Install messages:\n * {}", outcome.getMessage().stream().collect(Collectors.joining("\n * ")));
|
||||
assertThat(outcome.getMessage(), not(hasItem("Marking package hl7.fhir.uv.shorthand#0.11.1 as current version")));
|
||||
|
||||
spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.11.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY);
|
||||
igInstaller.install(spec);
|
||||
|
||||
|
||||
NpmPackage pkg;
|
||||
|
||||
pkg = myPackageCacheManager.loadPackage("hl7.fhir.uv.shorthand", "0.11.x");
|
||||
assertEquals("0.11.1", pkg.version());
|
||||
|
||||
pkg = myPackageCacheManager.loadPackage("hl7.fhir.uv.shorthand", "0.12.x");
|
||||
assertEquals("0.12.0", pkg.version());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInstallNewerPackageUpdatesLatestVersionFlag() throws Exception {
|
||||
myDaoConfig.setAllowExternalReferences(true);
|
||||
|
||||
byte[] contents0111 = loadClasspathBytes("/packages/hl7.fhir.uv.shorthand-0.11.1.tgz");
|
||||
byte[] contents0120 = loadClasspathBytes("/packages/hl7.fhir.uv.shorthand-0.12.0.tgz");
|
||||
myFakeNpmServlet.myResponses.put("/hl7.fhir.uv.shorthand/0.11.1", contents0111);
|
||||
myFakeNpmServlet.myResponses.put("/hl7.fhir.uv.shorthand/0.12.0", contents0120);
|
||||
|
||||
// Install older version
|
||||
PackageInstallationSpec spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.11.1").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY);
|
||||
igInstaller.install(spec);
|
||||
|
||||
// Older version is current
|
||||
runInTransaction(() -> {
|
||||
NpmPackageVersionEntity versionEntity = myPackageVersionDao.findByPackageIdAndVersion("hl7.fhir.uv.shorthand", "0.11.1").orElseThrow(() -> new IllegalArgumentException());
|
||||
assertEquals(true, versionEntity.isCurrentVersion());
|
||||
});
|
||||
|
||||
// Fetching a resource should return the older version
|
||||
runInTransaction(() -> {
|
||||
ImplementationGuide ig = (ImplementationGuide) myPackageCacheManager.loadPackageAssetByUrl(FhirVersionEnum.R4, "http://hl7.org/fhir/uv/shorthand/ImplementationGuide/hl7.fhir.uv.shorthand");
|
||||
assertEquals("0.11.1", ig.getVersion());
|
||||
});
|
||||
|
||||
// Now install newer version
|
||||
spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.12.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY);
|
||||
igInstaller.install(spec);
|
||||
|
||||
// Newer version is current
|
||||
runInTransaction(() -> {
|
||||
NpmPackageVersionEntity versionEntity = myPackageVersionDao.findByPackageIdAndVersion("hl7.fhir.uv.shorthand", "0.11.1").orElseThrow(() -> new IllegalArgumentException());
|
||||
assertEquals(false, versionEntity.isCurrentVersion());
|
||||
|
||||
versionEntity = myPackageVersionDao.findByPackageIdAndVersion("hl7.fhir.uv.shorthand", "0.12.0").orElseThrow(() -> new IllegalArgumentException());
|
||||
assertEquals(true, versionEntity.isCurrentVersion());
|
||||
});
|
||||
|
||||
// Fetching a resource should return the newer version
|
||||
runInTransaction(() -> {
|
||||
ImplementationGuide ig = (ImplementationGuide) myPackageCacheManager.loadPackageAssetByUrl(FhirVersionEnum.R4, "http://hl7.org/fhir/uv/shorthand/ImplementationGuide/hl7.fhir.uv.shorthand");
|
||||
assertEquals("0.12.0", ig.getVersion());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInstallOlderPackageDoesntUpdateLatestVersionFlag() throws Exception {
|
||||
myDaoConfig.setAllowExternalReferences(true);
|
||||
|
||||
myFakeNpmServlet.myResponses.put("/hl7.fhir.uv.shorthand/0.12.0", loadClasspathBytes("/packages/hl7.fhir.uv.shorthand-0.12.0.tgz"));
|
||||
myFakeNpmServlet.myResponses.put("/hl7.fhir.uv.shorthand/0.11.1", loadClasspathBytes("/packages/hl7.fhir.uv.shorthand-0.11.1.tgz"));
|
||||
|
||||
// Install newer version
|
||||
PackageInstallationSpec spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.12.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY);
|
||||
igInstaller.install(spec);
|
||||
|
||||
runInTransaction(() -> {
|
||||
NpmPackageVersionEntity versionEntity = myPackageVersionDao.findByPackageIdAndVersion("hl7.fhir.uv.shorthand", "0.12.0").orElseThrow(() -> new IllegalArgumentException());
|
||||
assertEquals(true, versionEntity.isCurrentVersion());
|
||||
});
|
||||
|
||||
// Fetching a resource should return the older version
|
||||
runInTransaction(() -> {
|
||||
ImplementationGuide ig = (ImplementationGuide) myPackageCacheManager.loadPackageAssetByUrl(FhirVersionEnum.R4, "http://hl7.org/fhir/uv/shorthand/ImplementationGuide/hl7.fhir.uv.shorthand");
|
||||
assertEquals("0.12.0", ig.getVersion());
|
||||
});
|
||||
|
||||
// Install older version
|
||||
spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.11.1").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY);
|
||||
igInstaller.install(spec);
|
||||
|
||||
// Newer version is still current
|
||||
runInTransaction(() -> {
|
||||
NpmPackageVersionEntity versionEntity = myPackageVersionDao.findByPackageIdAndVersion("hl7.fhir.uv.shorthand", "0.11.1").orElseThrow(() -> new IllegalArgumentException());
|
||||
assertEquals(false, versionEntity.isCurrentVersion());
|
||||
|
||||
versionEntity = myPackageVersionDao.findByPackageIdAndVersion("hl7.fhir.uv.shorthand", "0.12.0").orElseThrow(() -> new IllegalArgumentException());
|
||||
assertEquals(true, versionEntity.isCurrentVersion());
|
||||
});
|
||||
|
||||
// Fetching a resource should return the newer version
|
||||
runInTransaction(() -> {
|
||||
ImplementationGuide ig = (ImplementationGuide) myPackageCacheManager.loadPackageAssetByUrl(FhirVersionEnum.R4, "http://hl7.org/fhir/uv/shorthand/ImplementationGuide/hl7.fhir.uv.shorthand");
|
||||
assertEquals("0.12.0", ig.getVersion());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInstallAlreadyExistingIsIgnored() throws Exception {
|
||||
myDaoConfig.setAllowExternalReferences(true);
|
||||
|
||||
myFakeNpmServlet.myResponses.put("/hl7.fhir.uv.shorthand/0.12.0", loadClasspathBytes("/packages/hl7.fhir.uv.shorthand-0.12.0.tgz"));
|
||||
|
||||
// Install
|
||||
PackageInstallationSpec spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.12.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY);
|
||||
igInstaller.install(spec);
|
||||
|
||||
runInTransaction(() -> {
|
||||
NpmPackageVersionEntity versionEntity = myPackageVersionDao.findByPackageIdAndVersion("hl7.fhir.uv.shorthand", "0.12.0").orElseThrow(() -> new IllegalArgumentException());
|
||||
assertEquals(true, versionEntity.isCurrentVersion());
|
||||
});
|
||||
|
||||
// Install same again
|
||||
spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.12.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY);
|
||||
igInstaller.install(spec);
|
||||
|
||||
runInTransaction(() -> {
|
||||
NpmPackageVersionEntity versionEntity = myPackageVersionDao.findByPackageIdAndVersion("hl7.fhir.uv.shorthand", "0.12.0").orElseThrow(() -> new IllegalArgumentException());
|
||||
assertEquals(true, versionEntity.isCurrentVersion());
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadContents() throws IOException {
|
||||
byte[] contents0111 = loadClasspathBytes("/packages/hl7.fhir.uv.shorthand-0.11.1.tgz");
|
||||
byte[] contents0120 = loadClasspathBytes("/packages/hl7.fhir.uv.shorthand-0.12.0.tgz");
|
||||
|
||||
PackageInstallationSpec spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.11.1").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY).setPackageContents(contents0111);
|
||||
igInstaller.install(spec);
|
||||
spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.12.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY).setPackageContents(contents0120);
|
||||
igInstaller.install(spec);
|
||||
|
||||
|
||||
assertArrayEquals(contents0111, myPackageCacheManager.loadPackageContents("hl7.fhir.uv.shorthand", "0.11.1").getBytes());
|
||||
assertArrayEquals(contents0120, myPackageCacheManager.loadPackageContents("hl7.fhir.uv.shorthand", "0.12.0").getBytes());
|
||||
assertArrayEquals(contents0120, myPackageCacheManager.loadPackageContents("hl7.fhir.uv.shorthand", "latest").getBytes());
|
||||
assertEquals("0.11.1", myPackageCacheManager.loadPackageContents("hl7.fhir.uv.shorthand", "0.11.1").getVersion());
|
||||
assertEquals("0.12.0", myPackageCacheManager.loadPackageContents("hl7.fhir.uv.shorthand", "0.12.0").getVersion());
|
||||
assertEquals("0.12.0", myPackageCacheManager.loadPackageContents("hl7.fhir.uv.shorthand", "latest").getVersion());
|
||||
assertEquals(null, myPackageCacheManager.loadPackageContents("hl7.fhir.uv.shorthand", "1.2.3"));
|
||||
assertEquals(null, myPackageCacheManager.loadPackageContents("foo", "1.2.3"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testDeletePackage() throws IOException {
|
||||
myDaoConfig.setAllowExternalReferences(true);
|
||||
|
||||
myFakeNpmServlet.myResponses.put("/hl7.fhir.uv.shorthand/0.12.0", loadClasspathBytes("/packages/hl7.fhir.uv.shorthand-0.12.0.tgz"));
|
||||
myFakeNpmServlet.myResponses.put("/hl7.fhir.uv.shorthand/0.11.1", loadClasspathBytes("/packages/hl7.fhir.uv.shorthand-0.11.1.tgz"));
|
||||
myFakeNpmServlet.myResponses.put("/hl7.fhir.uv.shorthand/0.11.0", loadClasspathBytes("/packages/hl7.fhir.uv.shorthand-0.11.0.tgz"));
|
||||
|
||||
igInstaller.install(new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.12.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY));
|
||||
igInstaller.install(new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.11.1").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY));
|
||||
igInstaller.install(new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.11.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY));
|
||||
|
||||
runInTransaction(() -> {
|
||||
Slice<NpmPackageVersionResourceEntity> versions = myPackageVersionResourceDao.findCurrentVersionByCanonicalUrl(Pageable.unpaged(), FhirVersionEnum.R4, "http://hl7.org/fhir/uv/shorthand/ValueSet/shorthand-instance-tags");
|
||||
assertEquals(1, versions.getNumberOfElements());
|
||||
NpmPackageVersionResourceEntity resource = versions.getContent().get(0);
|
||||
assertEquals("0.12.0", resource.getCanonicalVersion());
|
||||
});
|
||||
|
||||
myPackageCacheManager.uninstallPackage("hl7.fhir.uv.shorthand", "0.12.0");
|
||||
|
||||
runInTransaction(() -> {
|
||||
Slice<NpmPackageVersionResourceEntity> versions = myPackageVersionResourceDao.findCurrentVersionByCanonicalUrl(Pageable.unpaged(), FhirVersionEnum.R4, "http://hl7.org/fhir/uv/shorthand/ValueSet/shorthand-instance-tags");
|
||||
assertEquals(1, versions.getNumberOfElements());
|
||||
NpmPackageVersionResourceEntity resource = versions.getContent().get(0);
|
||||
assertEquals("0.11.1", resource.getCanonicalVersion());
|
||||
});
|
||||
|
||||
myPackageCacheManager.uninstallPackage("hl7.fhir.uv.shorthand", "0.11.0");
|
||||
|
||||
runInTransaction(() -> {
|
||||
Slice<NpmPackageVersionResourceEntity> versions = myPackageVersionResourceDao.findCurrentVersionByCanonicalUrl(Pageable.unpaged(), FhirVersionEnum.R4, "http://hl7.org/fhir/uv/shorthand/ValueSet/shorthand-instance-tags");
|
||||
assertEquals(1, versions.getNumberOfElements());
|
||||
NpmPackageVersionResourceEntity resource = versions.getContent().get(0);
|
||||
assertEquals("0.11.1", resource.getCanonicalVersion());
|
||||
});
|
||||
|
||||
myPackageCacheManager.uninstallPackage("hl7.fhir.uv.shorthand", "0.11.1");
|
||||
|
||||
runInTransaction(() -> {
|
||||
Slice<NpmPackageVersionResourceEntity> versions = myPackageVersionResourceDao.findCurrentVersionByCanonicalUrl(Pageable.unpaged(), FhirVersionEnum.R4, "http://hl7.org/fhir/uv/shorthand/ValueSet/shorthand-instance-tags");
|
||||
assertEquals(0, versions.getNumberOfElements());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
static class FakeNpmServlet extends HttpServlet {
|
||||
|
||||
private final Map<String, byte[]> myResponses = new HashMap<>();
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
|
||||
String requestUrl = req.getRequestURI();
|
||||
if (myResponses.containsKey(requestUrl)) {
|
||||
ourLog.info("Responding to request: {}", requestUrl);
|
||||
|
||||
resp.setStatus(200);
|
||||
resp.setHeader(Constants.HEADER_CONTENT_TYPE, "application/gzip");
|
||||
resp.getOutputStream().write(myResponses.get(requestUrl));
|
||||
resp.getOutputStream().close();
|
||||
} else {
|
||||
ourLog.warn("Unknown request: {}", requestUrl);
|
||||
|
||||
resp.sendError(404);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Map<String, byte[]> getResponses() {
|
||||
return myResponses;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package ca.uhn.fhir.jpa.packages;
|
||||
|
||||
import ca.uhn.fhir.util.JsonUtil;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class PackageInstallationSpecTest {
|
||||
|
||||
@Test
|
||||
public void testExampleSupplier() throws IOException {
|
||||
PackageInstallationSpec output = new PackageInstallationSpec.ExampleSupplier().get();
|
||||
String json = JsonUtil.serialize(output);
|
||||
assertThat(json, containsString("\"name\" : \"hl7.fhir.us.core\""));
|
||||
|
||||
output = new PackageInstallationSpec.ExampleSupplier2().get();
|
||||
json = JsonUtil.serialize(output);
|
||||
assertThat(json, containsString("\"packageUrl\" : \"classpath:/my-resources.tgz\""));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package ca.uhn.fhir.jpa.packages;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static com.google.common.collect.Lists.newArrayList;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.thymeleaf.util.ListUtils.sort;
|
||||
|
||||
public class PackageVersionComparatorTest {
|
||||
|
||||
private PackageVersionComparator myCmp = new PackageVersionComparator();
|
||||
|
||||
@Test
|
||||
public void testCompareVersion() {
|
||||
assertThat(sort(newArrayList("10.1", "10.2"), myCmp), contains("10.1", "10.2"));
|
||||
assertThat(sort(newArrayList("10.2", "10.1"), myCmp), contains("10.1", "10.2"));
|
||||
assertThat(sort(newArrayList("10.1.2.3", "9.1.2.3"), myCmp), contains("9.1.2.3", "10.1.2.3"));
|
||||
assertThat(sort(newArrayList("9.1.2.3", "10.1.2.3"), myCmp), contains("9.1.2.3", "10.1.2.3"));
|
||||
assertThat(sort(newArrayList("9.1.2.3", "9.1"), myCmp), contains("9.1", "9.1.2.3"));
|
||||
assertThat(sort(newArrayList("9.1", "9.1.2.3"), myCmp), contains("9.1", "9.1.2.3"));
|
||||
assertThat(sort(newArrayList("A", "1"), myCmp), contains("1", "A"));
|
||||
assertThat(sort(newArrayList("1", "A"), myCmp), contains("1", "A"));
|
||||
assertThat(sort(newArrayList("A", "B"), myCmp), contains("A", "B"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsEquivalent() {
|
||||
assertTrue(PackageVersionComparator.isEquivalent("1.2.x", "1.2.3"));
|
||||
assertTrue(PackageVersionComparator.isEquivalent("1.2", "1.2.3"));
|
||||
assertTrue(PackageVersionComparator.isEquivalent("1.2.3", "1.2.3"));
|
||||
assertFalse(PackageVersionComparator.isEquivalent("1.2.4", "1.2.3"));
|
||||
assertFalse(PackageVersionComparator.isEquivalent("1.3", "1.2.3"));
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -68,7 +68,15 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
|
|||
init420(); // 20191015 - 20200217
|
||||
init430(); // Replaced by 5.0.0
|
||||
init500(); // 20200218 - 20200513
|
||||
init501(); // 20200514 - present
|
||||
init501(); // 20200514 - 20200515
|
||||
init510(); // 20200516 - present
|
||||
}
|
||||
|
||||
private void init510() {
|
||||
Builder version = forVersion(VersionEnum.V5_1_0);
|
||||
|
||||
// version.addTableByColumns("20200524.1", "")
|
||||
|
||||
}
|
||||
|
||||
private void init501() { //20200514 - present
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
package ca.uhn.fhir.jpa.model.entity;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR Model
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 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 org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.SequenceGenerator;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
import javax.persistence.Version;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@Entity()
|
||||
@Table(name = "NPM_PACKAGE", uniqueConstraints = {
|
||||
@UniqueConstraint(name = "IDX_PACK_ID", columnNames = "PACKAGE_ID")
|
||||
})
|
||||
public class NpmPackageEntity {
|
||||
|
||||
protected static final int PACKAGE_ID_LENGTH = 200;
|
||||
|
||||
@SequenceGenerator(name = "SEQ_NPM_PACK", sequenceName = "SEQ_NPM_PACK")
|
||||
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_NPM_PACK")
|
||||
@Id
|
||||
@Column(name = "PID")
|
||||
private Long myId;
|
||||
@Column(name = "PACKAGE_ID", length = PACKAGE_ID_LENGTH, nullable = false)
|
||||
private String myPackageId;
|
||||
@Column(name = "CUR_VERSION_ID", length = NpmPackageVersionEntity.VERSION_ID_LENGTH, nullable = true)
|
||||
private String myCurrentVersionId;
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
@Version
|
||||
@Column(name = "UPDATED_TIME", nullable = false)
|
||||
private Date myVersion;
|
||||
@Column(name = "PACKAGE_DESC", length = NpmPackageVersionEntity.VERSION_ID_LENGTH, nullable = true)
|
||||
private String myDescription;
|
||||
@OneToMany(mappedBy = "myPackage")
|
||||
private List<NpmPackageVersionEntity> myVersions;
|
||||
|
||||
public String getDescription() {
|
||||
return myDescription;
|
||||
}
|
||||
|
||||
public void setDescription(String theDescription) {
|
||||
myDescription = theDescription;
|
||||
}
|
||||
|
||||
public String getPackageId() {
|
||||
return myPackageId;
|
||||
}
|
||||
|
||||
public void setPackageId(String thePackageId) {
|
||||
myPackageId = thePackageId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object theO) {
|
||||
if (this == theO) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (theO == null || getClass() != theO.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NpmPackageEntity that = (NpmPackageEntity) theO;
|
||||
|
||||
return new EqualsBuilder()
|
||||
.append(myPackageId, that.myPackageId)
|
||||
.isEquals();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return new HashCodeBuilder(17, 37)
|
||||
.append(myPackageId)
|
||||
.toHashCode();
|
||||
}
|
||||
|
||||
public String getCurrentVersionId() {
|
||||
return myCurrentVersionId;
|
||||
}
|
||||
|
||||
public void setCurrentVersionId(String theCurrentVersionId) {
|
||||
myCurrentVersionId = theCurrentVersionId;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,202 @@
|
|||
package ca.uhn.fhir.jpa.model.entity;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR Model
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 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.util.StringUtil;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.EnumType;
|
||||
import javax.persistence.Enumerated;
|
||||
import javax.persistence.ForeignKey;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.SequenceGenerator;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
import javax.persistence.Version;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@Entity()
|
||||
@Table(name = "NPM_PACKAGE_VER", uniqueConstraints = {
|
||||
}, indexes = {
|
||||
@Index(name = "IDX_PACKVER", columnList = "PACKAGE_ID,VERSION_ID")
|
||||
})
|
||||
public class NpmPackageVersionEntity {
|
||||
|
||||
public static final int VERSION_ID_LENGTH = 200;
|
||||
public static final int FHIR_VERSION_LENGTH = 10;
|
||||
|
||||
@SequenceGenerator(name = "SEQ_NPM_PACKVER", sequenceName = "SEQ_NPM_PACKVER")
|
||||
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_NPM_PACKVER")
|
||||
@Id
|
||||
@Column(name = "PID")
|
||||
private Long myId;
|
||||
@Column(name = "PACKAGE_ID", length = NpmPackageEntity.PACKAGE_ID_LENGTH, nullable = false)
|
||||
private String myPackageId;
|
||||
@Column(name = "VERSION_ID", length = NpmPackageVersionEntity.VERSION_ID_LENGTH, nullable = false)
|
||||
private String myVersionId;
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "PACKAGE_PID", nullable = false, foreignKey = @ForeignKey(name = "FK_NPM_PKV_PKG"))
|
||||
private NpmPackageEntity myPackage;
|
||||
@OneToOne
|
||||
@JoinColumn(name = "BINARY_RES_ID", referencedColumnName = "RES_ID", nullable = false, foreignKey = @ForeignKey(name = "FK_NPM_PKV_RESID"))
|
||||
private ResourceTable myPackageBinary;
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
@Column(name = "SAVED_TIME", nullable = false)
|
||||
private Date mySavedTime;
|
||||
@Column(name = "PKG_DESC", nullable = false, length = 500)
|
||||
private String myDescription;
|
||||
@Column(name = "DESC_UPPER", nullable = false, length = 500)
|
||||
private String myDescriptionUpper;
|
||||
@Column(name = "CURRENT_VERSION", nullable = false)
|
||||
private boolean myCurrentVersion;
|
||||
@Column(name = "FHIR_VERSION_ID", length = NpmPackageVersionEntity.FHIR_VERSION_LENGTH, nullable = false)
|
||||
private String myFhirVersionId;
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "FHIR_VERSION", length = NpmPackageVersionEntity.FHIR_VERSION_LENGTH, nullable = false)
|
||||
private FhirVersionEnum myFhirVersion;
|
||||
@Column(name = "PACKAGE_SIZE_BYTES", nullable = false)
|
||||
private long myPackageSizeBytes;
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
@Version
|
||||
@Column(name = "UPDATED_TIME", nullable = false)
|
||||
private Date myUpdatedTime;
|
||||
@Column(name = "PACKAGE_NAME", nullable = true, length = 200)
|
||||
private String myName;
|
||||
@OneToMany(mappedBy = "myPackageVersion")
|
||||
private List<NpmPackageVersionResourceEntity> myResources;
|
||||
|
||||
public Date getUpdatedTime() {
|
||||
return myUpdatedTime;
|
||||
}
|
||||
|
||||
public long getPackageSizeBytes() {
|
||||
return myPackageSizeBytes;
|
||||
}
|
||||
|
||||
public void setPackageSizeBytes(long thePackageSizeBytes) {
|
||||
myPackageSizeBytes = thePackageSizeBytes;
|
||||
}
|
||||
|
||||
public boolean isCurrentVersion() {
|
||||
return myCurrentVersion;
|
||||
}
|
||||
|
||||
public void setCurrentVersion(boolean theCurrentVersion) {
|
||||
myCurrentVersion = theCurrentVersion;
|
||||
}
|
||||
|
||||
public String getPackageId() {
|
||||
return myPackageId;
|
||||
}
|
||||
|
||||
public void setPackageId(String thePackageId) {
|
||||
myPackageId = thePackageId;
|
||||
}
|
||||
|
||||
public String getVersionId() {
|
||||
return myVersionId;
|
||||
}
|
||||
|
||||
public void setVersionId(String theVersionId) {
|
||||
myVersionId = theVersionId;
|
||||
}
|
||||
|
||||
public String getFhirVersionId() {
|
||||
return myFhirVersionId;
|
||||
}
|
||||
|
||||
public void setFhirVersionId(String theFhirVersionId) {
|
||||
myFhirVersionId = theFhirVersionId;
|
||||
}
|
||||
|
||||
public FhirVersionEnum getFhirVersion() {
|
||||
return myFhirVersion;
|
||||
}
|
||||
|
||||
public void setFhirVersion(FhirVersionEnum theFhirVersion) {
|
||||
myFhirVersion = theFhirVersion;
|
||||
}
|
||||
|
||||
public NpmPackageEntity getPackage() {
|
||||
return myPackage;
|
||||
}
|
||||
|
||||
public void setPackage(NpmPackageEntity thePackage) {
|
||||
myPackage = thePackage;
|
||||
}
|
||||
|
||||
public ResourceTable getPackageBinary() {
|
||||
return myPackageBinary;
|
||||
}
|
||||
|
||||
public void setPackageBinary(ResourceTable thePackageBinary) {
|
||||
myPackageBinary = thePackageBinary;
|
||||
}
|
||||
|
||||
public void setSavedTime(Date theSavedTime) {
|
||||
mySavedTime = theSavedTime;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return myDescription;
|
||||
}
|
||||
|
||||
public void setDescription(String theDescription) {
|
||||
myDescription = theDescription;
|
||||
myDescriptionUpper = StringUtil.normalizeStringForSearchIndexing(theDescription);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
|
||||
.append("myId", myId)
|
||||
.append("myPackageId", myPackageId)
|
||||
.append("myVersionId", myVersionId)
|
||||
.append("myDescriptionUpper", myDescriptionUpper)
|
||||
.append("myFhirVersionId", myFhirVersionId)
|
||||
.toString();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return myName;
|
||||
}
|
||||
|
||||
public void setName(String theName) {
|
||||
myName = theName;
|
||||
}
|
||||
|
||||
public List<NpmPackageVersionResourceEntity> getResources() {
|
||||
return myResources;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,162 @@
|
|||
package ca.uhn.fhir.jpa.model.entity;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR Model
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 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 javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.EnumType;
|
||||
import javax.persistence.Enumerated;
|
||||
import javax.persistence.ForeignKey;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.SequenceGenerator;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
import javax.persistence.Version;
|
||||
import java.util.Date;
|
||||
|
||||
@Entity()
|
||||
@Table(name = "NPM_PACKAGE_VER_RES", uniqueConstraints = {
|
||||
}, indexes = {
|
||||
@Index(name = "IDX_PACKVERRES_URL", columnList = "CANONICAL_URL")
|
||||
})
|
||||
public class NpmPackageVersionResourceEntity {
|
||||
|
||||
public static final int VERSION_ID_LENGTH = 200;
|
||||
|
||||
@Id
|
||||
@SequenceGenerator(name = "SEQ_NPM_PACKVERRES", sequenceName = "SEQ_NPM_PACKVERRES")
|
||||
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_NPM_PACKVERRES")
|
||||
@Column(name = "SP_ID")
|
||||
private Long myId;
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "PACKVER_PID", referencedColumnName = "PID", foreignKey = @ForeignKey(name = "FK_NPM_PACKVERRES_PACKVER"))
|
||||
private NpmPackageVersionEntity myPackageVersion;
|
||||
@OneToOne
|
||||
@JoinColumn(name = "BINARY_RES_ID", referencedColumnName = "RES_ID", nullable = false, foreignKey = @ForeignKey(name = "FK_NPM_PKVR_RESID"))
|
||||
private ResourceTable myResourceBinary;
|
||||
@Column(name = "FILE_DIR", length = 200)
|
||||
private String myDirectory;
|
||||
@Column(name = "FILE_NAME", length = 200)
|
||||
private String myFilename;
|
||||
@Column(name = "RES_TYPE", length = ResourceTable.RESTYPE_LEN)
|
||||
private String myResourceType;
|
||||
@Column(name = "CANONICAL_URL", length = 200)
|
||||
private String myCanonicalUrl;
|
||||
@Column(name = "CANONICAL_VERSION", length = 200)
|
||||
private String myCanonicalVersion;
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "FHIR_VERSION", length = NpmPackageVersionEntity.FHIR_VERSION_LENGTH, nullable = false)
|
||||
private FhirVersionEnum myFhirVersion;
|
||||
@Column(name = "FHIR_VERSION_ID", length = NpmPackageVersionEntity.FHIR_VERSION_LENGTH, nullable = false)
|
||||
private String myFhirVersionId;
|
||||
@Column(name = "RES_SIZE_BYTES", nullable = false)
|
||||
private long myResSizeBytes;
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
@Version
|
||||
@Column(name = "UPDATED_TIME", nullable = false)
|
||||
private Date myVersion;
|
||||
|
||||
public long getResSizeBytes() {
|
||||
return myResSizeBytes;
|
||||
}
|
||||
|
||||
public void setResSizeBytes(long theResSizeBytes) {
|
||||
myResSizeBytes = theResSizeBytes;
|
||||
}
|
||||
|
||||
public String getCanonicalVersion() {
|
||||
return myCanonicalVersion;
|
||||
}
|
||||
|
||||
public void setCanonicalVersion(String theCanonicalVersion) {
|
||||
myCanonicalVersion = theCanonicalVersion;
|
||||
}
|
||||
|
||||
public ResourceTable getResourceBinary() {
|
||||
return myResourceBinary;
|
||||
}
|
||||
|
||||
public void setResourceBinary(ResourceTable theResourceBinary) {
|
||||
myResourceBinary = theResourceBinary;
|
||||
}
|
||||
|
||||
public String getFhirVersionId() {
|
||||
return myFhirVersionId;
|
||||
}
|
||||
|
||||
public void setFhirVersionId(String theFhirVersionId) {
|
||||
myFhirVersionId = theFhirVersionId;
|
||||
}
|
||||
|
||||
public FhirVersionEnum getFhirVersion() {
|
||||
return myFhirVersion;
|
||||
}
|
||||
|
||||
public void setFhirVersion(FhirVersionEnum theFhirVersion) {
|
||||
myFhirVersion = theFhirVersion;
|
||||
}
|
||||
|
||||
public void setPackageVersion(NpmPackageVersionEntity thePackageVersion) {
|
||||
myPackageVersion = thePackageVersion;
|
||||
}
|
||||
|
||||
public String getDirectory() {
|
||||
return myDirectory;
|
||||
}
|
||||
|
||||
public void setDirectory(String theDirectory) {
|
||||
myDirectory = theDirectory;
|
||||
}
|
||||
|
||||
public String getFilename() {
|
||||
return myFilename;
|
||||
}
|
||||
|
||||
public void setFilename(String theFilename) {
|
||||
myFilename = theFilename;
|
||||
}
|
||||
|
||||
public String getResourceType() {
|
||||
return myResourceType;
|
||||
}
|
||||
|
||||
public void setResourceType(String theResourceType) {
|
||||
myResourceType = theResourceType;
|
||||
}
|
||||
|
||||
public String getCanonicalUrl() {
|
||||
return myCanonicalUrl;
|
||||
}
|
||||
|
||||
public void setCanonicalUrl(String theCanonicalUrl) {
|
||||
myCanonicalUrl = theCanonicalUrl;
|
||||
}
|
||||
|
||||
}
|
|
@ -24,7 +24,7 @@ import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
|||
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
import ca.uhn.fhir.util.StringNormalizer;
|
||||
import ca.uhn.fhir.util.StringUtil;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
|
@ -266,7 +266,7 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP
|
|||
return false;
|
||||
}
|
||||
StringParam string = (StringParam) theParam;
|
||||
String normalizedString = StringNormalizer.normalizeStringForSearchIndexing(defaultString(string.getValue()));
|
||||
String normalizedString = StringUtil.normalizeStringForSearchIndexing(defaultString(string.getValue()));
|
||||
return defaultString(getValueNormalized()).startsWith(normalizedString);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
package ca.uhn.fhir.jpa.model.util;
|
||||
|
||||
import ca.uhn.fhir.util.StringNormalizer;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class StringNormalizerTest {
|
||||
@Test
|
||||
public void testNormalizeString() {
|
||||
assertEquals("TEST TEST", StringNormalizer.normalizeStringForSearchIndexing("TEST teSt"));
|
||||
assertEquals("AEIØU", StringNormalizer.normalizeStringForSearchIndexing("åéîøü"));
|
||||
assertEquals("杨浩", StringNormalizer.normalizeStringForSearchIndexing("杨浩"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package ca.uhn.fhir.jpa.model.util;
|
||||
|
||||
import ca.uhn.fhir.util.StringUtil;
|
||||
import com.google.common.base.Charsets;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class StringUtilTest {
|
||||
@Test
|
||||
public void testNormalizeString() {
|
||||
assertEquals("TEST TEST", StringUtil.normalizeStringForSearchIndexing("TEST teSt"));
|
||||
assertEquals("AEIØU", StringUtil.normalizeStringForSearchIndexing("åéîøü"));
|
||||
assertEquals("杨浩", StringUtil.normalizeStringForSearchIndexing("杨浩"));
|
||||
assertEquals(null, StringUtil.normalizeStringForSearchIndexing(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToStringNoBom() {
|
||||
String input = "help i'm a bug";
|
||||
String output = StringUtil.toUtf8String(input.getBytes(Charsets.UTF_8));
|
||||
assertEquals(input, output);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToStringWithBom() throws IOException {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(bos, StandardCharsets.UTF_8));
|
||||
out.write('\ufeff');
|
||||
out.write("help i'm a bug");
|
||||
out.close();
|
||||
|
||||
byte[] bytes = bos.toByteArray();
|
||||
String output = StringUtil.toUtf8String(bytes);
|
||||
assertEquals("help i'm a bug", output);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -43,7 +43,7 @@ import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
|||
import ca.uhn.fhir.model.primitive.BoundCodeDt;
|
||||
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.util.StringNormalizer;
|
||||
import ca.uhn.fhir.util.StringUtil;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
@ -1098,7 +1098,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
|||
}
|
||||
|
||||
String searchParamName = theSearchParam.getName();
|
||||
String valueNormalized = StringNormalizer.normalizeStringForSearchIndexing(value);
|
||||
String valueNormalized = StringUtil.normalizeStringForSearchIndexing(value);
|
||||
if (valueNormalized.length() > ResourceIndexedSearchParamString.MAX_LENGTH) {
|
||||
valueNormalized = valueNormalized.substring(0, ResourceIndexedSearchParamString.MAX_LENGTH);
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ import ca.uhn.fhir.jpa.searchparam.JpaRuntimeSearchParam;
|
|||
import ca.uhn.fhir.jpa.searchparam.SearchParamConstants;
|
||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
||||
import ca.uhn.fhir.util.StringNormalizer;
|
||||
import ca.uhn.fhir.util.StringUtil;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.hamcrest.Matchers;
|
||||
|
@ -74,7 +74,7 @@ public class SearchParamExtractorDstu3Test {
|
|||
String value = IntStream.range(1, 200).mapToObj(v -> "a").collect(Collectors.joining()) + "ئ";
|
||||
assertEquals(value.length(), 200);
|
||||
assertEquals(Normalizer.normalize(value, Normalizer.Form.NFD).length(), 201);
|
||||
assertEquals(StringNormalizer.normalizeStringForSearchIndexing(value).length(), 201);
|
||||
assertEquals(StringUtil.normalizeStringForSearchIndexing(value).length(), 201);
|
||||
|
||||
Questionnaire questionnaire = new Questionnaire();
|
||||
questionnaire.setDescription(value);
|
||||
|
|
|
@ -22,7 +22,7 @@ package ca.uhn.fhir.empi.rules.similarity;
|
|||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.empi.util.NameUtil;
|
||||
import ca.uhn.fhir.util.StringNormalizer;
|
||||
import ca.uhn.fhir.util.StringUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
|
||||
|
@ -56,10 +56,10 @@ public class NameSimilarity implements IEmpiFieldSimilarity {
|
|||
List<String> rightGivenNames = NameUtil.extractGivenNames(theFhirContext, theRightBase);
|
||||
|
||||
if (!exact) {
|
||||
leftFamilyName = StringNormalizer.normalizeStringForSearchIndexing(leftFamilyName);
|
||||
rightFamilyName = StringNormalizer.normalizeStringForSearchIndexing(rightFamilyName);
|
||||
leftGivenNames = leftGivenNames.stream().map(StringNormalizer::normalizeStringForSearchIndexing).collect(Collectors.toList());
|
||||
rightGivenNames = rightGivenNames.stream().map(StringNormalizer::normalizeStringForSearchIndexing).collect(Collectors.toList());
|
||||
leftFamilyName = StringUtil.normalizeStringForSearchIndexing(leftFamilyName);
|
||||
rightFamilyName = StringUtil.normalizeStringForSearchIndexing(rightFamilyName);
|
||||
leftGivenNames = leftGivenNames.stream().map(StringUtil::normalizeStringForSearchIndexing).collect(Collectors.toList());
|
||||
rightGivenNames = rightGivenNames.stream().map(StringUtil::normalizeStringForSearchIndexing).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
for (String leftGivenName : leftGivenNames) {
|
||||
|
|
|
@ -100,6 +100,7 @@ import java.util.concurrent.locks.ReentrantLock;
|
|||
import java.util.jar.Manifest;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static ca.uhn.fhir.util.StringUtil.toUtf8String;
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
|
@ -920,7 +921,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
if (isIgnoreServerParsedRequestParameters()) {
|
||||
String contentType = theRequest.getHeader(Constants.HEADER_CONTENT_TYPE);
|
||||
if (theRequestType == RequestTypeEnum.POST && isNotBlank(contentType) && contentType.startsWith(Constants.CT_X_FORM_URLENCODED)) {
|
||||
String requestBody = new String(requestDetails.loadRequestContents(), Constants.CHARSET_UTF8);
|
||||
String requestBody = toUtf8String(requestDetails.loadRequestContents());
|
||||
params = UrlUtil.parseQueryStrings(theRequest.getQueryString(), requestBody);
|
||||
} else if (theRequestType == RequestTypeEnum.GET) {
|
||||
params = UrlUtil.parseQueryString(theRequest.getQueryString());
|
||||
|
|
|
@ -5,6 +5,7 @@ import ca.uhn.fhir.rest.api.Constants;
|
|||
import ca.uhn.fhir.test.BaseTest;
|
||||
import ca.uhn.fhir.util.StopWatch;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.io.output.NullWriter;
|
||||
|
@ -17,7 +18,9 @@ import org.junit.Test;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
package ca.uhn.fhir.util;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class ResourceUtilTest {
|
||||
|
||||
@Test
|
||||
public void testRemoveNarrative() {
|
||||
Bundle bundle = new Bundle();
|
||||
|
||||
Patient patient = new Patient();
|
||||
patient.getText().getDiv().setValue("<div>help im a bug</div>");
|
||||
bundle.addEntry().setResource(patient);
|
||||
|
||||
Bundle embeddedBundle = new Bundle();
|
||||
embeddedBundle.setType(Bundle.BundleType.COLLECTION);
|
||||
bundle.addEntry().setResource(embeddedBundle);
|
||||
|
||||
ResourceUtil.removeNarrative(FhirContext.forR4(), bundle);
|
||||
|
||||
assertNull(((Patient)bundle.getEntry().get(0).getResource()).getText().getDiv().getValueAsString());
|
||||
}
|
||||
|
||||
}
|
|
@ -2,12 +2,10 @@ package ca.uhn.fhir.tinder;
|
|||
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collection;
|
||||
|
||||
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
||||
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
|
||||
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
|
||||
import ca.uhn.fhir.util.BundleUtil;
|
||||
import ca.uhn.fhir.util.ResourceUtil;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
@ -15,7 +13,6 @@ import org.apache.maven.plugin.*;
|
|||
import org.apache.maven.plugins.annotations.LifecyclePhase;
|
||||
import org.apache.maven.plugins.annotations.Mojo;
|
||||
import org.apache.maven.plugins.annotations.Parameter;
|
||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
|
@ -72,7 +69,7 @@ public class ResourceMinimizerMojo extends AbstractMojo {
|
|||
|
||||
String inputString;
|
||||
try {
|
||||
inputString = IOUtils.toString(new FileInputStream(nextFile), "UTF-8");
|
||||
inputString = IOUtils.toString(new FileInputStream(nextFile), StandardCharsets.UTF_8);
|
||||
} catch (IOException e) {
|
||||
throw new MojoFailureException("Failed to read file: " + nextFile, e);
|
||||
}
|
||||
|
@ -81,18 +78,18 @@ public class ResourceMinimizerMojo extends AbstractMojo {
|
|||
IBaseResource input = parser.parseResource(inputString);
|
||||
|
||||
if (input instanceof IResource) {
|
||||
((IResource) input).getText().getDiv().setValueAsString((String) null);
|
||||
((IResource) input).getText().getStatus().setValueAsString((String) null);
|
||||
((IResource) input).getText().getDiv().setValueAsString(null);
|
||||
((IResource) input).getText().getStatus().setValueAsString(null);
|
||||
if (input instanceof Bundle) {
|
||||
for (Entry nextEntry : ((Bundle) input).getEntry()) {
|
||||
if (nextEntry.getResource() != null) {
|
||||
nextEntry.getResource().getText().getDiv().setValueAsString((String) null);
|
||||
nextEntry.getResource().getText().getStatus().setValueAsString((String) null);
|
||||
nextEntry.getResource().getText().getDiv().setValueAsString(null);
|
||||
nextEntry.getResource().getText().getStatus().setValueAsString(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
minimizeResource((IBaseResource)input);
|
||||
ResourceUtil.removeNarrative(myCtx, input);
|
||||
}
|
||||
|
||||
String outputString = parser.setPrettyPrint(true).encodeResourceToString(input);
|
||||
|
@ -117,7 +114,7 @@ public class ResourceMinimizerMojo extends AbstractMojo {
|
|||
myFileCount++;
|
||||
try {
|
||||
String f = nextFile.getAbsolutePath();
|
||||
Writer w = new OutputStreamWriter(new FileOutputStream(f, false), "UTF-8");
|
||||
Writer w = new OutputStreamWriter(new FileOutputStream(f, false), StandardCharsets.UTF_8);
|
||||
w = new BufferedWriter(w);
|
||||
w.append(outputString);
|
||||
w.close();
|
||||
|
@ -130,20 +127,6 @@ public class ResourceMinimizerMojo extends AbstractMojo {
|
|||
}
|
||||
}
|
||||
|
||||
private void minimizeResource(IBaseResource theInput) {
|
||||
if (theInput instanceof IBaseBundle) {
|
||||
for (IBaseResource next : BundleUtil.toListOfResources(myCtx, (IBaseBundle) theInput)) {
|
||||
minimizeResource(next);
|
||||
}
|
||||
}
|
||||
|
||||
BaseRuntimeElementCompositeDefinition<?> element = (BaseRuntimeElementCompositeDefinition) myCtx.getElementDefinition(theInput.getClass());
|
||||
BaseRuntimeChildDefinition textElement = element.getChildByName("text");
|
||||
if (textElement != null) {
|
||||
textElement.getMutator().setValue(theInput, null);
|
||||
}
|
||||
}
|
||||
|
||||
public long getByteCount() {
|
||||
return myByteCount;
|
||||
}
|
||||
|
|
7
pom.xml
7
pom.xml
|
@ -665,7 +665,7 @@
|
|||
|
||||
<properties>
|
||||
|
||||
<fhir_core_version>5.0.1-SNAPSHOT</fhir_core_version>
|
||||
<fhir_core_version>5.0.7-SNAPSHOT</fhir_core_version>
|
||||
<ucum_version>1.0.2</ucum_version>
|
||||
|
||||
<surefire_jvm_args>-Dfile.encoding=UTF-8 -Xmx2048m</surefire_jvm_args>
|
||||
|
@ -1008,6 +1008,11 @@
|
|||
<artifactId>reflow-velocity-tools</artifactId>
|
||||
<version>2.0.0-beta2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.swagger</groupId>
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
<version>1.6.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
|
|
Loading…
Reference in New Issue