Release 5.6.0 (#3174)
* 3138 externalized binary packages (#3139) * Add test and impl * Add changelog * Fix test * Update hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/3138-support-externalized-binaries.yaml * add beans to test configs * Typo * Update hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/binstore/IBinaryStorageSvc.java Co-authored-by: michaelabuckley <michaelabuckley@gmail.com> * Update hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/binstore/NullBinaryStorageSvcImpl.java Co-authored-by: michaelabuckley <michaelabuckley@gmail.com> Co-authored-by: Kevin Dougan SmileCDR <72025369+KevinDougan-SmileCDR@users.noreply.github.com> Co-authored-by: michaelabuckley <michaelabuckley@gmail.com> * 3131 - Added support for the lookup operation in the Remote Terminology code (#3134) * Remove leading underscores from identifiers (#3146) * Version bump * License files * version.yaml Co-authored-by: Kevin Dougan SmileCDR <72025369+KevinDougan-SmileCDR@users.noreply.github.com> Co-authored-by: michaelabuckley <michaelabuckley@gmail.com>
This commit is contained in:
parent
e3a5aaf298
commit
0a64294467
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
type: add
|
||||||
|
issue: 3131
|
||||||
|
title: "Provided a Remote Terminology Service implementation for the $lookup Operation."
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
type: fix
|
||||||
|
jira: SMILE-3152
|
||||||
|
issue: 3138
|
||||||
|
title: "Previously, the package registry would not work correctly when externalized binary storage was enabled. This has been corrected."
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
---
|
||||||
|
release-date: "2021-11-18"
|
||||||
|
codename: "Raccoon"
|
|
@ -346,7 +346,7 @@ Sort specifications can be passed into handler methods by adding a parameter of
|
||||||
Example URL to invoke this method:
|
Example URL to invoke this method:
|
||||||
|
|
||||||
```url
|
```url
|
||||||
http://fhir.example.com/Patient?_identifier=urn:foo|123&_sort=given
|
http://fhir.example.com/Patient?identifier=urn:foo|123&_sort=given
|
||||||
```
|
```
|
||||||
|
|
||||||
<a name="limiting-results"/>
|
<a name="limiting-results"/>
|
||||||
|
@ -364,7 +364,7 @@ of resources fetched from the database.
|
||||||
Example URL to invoke this method:
|
Example URL to invoke this method:
|
||||||
|
|
||||||
```url
|
```url
|
||||||
http://fhir.example.com/Patient?_identifier=urn:foo|123&_count=10
|
http://fhir.example.com/Patient?identifier=urn:foo|123&_count=10
|
||||||
```
|
```
|
||||||
|
|
||||||
# Paging
|
# Paging
|
||||||
|
@ -388,17 +388,17 @@ for more information.
|
||||||
Example URL to invoke this method for the first page:
|
Example URL to invoke this method for the first page:
|
||||||
|
|
||||||
```url
|
```url
|
||||||
http://fhir.example.com/Patient?_identifier=urn:foo|123&_count=10&_offset=0
|
http://fhir.example.com/Patient?identifier=urn:foo|123&_count=10&_offset=0
|
||||||
```
|
```
|
||||||
or just
|
or just
|
||||||
```url
|
```url
|
||||||
http://fhir.example.com/Patient?_identifier=urn:foo|123&_count=10
|
http://fhir.example.com/Patient?identifier=urn:foo|123&_count=10
|
||||||
```
|
```
|
||||||
|
|
||||||
Example URL to invoke this method for the second page:
|
Example URL to invoke this method for the second page:
|
||||||
|
|
||||||
```url
|
```url
|
||||||
http://fhir.example.com/Patient?_identifier=urn:foo|123&_count=10&_offset=10
|
http://fhir.example.com/Patient?identifier=urn:foo|123&_count=10&_offset=10
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that if the paging provider is configured to be database backed, `_offset=0` behaves differently than no `_offset`. This
|
Note that if the paging provider is configured to be database backed, `_offset=0` behaves differently than no `_offset`. This
|
||||||
|
|
|
@ -20,20 +20,31 @@ package ca.uhn.fhir.jpa.binstore;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.PayloadTooLargeException;
|
import ca.uhn.fhir.rest.server.exceptions.PayloadTooLargeException;
|
||||||
|
import ca.uhn.fhir.util.BinaryUtil;
|
||||||
|
import ca.uhn.fhir.util.HapiExtensions;
|
||||||
import com.google.common.hash.HashFunction;
|
import com.google.common.hash.HashFunction;
|
||||||
import com.google.common.hash.Hashing;
|
import com.google.common.hash.Hashing;
|
||||||
import com.google.common.hash.HashingInputStream;
|
import com.google.common.hash.HashingInputStream;
|
||||||
import com.google.common.io.ByteStreams;
|
import com.google.common.io.ByteStreams;
|
||||||
import org.apache.commons.io.input.CountingInputStream;
|
import org.apache.commons.io.input.CountingInputStream;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseBinary;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseHasExtensions;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
abstract class BaseBinaryStorageSvcImpl implements IBinaryStorageSvc {
|
abstract class BaseBinaryStorageSvcImpl implements IBinaryStorageSvc {
|
||||||
private final SecureRandom myRandom;
|
private final SecureRandom myRandom;
|
||||||
|
@ -41,6 +52,8 @@ abstract class BaseBinaryStorageSvcImpl implements IBinaryStorageSvc {
|
||||||
private final int ID_LENGTH = 100;
|
private final int ID_LENGTH = 100;
|
||||||
private int myMaximumBinarySize = Integer.MAX_VALUE;
|
private int myMaximumBinarySize = Integer.MAX_VALUE;
|
||||||
private int myMinimumBinarySize;
|
private int myMinimumBinarySize;
|
||||||
|
@Autowired
|
||||||
|
private FhirContext myFhirContext;
|
||||||
|
|
||||||
BaseBinaryStorageSvcImpl() {
|
BaseBinaryStorageSvcImpl() {
|
||||||
myRandom = new SecureRandom();
|
myRandom = new SecureRandom();
|
||||||
|
@ -104,7 +117,6 @@ abstract class BaseBinaryStorageSvcImpl implements IBinaryStorageSvc {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
String provideIdForNewBlob(String theBlobIdOrNull) {
|
String provideIdForNewBlob(String theBlobIdOrNull) {
|
||||||
String id = theBlobIdOrNull;
|
String id = theBlobIdOrNull;
|
||||||
if (isBlank(theBlobIdOrNull)) {
|
if (isBlank(theBlobIdOrNull)) {
|
||||||
|
@ -112,4 +124,32 @@ abstract class BaseBinaryStorageSvcImpl implements IBinaryStorageSvc {
|
||||||
}
|
}
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] fetchDataBlobFromBinary(IBaseBinary theBaseBinary) throws IOException {
|
||||||
|
IPrimitiveType<byte[]> dataElement = BinaryUtil.getOrCreateData(myFhirContext, theBaseBinary);
|
||||||
|
byte[] value = dataElement.getValue();
|
||||||
|
if (value == null) {
|
||||||
|
Optional<String> attachmentId = getAttachmentId((IBaseHasExtensions) dataElement);
|
||||||
|
if (attachmentId.isPresent()) {
|
||||||
|
value = fetchBlob(theBaseBinary.getIdElement(), attachmentId.get());
|
||||||
|
} else {
|
||||||
|
throw new InternalErrorException("Unable to load binary blob data for " + theBaseBinary.getIdElement());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private Optional<String> getAttachmentId(IBaseHasExtensions theBaseBinary) {
|
||||||
|
return theBaseBinary
|
||||||
|
.getExtension()
|
||||||
|
.stream()
|
||||||
|
.filter(t -> HapiExtensions.EXT_EXTERNALIZED_BINARY_ID.equals(t.getUrl()))
|
||||||
|
.filter(t -> t.getValue() instanceof IPrimitiveType)
|
||||||
|
.map(t -> (IPrimitiveType<String>) t.getValue())
|
||||||
|
.map(t -> t.getValue())
|
||||||
|
.filter(t -> isNotBlank(t))
|
||||||
|
.findFirst();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@ package ca.uhn.fhir.jpa.binstore;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseBinary;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
@ -101,4 +103,13 @@ public interface IBinaryStorageSvc {
|
||||||
* @return The payload as a byte array
|
* @return The payload as a byte array
|
||||||
*/
|
*/
|
||||||
byte[] fetchBlob(IIdType theResourceId, String theBlobId) throws IOException;
|
byte[] fetchBlob(IIdType theResourceId, String theBlobId) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch the byte[] contents of a given Binary resource's `data` element. If the data is a standard base64encoded string that is embedded, return it.
|
||||||
|
* Otherwise, attempt to load the externalized binary blob via the the externalized binary storage service.
|
||||||
|
*
|
||||||
|
* @param theResourceId The resource ID The ID of the Binary resource you want to extract data bytes from
|
||||||
|
* @return The binary data blob as a byte array
|
||||||
|
*/
|
||||||
|
byte[] fetchDataBlobFromBinary(IBaseBinary theResource) throws IOException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,8 +20,10 @@ package ca.uhn.fhir.jpa.binstore;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseBinary;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
@ -81,4 +83,9 @@ public class NullBinaryStorageSvcImpl implements IBinaryStorageSvc {
|
||||||
public byte[] fetchBlob(IIdType theResourceId, String theBlobId) {
|
public byte[] fetchBlob(IIdType theResourceId, String theBlobId) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] fetchDataBlobFromBinary(IBaseBinary theResource) throws IOException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||||
import ca.uhn.fhir.jpa.api.model.ExpungeOptions;
|
import ca.uhn.fhir.jpa.api.model.ExpungeOptions;
|
||||||
|
import ca.uhn.fhir.jpa.binstore.IBinaryStorageSvc;
|
||||||
import ca.uhn.fhir.jpa.dao.data.INpmPackageDao;
|
import ca.uhn.fhir.jpa.dao.data.INpmPackageDao;
|
||||||
import ca.uhn.fhir.jpa.dao.data.INpmPackageVersionDao;
|
import ca.uhn.fhir.jpa.dao.data.INpmPackageVersionDao;
|
||||||
import ca.uhn.fhir.jpa.dao.data.INpmPackageVersionResourceDao;
|
import ca.uhn.fhir.jpa.dao.data.INpmPackageVersionResourceDao;
|
||||||
|
@ -123,6 +124,9 @@ public class JpaPackageCache extends BasePackageCacheManager implements IHapiPac
|
||||||
@Autowired
|
@Autowired
|
||||||
private PartitionSettings myPartitionSettings;
|
private PartitionSettings myPartitionSettings;
|
||||||
|
|
||||||
|
@Autowired(required = false)//It is possible that some implementers will not create such a bean.
|
||||||
|
private IBinaryStorageSvc myBinaryStorageSvc;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public NpmPackage loadPackageFromCacheOnly(String theId, @Nullable String theVersion) {
|
public NpmPackage loadPackageFromCacheOnly(String theId, @Nullable String theVersion) {
|
||||||
|
@ -172,13 +176,37 @@ public class JpaPackageCache extends BasePackageCacheManager implements IHapiPac
|
||||||
private IHapiPackageCacheManager.PackageContents loadPackageContents(NpmPackageVersionEntity thePackageVersion) {
|
private IHapiPackageCacheManager.PackageContents loadPackageContents(NpmPackageVersionEntity thePackageVersion) {
|
||||||
IFhirResourceDao<? extends IBaseBinary> binaryDao = getBinaryDao();
|
IFhirResourceDao<? extends IBaseBinary> binaryDao = getBinaryDao();
|
||||||
IBaseBinary binary = binaryDao.readByPid(new ResourcePersistentId(thePackageVersion.getPackageBinary().getId()));
|
IBaseBinary binary = binaryDao.readByPid(new ResourcePersistentId(thePackageVersion.getPackageBinary().getId()));
|
||||||
|
try {
|
||||||
|
byte[] content = fetchBlobFromBinary(binary);
|
||||||
PackageContents retVal = new PackageContents()
|
PackageContents retVal = new PackageContents()
|
||||||
.setBytes(binary.getContent())
|
.setBytes(content)
|
||||||
.setPackageId(thePackageVersion.getPackageId())
|
.setPackageId(thePackageVersion.getPackageId())
|
||||||
.setVersion(thePackageVersion.getVersionId())
|
.setVersion(thePackageVersion.getVersionId())
|
||||||
.setLastModified(thePackageVersion.getUpdatedTime());
|
.setLastModified(thePackageVersion.getUpdatedTime());
|
||||||
return retVal;
|
return retVal;
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new InternalErrorException("Failed to load package. There was a problem reading binaries", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method which will attempt to use the IBinaryStorageSvc to resolve the binary blob if available. If
|
||||||
|
* the bean is unavailable, fallback to assuming we are using an embedded base64 in the data element.
|
||||||
|
* @param theBinary the Binary who's `data` blob you want to retrieve
|
||||||
|
* @return a byte array containing the blob.
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
private byte[] fetchBlobFromBinary(IBaseBinary theBinary) throws IOException {
|
||||||
|
if (myBinaryStorageSvc != null) {
|
||||||
|
return myBinaryStorageSvc.fetchDataBlobFromBinary(theBinary);
|
||||||
|
} else {
|
||||||
|
byte[] value = BinaryUtil.getOrCreateData(myCtx, theBinary).getValue();
|
||||||
|
if (value == null) {
|
||||||
|
throw new InternalErrorException("Failed to fetch blob from Binary/" + theBinary.getIdElement());
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
@ -487,12 +515,10 @@ public class JpaPackageCache extends BasePackageCacheManager implements IHapiPac
|
||||||
|
|
||||||
private IBaseResource loadPackageEntity(NpmPackageVersionResourceEntity contents) {
|
private IBaseResource loadPackageEntity(NpmPackageVersionResourceEntity contents) {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
ResourcePersistentId binaryPid = new ResourcePersistentId(contents.getResourceBinary().getId());
|
ResourcePersistentId binaryPid = new ResourcePersistentId(contents.getResourceBinary().getId());
|
||||||
IBaseBinary binary = getBinaryDao().readByPid(binaryPid);
|
IBaseBinary binary = getBinaryDao().readByPid(binaryPid);
|
||||||
byte[] resourceContentsBytes = BinaryUtil.getOrCreateData(myCtx, binary).getValue();
|
byte[] resourceContentsBytes= fetchBlobFromBinary(binary);
|
||||||
String resourceContents = new String(resourceContentsBytes, StandardCharsets.UTF_8);
|
String resourceContents = new String(resourceContentsBytes, StandardCharsets.UTF_8);
|
||||||
|
|
||||||
FhirContext packageContext = getFhirContext(contents.getFhirVersion());
|
FhirContext packageContext = getFhirContext(contents.getFhirVersion());
|
||||||
return EncodingEnum.detectEncoding(resourceContents).newParser(packageContext).parseResource(resourceContents);
|
return EncodingEnum.detectEncoding(resourceContents).newParser(packageContext).parseResource(resourceContents);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|
|
@ -172,6 +172,4 @@ public class TestDstu2Config extends BaseJavaConfigDstu2 {
|
||||||
|
|
||||||
return requestValidator;
|
return requestValidator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package ca.uhn.fhir.jpa.config;
|
package ca.uhn.fhir.jpa.config;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||||
|
import ca.uhn.fhir.jpa.binstore.IBinaryStorageSvc;
|
||||||
|
import ca.uhn.fhir.jpa.binstore.MemoryBinaryStorageSvcImpl;
|
||||||
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
||||||
import ca.uhn.fhir.jpa.subscription.SubscriptionTestUtil;
|
import ca.uhn.fhir.jpa.subscription.SubscriptionTestUtil;
|
||||||
|
@ -70,4 +72,10 @@ public class TestJPAConfig {
|
||||||
public BatchJobHelper batchJobHelper(JobExplorer theJobExplorer) {
|
public BatchJobHelper batchJobHelper(JobExplorer theJobExplorer) {
|
||||||
return new BatchJobHelper(theJobExplorer);
|
return new BatchJobHelper(theJobExplorer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Lazy
|
||||||
|
public IBinaryStorageSvc binaryStorage() {
|
||||||
|
return new MemoryBinaryStorageSvcImpl();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -270,6 +270,78 @@ public class NpmR4Test extends BaseJpaR4Test {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInstallR4PackageWithExternalizedBinaries() throws Exception {
|
||||||
|
myDaoConfig.setAllowExternalReferences(true);
|
||||||
|
|
||||||
|
myInterceptorService.registerInterceptor(myBinaryStorageInterceptor);
|
||||||
|
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);
|
||||||
|
PackageInstallOutcomeJson outcome = myPackageInstallerSvc.install(spec);
|
||||||
|
assertEquals(1, outcome.getResourcesInstalled().get("CodeSystem"));
|
||||||
|
|
||||||
|
// 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.getPackageId());
|
||||||
|
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 result = myCodeSystemDao.search(map);
|
||||||
|
assertEquals(1, result.sizeOrThrowNpe());
|
||||||
|
IBaseResource resource = result.getResources(0, 1).get(0);
|
||||||
|
assertEquals("CodeSystem/shorthand-code-system/_history/1", resource.getIdElement().toString());
|
||||||
|
});
|
||||||
|
|
||||||
|
myInterceptorService.unregisterInterceptor(myBinaryStorageInterceptor);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNumericIdsInstalledWithNpmPrefix() throws Exception {
|
public void testNumericIdsInstalledWithNpmPrefix() throws Exception {
|
||||||
myDaoConfig.setAllowExternalReferences(true);
|
myDaoConfig.setAllowExternalReferences(true);
|
||||||
|
|
|
@ -30,6 +30,7 @@ public class BinaryStorageEntity {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@Column(name = "BLOB_ID", length = 200, nullable = false)
|
@Column(name = "BLOB_ID", length = 200, nullable = false)
|
||||||
|
//N.B GGG: Note that the `blob id` is the same as the `externalized binary id`.
|
||||||
private String myBlobId;
|
private String myBlobId;
|
||||||
@Column(name = "RESOURCE_ID", length = 100, nullable = false)
|
@Column(name = "RESOURCE_ID", length = 100, nullable = false)
|
||||||
private String myResourceId;
|
private String myResourceId;
|
||||||
|
|
|
@ -21,10 +21,13 @@ package ca.uhn.fhir.jpa.config;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||||
|
import ca.uhn.fhir.jpa.binstore.IBinaryStorageSvc;
|
||||||
|
import ca.uhn.fhir.jpa.binstore.MemoryBinaryStorageSvcImpl;
|
||||||
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.context.annotation.Primary;
|
import org.springframework.context.annotation.Primary;
|
||||||
import org.springframework.orm.jpa.JpaTransactionManager;
|
import org.springframework.orm.jpa.JpaTransactionManager;
|
||||||
|
|
||||||
|
@ -43,6 +46,12 @@ public class TestJpaConfig {
|
||||||
return daoConfig().getModelConfig();
|
return daoConfig().getModelConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Lazy
|
||||||
|
public IBinaryStorageSvc binaryStorage() {
|
||||||
|
return new MemoryBinaryStorageSvcImpl();
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@Primary
|
@Primary
|
||||||
public JpaTransactionManager hapiTransactionManager(EntityManagerFactory entityManagerFactory) {
|
public JpaTransactionManager hapiTransactionManager(EntityManagerFactory entityManagerFactory) {
|
||||||
|
|
|
@ -135,10 +135,6 @@ public class TestJpaDstu3Config extends BaseJavaConfigDstu3 {
|
||||||
return requestValidator;
|
return requestValidator;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
|
||||||
public IBinaryStorageSvc binaryStorage() {
|
|
||||||
return new MemoryBinaryStorageSvcImpl();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public DefaultProfileValidationSupport validationSupportChainDstu3() {
|
public DefaultProfileValidationSupport validationSupportChainDstu3() {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.hl7.fhir.common.hapi.validation.support;
|
package org.hl7.fhir.common.hapi.validation.support;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||||
import ca.uhn.fhir.context.support.ConceptValidationOptions;
|
import ca.uhn.fhir.context.support.ConceptValidationOptions;
|
||||||
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
|
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
|
||||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||||
|
@ -9,8 +10,8 @@ import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||||
import ca.uhn.fhir.util.BundleUtil;
|
import ca.uhn.fhir.util.BundleUtil;
|
||||||
import ca.uhn.fhir.util.JsonUtil;
|
import ca.uhn.fhir.util.JsonUtil;
|
||||||
import ca.uhn.fhir.util.ParametersUtil;
|
import ca.uhn.fhir.util.ParametersUtil;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.checkerframework.framework.qual.InvisibleQualifier;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
@ -111,11 +112,9 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
|
||||||
} catch (IOException theE) {
|
} catch (IOException theE) {
|
||||||
ourLog.error("IOException trying to serialize ValueSet to json: " + theE);
|
ourLog.error("IOException trying to serialize ValueSet to json: " + theE);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private String getVersionedCodeSystem(ValueSet.ConceptSetComponent theComponent) {
|
private String getVersionedCodeSystem(ValueSet.ConceptSetComponent theComponent) {
|
||||||
String codeSystem = theComponent.getSystem();
|
String codeSystem = theComponent.getSystem();
|
||||||
if ( ! codeSystem.contains("|") && theComponent.hasVersion()) {
|
if ( ! codeSystem.contains("|") && theComponent.hasVersion()) {
|
||||||
|
@ -124,7 +123,6 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
|
||||||
return codeSystem;
|
return codeSystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IBaseResource fetchCodeSystem(String theSystem) {
|
public IBaseResource fetchCodeSystem(String theSystem) {
|
||||||
IGenericClient client = provideClient();
|
IGenericClient client = provideClient();
|
||||||
|
@ -143,6 +141,167 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theDisplayLanguage) {
|
||||||
|
Validate.notBlank(theCode, "theCode must be provided");
|
||||||
|
|
||||||
|
IGenericClient client = provideClient();
|
||||||
|
FhirContext fhirContext = client.getFhirContext();
|
||||||
|
FhirVersionEnum fhirVersion = fhirContext.getVersion().getVersion();
|
||||||
|
|
||||||
|
switch (fhirVersion) {
|
||||||
|
case DSTU3:
|
||||||
|
case R4:
|
||||||
|
IBaseParameters params = ParametersUtil.newInstance(fhirContext);
|
||||||
|
ParametersUtil.addParameterToParametersString(fhirContext, params, "code", theCode);
|
||||||
|
if (!StringUtils.isEmpty(theSystem)) {
|
||||||
|
ParametersUtil.addParameterToParametersString(fhirContext, params, "system", theSystem);
|
||||||
|
}
|
||||||
|
if (!StringUtils.isEmpty(theDisplayLanguage)) {
|
||||||
|
ParametersUtil.addParameterToParametersString(fhirContext, params, "language", theDisplayLanguage);
|
||||||
|
}
|
||||||
|
Class<?> codeSystemClass = myCtx.getResourceDefinition("CodeSystem").getImplementingClass();
|
||||||
|
IBaseParameters outcome = client
|
||||||
|
.operation()
|
||||||
|
.onType((Class<? extends IBaseResource>) codeSystemClass)
|
||||||
|
.named("$lookup")
|
||||||
|
.withParameters(params)
|
||||||
|
.useHttpGet()
|
||||||
|
.execute();
|
||||||
|
if (outcome != null && !outcome.isEmpty()) {
|
||||||
|
switch (fhirVersion) {
|
||||||
|
case DSTU3:
|
||||||
|
return generateLookupCodeResultDSTU3(theCode, theSystem, (org.hl7.fhir.dstu3.model.Parameters)outcome);
|
||||||
|
case R4:
|
||||||
|
return generateLookupCodeResultR4(theCode, theSystem, (org.hl7.fhir.r4.model.Parameters)outcome);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new UnsupportedOperationException("Unsupported FHIR version '" + fhirVersion.getFhirVersionString() +
|
||||||
|
"'. Only DSTU3 and R4 are supported.");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private LookupCodeResult generateLookupCodeResultDSTU3(String theCode, String theSystem, org.hl7.fhir.dstu3.model.Parameters outcomeDSTU3) {
|
||||||
|
// NOTE: I wanted to put all of this logic into the IValidationSupport Class, but it would've required adding
|
||||||
|
// several new dependencies on version-specific libraries and that is explicitly forbidden (see comment in POM).
|
||||||
|
LookupCodeResult result = new LookupCodeResult();
|
||||||
|
result.setSearchedForCode(theCode);
|
||||||
|
result.setSearchedForSystem(theSystem);
|
||||||
|
result.setFound(true);
|
||||||
|
for (org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent parameterComponent : outcomeDSTU3.getParameter()) {
|
||||||
|
switch (parameterComponent.getName()) {
|
||||||
|
case "property":
|
||||||
|
org.hl7.fhir.dstu3.model.Property part = parameterComponent.getChildByName("part");
|
||||||
|
// The assumption here is that we may only have 2 elements in this part, and if so, these 2 will be saved
|
||||||
|
if (part != null && part.hasValues() && part.getValues().size() >= 2) {
|
||||||
|
String key = ((org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent) part.getValues().get(0)).getValue().toString();
|
||||||
|
String value = ((org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent) part.getValues().get(1)).getValue().toString();
|
||||||
|
if (!StringUtils.isEmpty(key) && !StringUtils.isEmpty(value)) {
|
||||||
|
result.getProperties().add(new StringConceptProperty(key, value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "designation":
|
||||||
|
ConceptDesignation conceptDesignation = new ConceptDesignation();
|
||||||
|
for (org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent designationComponent : parameterComponent.getPart()) {
|
||||||
|
switch(designationComponent.getName()) {
|
||||||
|
case "language":
|
||||||
|
conceptDesignation.setLanguage(designationComponent.getValue().toString());
|
||||||
|
break;
|
||||||
|
case "use":
|
||||||
|
org.hl7.fhir.dstu3.model.Coding coding = (org.hl7.fhir.dstu3.model.Coding)designationComponent.getValue();
|
||||||
|
if (coding != null) {
|
||||||
|
conceptDesignation.setUseSystem(coding.getSystem());
|
||||||
|
conceptDesignation.setUseCode(coding.getCode());
|
||||||
|
conceptDesignation.setUseDisplay(coding.getDisplay());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "value":
|
||||||
|
conceptDesignation.setValue(((designationComponent.getValue() == null)?null:designationComponent.getValue().toString()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.getDesignations().add(conceptDesignation);
|
||||||
|
break;
|
||||||
|
case "name":
|
||||||
|
result.setCodeSystemDisplayName(((parameterComponent.getValue() == null)?null:parameterComponent.getValue().toString()));
|
||||||
|
break;
|
||||||
|
case "version":
|
||||||
|
result.setCodeSystemVersion(((parameterComponent.getValue() == null)?null:parameterComponent.getValue().toString()));
|
||||||
|
break;
|
||||||
|
case "display":
|
||||||
|
result.setCodeDisplay(((parameterComponent.getValue() == null)?null:parameterComponent.getValue().toString()));
|
||||||
|
break;
|
||||||
|
case "abstract":
|
||||||
|
result.setCodeIsAbstract(((parameterComponent.getValue() == null)?false:Boolean.parseBoolean(parameterComponent.getValue().toString())));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private LookupCodeResult generateLookupCodeResultR4(String theCode, String theSystem, org.hl7.fhir.r4.model.Parameters outcomeR4) {
|
||||||
|
// NOTE: I wanted to put all of this logic into the IValidationSupport Class, but it would've required adding
|
||||||
|
// several new dependencies on version-specific libraries and that is explicitly forbidden (see comment in POM).
|
||||||
|
LookupCodeResult result = new LookupCodeResult();
|
||||||
|
result.setSearchedForCode(theCode);
|
||||||
|
result.setSearchedForSystem(theSystem);
|
||||||
|
result.setFound(true);
|
||||||
|
for (org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent parameterComponent : outcomeR4.getParameter()) {
|
||||||
|
switch (parameterComponent.getName()) {
|
||||||
|
case "property":
|
||||||
|
org.hl7.fhir.r4.model.Property part = parameterComponent.getChildByName("part");
|
||||||
|
// The assumption here is that we may only have 2 elements in this part, and if so, these 2 will be saved
|
||||||
|
if (part != null && part.hasValues() && part.getValues().size() >= 2) {
|
||||||
|
String key = ((org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent) part.getValues().get(0)).getValue().toString();
|
||||||
|
String value = ((org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent) part.getValues().get(1)).getValue().toString();
|
||||||
|
if (!StringUtils.isEmpty(key) && !StringUtils.isEmpty(value)) {
|
||||||
|
result.getProperties().add(new StringConceptProperty(key, value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "designation":
|
||||||
|
ConceptDesignation conceptDesignation = new ConceptDesignation();
|
||||||
|
for (org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent designationComponent : parameterComponent.getPart()) {
|
||||||
|
switch(designationComponent.getName()) {
|
||||||
|
case "language":
|
||||||
|
conceptDesignation.setLanguage(designationComponent.getValue().toString());
|
||||||
|
break;
|
||||||
|
case "use":
|
||||||
|
org.hl7.fhir.r4.model.Coding coding = (org.hl7.fhir.r4.model.Coding)designationComponent.getValue();
|
||||||
|
if (coding != null) {
|
||||||
|
conceptDesignation.setUseSystem(coding.getSystem());
|
||||||
|
conceptDesignation.setUseCode(coding.getCode());
|
||||||
|
conceptDesignation.setUseDisplay(coding.getDisplay());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "value":
|
||||||
|
conceptDesignation.setValue(((designationComponent.getValue() == null)?null:designationComponent.getValue().toString()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.getDesignations().add(conceptDesignation);
|
||||||
|
break;
|
||||||
|
case "name":
|
||||||
|
result.setCodeSystemDisplayName(((parameterComponent.getValue() == null)?null:parameterComponent.getValue().toString()));
|
||||||
|
break;
|
||||||
|
case "version":
|
||||||
|
result.setCodeSystemVersion(((parameterComponent.getValue() == null)?null:parameterComponent.getValue().toString()));
|
||||||
|
break;
|
||||||
|
case "display":
|
||||||
|
result.setCodeDisplay(((parameterComponent.getValue() == null)?null:parameterComponent.getValue().toString()));
|
||||||
|
break;
|
||||||
|
case "abstract":
|
||||||
|
result.setCodeIsAbstract(((parameterComponent.getValue() == null)?false:Boolean.parseBoolean(parameterComponent.getValue().toString())));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IBaseResource fetchValueSet(String theValueSetUrl) {
|
public IBaseResource fetchValueSet(String theValueSetUrl) {
|
||||||
IGenericClient client = provideClient();
|
IGenericClient client = provideClient();
|
||||||
|
|
|
@ -3,12 +3,14 @@ package org.hl7.fhir.common.hapi.validation.support;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.support.ConceptValidationOptions;
|
import ca.uhn.fhir.context.support.ConceptValidationOptions;
|
||||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||||
|
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||||
import ca.uhn.fhir.parser.IJsonLikeParser;
|
import ca.uhn.fhir.parser.IJsonLikeParser;
|
||||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||||
import ca.uhn.fhir.rest.annotation.Operation;
|
import ca.uhn.fhir.rest.annotation.Operation;
|
||||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||||
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
||||||
import ca.uhn.fhir.rest.annotation.Search;
|
import ca.uhn.fhir.rest.annotation.Search;
|
||||||
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
import ca.uhn.fhir.rest.client.api.IClientInterceptor;
|
import ca.uhn.fhir.rest.client.api.IClientInterceptor;
|
||||||
import ca.uhn.fhir.rest.client.api.IHttpRequest;
|
import ca.uhn.fhir.rest.client.api.IHttpRequest;
|
||||||
import ca.uhn.fhir.rest.client.api.IHttpResponse;
|
import ca.uhn.fhir.rest.client.api.IHttpResponse;
|
||||||
|
@ -22,12 +24,14 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.r4.model.BooleanType;
|
import org.hl7.fhir.r4.model.BooleanType;
|
||||||
import org.hl7.fhir.r4.model.CodeSystem;
|
import org.hl7.fhir.r4.model.CodeSystem;
|
||||||
import org.hl7.fhir.r4.model.CodeType;
|
import org.hl7.fhir.r4.model.CodeType;
|
||||||
|
import org.hl7.fhir.r4.model.Coding;
|
||||||
import org.hl7.fhir.r4.model.IdType;
|
import org.hl7.fhir.r4.model.IdType;
|
||||||
import org.hl7.fhir.r4.model.Parameters;
|
import org.hl7.fhir.r4.model.Parameters;
|
||||||
import org.hl7.fhir.r4.model.StringType;
|
import org.hl7.fhir.r4.model.StringType;
|
||||||
import org.hl7.fhir.r4.model.UriType;
|
import org.hl7.fhir.r4.model.UriType;
|
||||||
import org.hl7.fhir.r4.model.ValueSet;
|
import org.hl7.fhir.r4.model.ValueSet;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Nested;
|
import org.junit.jupiter.api.Nested;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
@ -44,14 +48,18 @@ import static org.hamcrest.Matchers.lessThan;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
public class RemoteTerminologyServiceValidationSupportTest {
|
public class RemoteTerminologyServiceValidationSupportTest {
|
||||||
|
|
||||||
private static final String DISPLAY = "DISPLAY";
|
private static final String DISPLAY = "DISPLAY";
|
||||||
|
private static final String LANGUAGE = "en";
|
||||||
private static final String CODE_SYSTEM = "CODE_SYS";
|
private static final String CODE_SYSTEM = "CODE_SYS";
|
||||||
|
private static final String CODE_SYSTEM_VERSION = "2.1";
|
||||||
|
private static final String CODE_SYSTEM_VERSION_AS_TEXT = "v2.1.12";
|
||||||
private static final String CODE = "CODE";
|
private static final String CODE = "CODE";
|
||||||
private static final String VALUE_SET_URL = "http://value.set/url";
|
private static final String VALUE_SET_URL = "http://value.set/url";
|
||||||
private static final String ERROR_MESSAGE = "This is an error message";
|
private static final String ERROR_MESSAGE = "This is an error message";
|
||||||
|
|
||||||
private static FhirContext ourCtx = FhirContext.forR4();
|
private static FhirContext ourCtx = FhirContext.forR4();
|
||||||
|
|
||||||
@RegisterExtension
|
@RegisterExtension
|
||||||
|
@ -88,7 +96,50 @@ public class RemoteTerminologyServiceValidationSupportTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testValidateCode_SystemCodeDisplayUrl_Success() {
|
public void testLookupOperation_CodeSystem_Success() {
|
||||||
|
createNextCodeSystemLookupReturnParameters(true, CODE_SYSTEM_VERSION, CODE_SYSTEM_VERSION_AS_TEXT,
|
||||||
|
DISPLAY, null);
|
||||||
|
|
||||||
|
IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(null, CODE_SYSTEM, CODE);
|
||||||
|
assertNotNull(outcome, "Call to lookupCode() should return a non-NULL result!");
|
||||||
|
assertEquals(DISPLAY, outcome.getCodeDisplay());
|
||||||
|
assertEquals(CODE_SYSTEM_VERSION, outcome.getCodeSystemVersion());
|
||||||
|
|
||||||
|
assertEquals(CODE, myCodeSystemProvider.myLastCode.getCode());
|
||||||
|
assertEquals(CODE_SYSTEM, myCodeSystemProvider.myLastUrl.getValueAsString());
|
||||||
|
assertEquals(CODE_SYSTEM_VERSION_AS_TEXT, myCodeSystemProvider.myNextReturnParams.getParameter("name").toString());
|
||||||
|
assertTrue(Boolean.parseBoolean(myCodeSystemProvider.myNextReturnParams.getParameter("result").primitiveValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLookupOperationWithAllParams_CodeSystem_Success() {
|
||||||
|
createNextCodeSystemLookupReturnParameters(true, CODE_SYSTEM_VERSION, CODE_SYSTEM_VERSION_AS_TEXT,
|
||||||
|
DISPLAY, null);
|
||||||
|
addAdditionalReturnParameters();
|
||||||
|
|
||||||
|
IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(null, CODE_SYSTEM, CODE);
|
||||||
|
assertNotNull(outcome, "Call to lookupCode() should return a non-NULL result!");
|
||||||
|
assertEquals(DISPLAY, outcome.getCodeDisplay());
|
||||||
|
assertEquals(CODE_SYSTEM_VERSION, outcome.getCodeSystemVersion());
|
||||||
|
assertEquals(CODE_SYSTEM_VERSION_AS_TEXT, myCodeSystemProvider.myNextReturnParams.getParameter("name").toString());
|
||||||
|
|
||||||
|
assertEquals(CODE, myCodeSystemProvider.myLastCode.getCode());
|
||||||
|
assertEquals(CODE_SYSTEM, myCodeSystemProvider.myLastUrl.getValueAsString());
|
||||||
|
assertTrue(Boolean.parseBoolean(myCodeSystemProvider.myNextReturnParams.getParameter("result").primitiveValue()));
|
||||||
|
|
||||||
|
validateExtraCodeSystemParams();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLookupCode_BlankCode_ThrowsException() {
|
||||||
|
Assertions.assertThrows(IllegalArgumentException.class, () -> {
|
||||||
|
IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(null, CODE_SYSTEM,
|
||||||
|
"", null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateCode_ValueSet_Success() {
|
||||||
createNextValueSetReturnParameters(true, DISPLAY, null);
|
createNextValueSetReturnParameters(true, DISPLAY, null);
|
||||||
|
|
||||||
IValidationSupport.CodeValidationResult outcome = mySvc.validateCode(null, null, CODE_SYSTEM, CODE, DISPLAY, VALUE_SET_URL);
|
IValidationSupport.CodeValidationResult outcome = mySvc.validateCode(null, null, CODE_SYSTEM, CODE, DISPLAY, VALUE_SET_URL);
|
||||||
|
@ -104,6 +155,62 @@ public class RemoteTerminologyServiceValidationSupportTest {
|
||||||
assertEquals(null, myValueSetProvider.myLastValueSet);
|
assertEquals(null, myValueSetProvider.myLastValueSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateCodeWithAllParams_CodeSystem_Success() {
|
||||||
|
createNextCodeSystemReturnParameters(true, DISPLAY, null);
|
||||||
|
addAdditionalReturnParameters();
|
||||||
|
|
||||||
|
IValidationSupport.CodeValidationResult outcome = mySvc.validateCode(null, null, CODE_SYSTEM, CODE, DISPLAY, null);
|
||||||
|
assertEquals(CODE, outcome.getCode());
|
||||||
|
assertEquals(DISPLAY, outcome.getDisplay());
|
||||||
|
assertEquals(null, outcome.getSeverity());
|
||||||
|
assertEquals(null, outcome.getMessage());
|
||||||
|
|
||||||
|
validateExtraCodeSystemParams();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateExtraCodeSystemParams() {
|
||||||
|
assertEquals(CODE, myCodeSystemProvider.myLastCode.getCode());
|
||||||
|
assertEquals(CODE_SYSTEM, myCodeSystemProvider.myLastUrl.getValueAsString());
|
||||||
|
for (Parameters.ParametersParameterComponent param : myCodeSystemProvider.myNextReturnParams.getParameter()) {
|
||||||
|
String paramName = param.getName();
|
||||||
|
if (paramName.equals("result")) {
|
||||||
|
assertEquals(true, ((BooleanType)param.getValue()).booleanValue());
|
||||||
|
} else if (paramName.equals("display")) {
|
||||||
|
assertEquals(DISPLAY, param.getValue().toString());
|
||||||
|
} else if (paramName.equals("property")) {
|
||||||
|
for (Parameters.ParametersParameterComponent propertyComponent : param.getPart()) {
|
||||||
|
switch(propertyComponent.getName()) {
|
||||||
|
case "name":
|
||||||
|
assertEquals("birthDate", propertyComponent.getValue().toString());
|
||||||
|
break;
|
||||||
|
case "value":
|
||||||
|
assertEquals("1930-01-01", propertyComponent.getValue().toString());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (paramName.equals("designation")) {
|
||||||
|
for (Parameters.ParametersParameterComponent designationComponent : param.getPart()) {
|
||||||
|
switch(designationComponent.getName()) {
|
||||||
|
case "language":
|
||||||
|
assertEquals(LANGUAGE, designationComponent.getValue().toString());
|
||||||
|
break;
|
||||||
|
case "use":
|
||||||
|
Coding coding = (Coding)designationComponent.getValue();
|
||||||
|
assertNotNull(coding, "Coding value returned via designation use should NOT be NULL!");
|
||||||
|
assertEquals("code", coding.getCode());
|
||||||
|
assertEquals("system", coding.getSystem());
|
||||||
|
assertEquals("display", coding.getDisplay());
|
||||||
|
break;
|
||||||
|
case "value":
|
||||||
|
assertEquals("some value", designationComponent.getValue().toString());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testValidateCode_SystemCodeDisplayUrl_Error() {
|
public void testValidateCode_SystemCodeDisplayUrl_Error() {
|
||||||
createNextValueSetReturnParameters(false, null, ERROR_MESSAGE);
|
createNextValueSetReturnParameters(false, null, ERROR_MESSAGE);
|
||||||
|
@ -132,7 +239,6 @@ public class RemoteTerminologyServiceValidationSupportTest {
|
||||||
assertEquals(null, outcome.getMessage());
|
assertEquals(null, outcome.getMessage());
|
||||||
|
|
||||||
assertEquals(CODE, myCodeSystemProvider.myLastCode.getCode());
|
assertEquals(CODE, myCodeSystemProvider.myLastCode.getCode());
|
||||||
assertEquals(DISPLAY, myCodeSystemProvider.myLastDisplay.getValue());
|
|
||||||
assertEquals(CODE_SYSTEM, myCodeSystemProvider.myLastUrl.getValueAsString());
|
assertEquals(CODE_SYSTEM, myCodeSystemProvider.myLastUrl.getValueAsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,6 +481,18 @@ public class RemoteTerminologyServiceValidationSupportTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void createNextCodeSystemLookupReturnParameters(boolean theResult, String theVersion, String theVersionAsText,
|
||||||
|
String theDisplay, String theMessage) {
|
||||||
|
myCodeSystemProvider.myNextReturnParams = new Parameters();
|
||||||
|
myCodeSystemProvider.myNextReturnParams.addParameter("result", theResult);
|
||||||
|
myCodeSystemProvider.myNextReturnParams.addParameter("version", theVersion);
|
||||||
|
myCodeSystemProvider.myNextReturnParams.addParameter("name", theVersionAsText);
|
||||||
|
myCodeSystemProvider.myNextReturnParams.addParameter("display", theDisplay);
|
||||||
|
if (theMessage != null) {
|
||||||
|
myCodeSystemProvider.myNextReturnParams.addParameter("message", theMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void createNextValueSetReturnParameters(boolean theResult, String theDisplay, String theMessage) {
|
private void createNextValueSetReturnParameters(boolean theResult, String theDisplay, String theMessage) {
|
||||||
myValueSetProvider.myNextReturnParams = new Parameters();
|
myValueSetProvider.myNextReturnParams = new Parameters();
|
||||||
myValueSetProvider.myNextReturnParams.addParameter("result", theResult);
|
myValueSetProvider.myNextReturnParams.addParameter("result", theResult);
|
||||||
|
@ -384,6 +502,23 @@ public class RemoteTerminologyServiceValidationSupportTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addAdditionalReturnParameters() {
|
||||||
|
// property
|
||||||
|
Parameters.ParametersParameterComponent param = myCodeSystemProvider.myNextReturnParams.addParameter().setName("property");
|
||||||
|
param.addPart().setName("name").setValue(new StringType("birthDate"));
|
||||||
|
param.addPart().setName("value").setValue(new StringType("1930-01-01"));
|
||||||
|
// designation
|
||||||
|
param = myCodeSystemProvider.myNextReturnParams.addParameter().setName("designation");
|
||||||
|
param.addPart().setName("language").setValue(new CodeType("en"));
|
||||||
|
Parameters.ParametersParameterComponent codingParam = param.addPart().setName("use");
|
||||||
|
Coding coding = new Coding();
|
||||||
|
coding.setCode("code");
|
||||||
|
coding.setSystem("system");
|
||||||
|
coding.setDisplay("display");
|
||||||
|
codingParam.setValue(coding);
|
||||||
|
param.addPart().setName("value").setValue(new StringType("some value"));
|
||||||
|
}
|
||||||
|
|
||||||
private static class MyCodeSystemProvider implements IResourceProvider {
|
private static class MyCodeSystemProvider implements IResourceProvider {
|
||||||
|
|
||||||
private UriParam myLastUrlParam;
|
private UriParam myLastUrlParam;
|
||||||
|
@ -391,8 +526,10 @@ public class RemoteTerminologyServiceValidationSupportTest {
|
||||||
private int myInvocationCount;
|
private int myInvocationCount;
|
||||||
private UriType myLastUrl;
|
private UriType myLastUrl;
|
||||||
private CodeType myLastCode;
|
private CodeType myLastCode;
|
||||||
private StringType myLastDisplay;
|
private Coding myLastCoding;
|
||||||
|
private StringType myLastVersion;
|
||||||
private Parameters myNextReturnParams;
|
private Parameters myNextReturnParams;
|
||||||
|
private IValidationSupport.LookupCodeResult myNextLookupCodeResult;
|
||||||
|
|
||||||
@Operation(name = "validate-code", idempotent = true, returnParameters = {
|
@Operation(name = "validate-code", idempotent = true, returnParameters = {
|
||||||
@OperationParam(name = "result", type = BooleanType.class, min = 1),
|
@OperationParam(name = "result", type = BooleanType.class, min = 1),
|
||||||
|
@ -409,11 +546,34 @@ public class RemoteTerminologyServiceValidationSupportTest {
|
||||||
myInvocationCount++;
|
myInvocationCount++;
|
||||||
myLastUrl = theCodeSystemUrl;
|
myLastUrl = theCodeSystemUrl;
|
||||||
myLastCode = theCode;
|
myLastCode = theCode;
|
||||||
myLastDisplay = theDisplay;
|
|
||||||
return myNextReturnParams;
|
return myNextReturnParams;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters= {
|
||||||
|
@OperationParam(name="name", type=StringType.class, min=1),
|
||||||
|
@OperationParam(name="version", type=StringType.class, min=0),
|
||||||
|
@OperationParam(name="display", type=StringType.class, min=1),
|
||||||
|
@OperationParam(name="abstract", type=BooleanType.class, min=1),
|
||||||
|
})
|
||||||
|
public Parameters lookup(
|
||||||
|
HttpServletRequest theServletRequest,
|
||||||
|
@OperationParam(name="code", min=0, max=1) CodeType theCode,
|
||||||
|
@OperationParam(name="system", min=0, max=1) UriType theSystem,
|
||||||
|
@OperationParam(name="coding", min=0, max=1) Coding theCoding,
|
||||||
|
@OperationParam(name="version", min=0, max=1) StringType theVersion,
|
||||||
|
@OperationParam(name="displayLanguage", min=0, max=1) CodeType theDisplayLanguage,
|
||||||
|
@OperationParam(name="property", min = 0, max = OperationParam.MAX_UNLIMITED) List<CodeType> theProperties,
|
||||||
|
RequestDetails theRequestDetails
|
||||||
|
) {
|
||||||
|
myInvocationCount++;
|
||||||
|
myLastCode = theCode;
|
||||||
|
myLastUrl = theSystem;
|
||||||
|
myLastCoding = theCoding;
|
||||||
|
myLastVersion = theVersion;
|
||||||
|
return myNextReturnParams;
|
||||||
|
}
|
||||||
|
|
||||||
@Search
|
@Search
|
||||||
public List<CodeSystem> find(@RequiredParam(name = "url") UriParam theUrlParam) {
|
public List<CodeSystem> find(@RequiredParam(name = "url") UriParam theUrlParam) {
|
||||||
myLastUrlParam = theUrlParam;
|
myLastUrlParam = theUrlParam;
|
||||||
|
@ -429,8 +589,6 @@ public class RemoteTerminologyServiceValidationSupportTest {
|
||||||
|
|
||||||
|
|
||||||
private static class MyValueSetProvider implements IResourceProvider {
|
private static class MyValueSetProvider implements IResourceProvider {
|
||||||
|
|
||||||
|
|
||||||
private Parameters myNextReturnParams;
|
private Parameters myNextReturnParams;
|
||||||
private List<ValueSet> myNextReturnValueSets;
|
private List<ValueSet> myNextReturnValueSets;
|
||||||
private UriType myLastUrl;
|
private UriType myLastUrl;
|
||||||
|
|
|
@ -0,0 +1,214 @@
|
||||||
|
package org.hl7.fhir.dstu3.hapi.validation;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||||
|
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Operation;
|
||||||
|
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||||
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
|
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
||||||
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
|
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
|
||||||
|
import org.hl7.fhir.dstu3.model.BooleanType;
|
||||||
|
import org.hl7.fhir.dstu3.model.CodeSystem;
|
||||||
|
import org.hl7.fhir.dstu3.model.CodeType;
|
||||||
|
import org.hl7.fhir.dstu3.model.Coding;
|
||||||
|
import org.hl7.fhir.dstu3.model.DateType;
|
||||||
|
import org.hl7.fhir.dstu3.model.Parameters;
|
||||||
|
import org.hl7.fhir.dstu3.model.StringType;
|
||||||
|
import org.hl7.fhir.dstu3.model.UriType;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
|
||||||
|
public class RemoteTerminologyServiceValidationSupportDstu3Test {
|
||||||
|
private static final String DISPLAY = "DISPLAY";
|
||||||
|
private static final String LANGUAGE = "en";
|
||||||
|
private static final String CODE_SYSTEM = "CODE_SYS";
|
||||||
|
private static final String CODE_SYSTEM_VERSION = "2.1";
|
||||||
|
private static final String CODE_SYSTEM_VERSION_AS_TEXT = "v2.1.12";
|
||||||
|
private static final String CODE = "CODE";
|
||||||
|
|
||||||
|
private static FhirContext ourCtx = FhirContext.forDstu3();
|
||||||
|
|
||||||
|
@RegisterExtension
|
||||||
|
public RestfulServerExtension myRestfulServerExtension = new RestfulServerExtension(ourCtx);
|
||||||
|
|
||||||
|
private RemoteTerminologyServiceValidationSupport mySvc;
|
||||||
|
private MyCodeSystemProvider myCodeSystemProvider;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void before() {
|
||||||
|
myCodeSystemProvider = new MyCodeSystemProvider();
|
||||||
|
myRestfulServerExtension.getRestfulServer().registerProvider(myCodeSystemProvider);
|
||||||
|
String baseUrl = "http://localhost:" + myRestfulServerExtension.getPort();
|
||||||
|
mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx);
|
||||||
|
mySvc.setBaseUrl(baseUrl);
|
||||||
|
mySvc.addClientInterceptor(new LoggingInterceptor(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLookupOperation_CodeSystem_Success() {
|
||||||
|
createNextCodeSystemLookupReturnParameters(true, CODE_SYSTEM_VERSION, CODE_SYSTEM_VERSION_AS_TEXT, DISPLAY);
|
||||||
|
|
||||||
|
IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(null, CODE_SYSTEM, CODE);
|
||||||
|
assertNotNull(outcome, "Call to lookupCode() should return a non-NULL result!");
|
||||||
|
assertEquals(DISPLAY, outcome.getCodeDisplay());
|
||||||
|
assertEquals(CODE_SYSTEM_VERSION, outcome.getCodeSystemVersion());
|
||||||
|
|
||||||
|
assertEquals(CODE, myCodeSystemProvider.myLastCode.asStringValue());
|
||||||
|
assertEquals(CODE_SYSTEM, myCodeSystemProvider.myLastUrl.getValueAsString());
|
||||||
|
for (Parameters.ParametersParameterComponent param : myCodeSystemProvider.myNextReturnParams.getParameter()) {
|
||||||
|
String paramName = param.getName();
|
||||||
|
if (paramName.equals("result")) {
|
||||||
|
assertEquals(true, ((BooleanType)param.getValue()).booleanValue());
|
||||||
|
} else if (paramName.equals("version")) {
|
||||||
|
assertEquals(CODE_SYSTEM_VERSION, param.getValue().toString());
|
||||||
|
} else if (paramName.equals("display")) {
|
||||||
|
assertEquals(DISPLAY, param.getValue().toString());
|
||||||
|
} else if (paramName.equals("name")) {
|
||||||
|
assertEquals(CODE_SYSTEM_VERSION_AS_TEXT, param.getValue().toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLookupOperationWithAllParams_CodeSystem_Success() {
|
||||||
|
createNextCodeSystemLookupReturnParameters(true, CODE_SYSTEM_VERSION, CODE_SYSTEM_VERSION_AS_TEXT, DISPLAY, LANGUAGE);
|
||||||
|
addAdditionalCodeSystemLookupReturnParameters();
|
||||||
|
|
||||||
|
IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(null, CODE_SYSTEM, CODE, LANGUAGE);
|
||||||
|
assertNotNull(outcome, "Call to lookupCode() should return a non-NULL result!");
|
||||||
|
assertEquals(DISPLAY, outcome.getCodeDisplay());
|
||||||
|
assertEquals(CODE_SYSTEM_VERSION, outcome.getCodeSystemVersion());
|
||||||
|
|
||||||
|
assertEquals(CODE, myCodeSystemProvider.myLastCode.asStringValue());
|
||||||
|
assertEquals(CODE_SYSTEM, myCodeSystemProvider.myLastUrl.getValueAsString());
|
||||||
|
for (Parameters.ParametersParameterComponent param : myCodeSystemProvider.myNextReturnParams.getParameter()) {
|
||||||
|
String paramName = param.getName();
|
||||||
|
if (paramName.equals("result")) {
|
||||||
|
assertEquals(true, ((BooleanType)param.getValue()).booleanValue());
|
||||||
|
} else if (paramName.equals("version")) {
|
||||||
|
assertEquals(CODE_SYSTEM_VERSION, param.getValue().toString());
|
||||||
|
} else if (paramName.equals("display")) {
|
||||||
|
assertEquals(DISPLAY, param.getValue().toString());
|
||||||
|
} else if (paramName.equals("name")) {
|
||||||
|
assertEquals(CODE_SYSTEM_VERSION_AS_TEXT, param.getValue().toString());
|
||||||
|
} else if (paramName.equals("language")) {
|
||||||
|
assertEquals(LANGUAGE, param.getValue().toString());
|
||||||
|
} else if (paramName.equals("property")) {
|
||||||
|
for (org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent propertyComponent : param.getPart()) {
|
||||||
|
switch(propertyComponent.getName()) {
|
||||||
|
case "name":
|
||||||
|
assertEquals("birthDate", propertyComponent.getValue().toString());
|
||||||
|
break;
|
||||||
|
case "value":
|
||||||
|
assertEquals("1930-01-01", ((DateType)propertyComponent.getValue()).asStringValue());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (paramName.equals("designation")) {
|
||||||
|
for (org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent designationComponent : param.getPart()) {
|
||||||
|
switch(designationComponent.getName()) {
|
||||||
|
case "language":
|
||||||
|
assertEquals(LANGUAGE, designationComponent.getValue().toString());
|
||||||
|
break;
|
||||||
|
case "use":
|
||||||
|
Coding coding = (Coding)designationComponent.getValue();
|
||||||
|
assertNotNull(coding, "Coding value returned via designation use should NOT be NULL!");
|
||||||
|
assertEquals("code", coding.getCode());
|
||||||
|
assertEquals("system", coding.getSystem());
|
||||||
|
assertEquals("display", coding.getDisplay());
|
||||||
|
break;
|
||||||
|
case "value":
|
||||||
|
assertEquals("some value", designationComponent.getValue().toString());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createNextCodeSystemLookupReturnParameters(boolean theResult, String theVersion, String theVersionAsText,
|
||||||
|
String theDisplay) {
|
||||||
|
createNextCodeSystemLookupReturnParameters(theResult, theVersion, theVersionAsText, theDisplay, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createNextCodeSystemLookupReturnParameters(boolean theResult, String theVersion, String theVersionAsText,
|
||||||
|
String theDisplay, String theLanguage) {
|
||||||
|
myCodeSystemProvider.myNextReturnParams = new Parameters();
|
||||||
|
myCodeSystemProvider.myNextReturnParams.addParameter().setName("result").setValue(new BooleanType(theResult));
|
||||||
|
myCodeSystemProvider.myNextReturnParams.addParameter().setName("version").setValue(new StringType(theVersion));
|
||||||
|
myCodeSystemProvider.myNextReturnParams.addParameter().setName("name").setValue(new StringType(theVersionAsText));
|
||||||
|
myCodeSystemProvider.myNextReturnParams.addParameter().setName("display").setValue(new StringType(theDisplay));
|
||||||
|
if (!StringUtils.isBlank(theLanguage)) {
|
||||||
|
myCodeSystemProvider.myNextReturnParams.addParameter().setName("language").setValue(new StringType(theLanguage));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addAdditionalCodeSystemLookupReturnParameters() {
|
||||||
|
// property
|
||||||
|
Parameters.ParametersParameterComponent param = myCodeSystemProvider.myNextReturnParams.addParameter().setName("property");
|
||||||
|
param.addPart().setName("name").setValue(new StringType("birthDate"));
|
||||||
|
param.addPart().setName("value").setValue(new DateType("1930-01-01"));
|
||||||
|
// designation
|
||||||
|
param = myCodeSystemProvider.myNextReturnParams.addParameter().setName("designation");
|
||||||
|
param.addPart().setName("language").setValue(new CodeType("en"));
|
||||||
|
Parameters.ParametersParameterComponent codingParam = param.addPart().setName("use");
|
||||||
|
Coding coding = new Coding();
|
||||||
|
coding.setCode("code");
|
||||||
|
coding.setSystem("system");
|
||||||
|
coding.setDisplay("display");
|
||||||
|
codingParam.setValue(coding);
|
||||||
|
param.addPart().setName("value").setValue(new StringType("some value"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class MyCodeSystemProvider implements IResourceProvider {
|
||||||
|
private int myInvocationCount;
|
||||||
|
private UriType myLastUrl;
|
||||||
|
private CodeType myLastCode;
|
||||||
|
private Coding myLastCoding;
|
||||||
|
private StringType myLastVersion;
|
||||||
|
private CodeType myLastDisplayLanguage;
|
||||||
|
private Parameters myNextReturnParams;
|
||||||
|
|
||||||
|
@Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters= {
|
||||||
|
@OperationParam(name="name", type=StringType.class, min=1),
|
||||||
|
@OperationParam(name="version", type=StringType.class, min=0),
|
||||||
|
@OperationParam(name="display", type=StringType.class, min=1),
|
||||||
|
@OperationParam(name="abstract", type=BooleanType.class, min=1),
|
||||||
|
})
|
||||||
|
public Parameters lookup(
|
||||||
|
HttpServletRequest theServletRequest,
|
||||||
|
@OperationParam(name="code", min=0, max=1) CodeType theCode,
|
||||||
|
@OperationParam(name="system", min=0, max=1) UriType theSystem,
|
||||||
|
@OperationParam(name="coding", min=0, max=1) Coding theCoding,
|
||||||
|
@OperationParam(name="version", min=0, max=1) StringType theVersion,
|
||||||
|
@OperationParam(name="displayLanguage", min=0, max=1) CodeType theDisplayLanguage,
|
||||||
|
@OperationParam(name="property", min = 0, max = OperationParam.MAX_UNLIMITED) List<CodeType> theProperties,
|
||||||
|
RequestDetails theRequestDetails
|
||||||
|
) {
|
||||||
|
myInvocationCount++;
|
||||||
|
myLastCode = theCode;
|
||||||
|
myLastUrl = theSystem;
|
||||||
|
myLastCoding = theCoding;
|
||||||
|
myLastVersion = theVersion;
|
||||||
|
myLastDisplayLanguage = theDisplayLanguage;
|
||||||
|
return myNextReturnParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends IBaseResource> getResourceType() {
|
||||||
|
return CodeSystem.class;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package org.hl7.fhir.r5.validation;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||||
|
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
||||||
|
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
||||||
|
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
|
||||||
|
public class RemoteTerminologyServiceValidationSupportR5Test {
|
||||||
|
private static final String ANY_NONBLANK_VALUE = "anything";
|
||||||
|
private static FhirContext ourCtx = FhirContext.forR5();
|
||||||
|
@RegisterExtension
|
||||||
|
public RestfulServerExtension myRestfulServerExtension = new RestfulServerExtension(ourCtx);
|
||||||
|
|
||||||
|
private RemoteTerminologyServiceValidationSupport mySvc;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void before() {
|
||||||
|
String baseUrl = "http://localhost:" + myRestfulServerExtension.getPort();
|
||||||
|
mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx);
|
||||||
|
mySvc.setBaseUrl(baseUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLookupCode_R5_ThrowsException() {
|
||||||
|
Assertions.assertThrows(UnsupportedOperationException.class, () -> {
|
||||||
|
IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(
|
||||||
|
new ValidationSupportContext(FhirContext.forR5().getValidationSupport()), ANY_NONBLANK_VALUE, ANY_NONBLANK_VALUE);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue