3764 Fix NullPointer on validator meta extension (#3910)
* Failing test * Failing test * Add necessary package load to test * WIP add minimum code for passing test * Use PrePopulatedValidationSupport binaryMap in NpmPackage * Rename test * Add test for non-CachingValidationSupport * Rename Tests + InvalidExtensionValue test * Revert change to ValidateDirectory * Gentle reformat * Fix typo * Test CachingValidationSupport fetchBinary * Test for NpmPackageValidationSupport * Refactor * Check for nulls when adding Binary * Test PrePopulatedValidationSupport addBinary * Test ValidationSupportChain * Move ValidationSupportChainTest to proper package * Remove FIXME in VersionSpecificWorkerContextWrapper * Test VersionSpecificWorkerContextWrapper * Add example in docs Co-authored-by: dotasek <david.otasek@smilecdr.com>
This commit is contained in:
parent
64e1f4d381
commit
dad1e420fa
|
@ -255,6 +255,13 @@ public interface IValidationSupport {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the given binary data by key.
|
||||
* @param binaryKey
|
||||
* @return
|
||||
*/
|
||||
default byte[] fetchBinary(String binaryKey) { return null; }
|
||||
|
||||
/**
|
||||
* Validates that the given code exists and if possible returns a display
|
||||
* name. This method is called to check codes which are found in "example"
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
The following code can be used to generate a Snapshot Profile (StructureDefinition) when all you have is a differential.
|
||||
|
||||
```java
|
||||
// Create a validation chain that includes default validation support and a
|
||||
// snapshot generator
|
||||
// Create a validation support chain that includes default validation support
|
||||
// and a snapshot generator
|
||||
DefaultProfileValidationSupport defaultSupport = new DefaultProfileValidationSupport();
|
||||
SnapshotGeneratingValidationSupport snapshotGenerator = new SnapshotGeneratingValidationSupport(myFhirCtx, defaultSupport);
|
||||
ValidationSupportChain chain = new ValidationSupportChain(defaultSupport, snapshotGenerator);
|
||||
|
@ -16,4 +16,28 @@ ValidationSupportChain chain = new ValidationSupportChain(defaultSupport, snapsh
|
|||
StructureDefinition snapshot = chain.generateSnapshot(differential, "http://foo", null, "THE BEST PROFILE");
|
||||
```
|
||||
|
||||
## Validate a Resource with Cross Version Extensions
|
||||
|
||||
The following code can be used to validate a resource using FHIR [Cross Version Extensions](http://hl7.org/fhir/versions.html#extensions).
|
||||
|
||||
Note that you must have the [hl7.fhir.xver-extensions-0.0.11.tgz](http://fhir.org/packages/hl7.fhir.xver-extensions/0.0.11/package.tgz) package available in your classpath.
|
||||
|
||||
```java
|
||||
// Create a validation support chain that includes default validation support
|
||||
// and support from the hl7.fhir.xver-extensions NPM pacakage.
|
||||
NpmPackageValidationSupport npmPackageSupport = new NpmPackageValidationSupport(myFhirCtx);
|
||||
npmPackageSupport.loadPackageFromClasspath("classpath:package/hl7.fhir.xver-extensions-0.0.11.tgz");
|
||||
|
||||
myFhirCtx.setValidationSupport(new ValidationSupportChain(
|
||||
new DefaultProfileValidationSupport(myFhirCtx),
|
||||
npmPackageSupport
|
||||
));
|
||||
|
||||
FhirInstanceValidator instanceValidator = new FhirInstanceValidator(myFhirCtx);
|
||||
|
||||
FhirValidator validator = myFhirCtx.newValidator();
|
||||
validator.registerValidatorModule(instanceValidator);
|
||||
|
||||
// Validate theResource
|
||||
ValidationResult validationResult = validator.validateWithResult(theResource);
|
||||
```
|
||||
|
|
|
@ -56,6 +56,11 @@ public class BaseValidationSupportWrapper extends BaseValidationSupport {
|
|||
return myWrap.fetchResource(theClass, theUri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] fetchBinary(String theBinaryKey) {
|
||||
return myWrap.fetchBinary(theBinaryKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCodeSystemSupported(ValidationSupportContext theValidationSupportContext, String theSystem) {
|
||||
return myWrap.isCodeSystemSupported(theValidationSupportContext, theSystem);
|
||||
|
|
|
@ -47,7 +47,7 @@ public class CachingValidationSupport extends BaseValidationSupportWrapper imple
|
|||
private final Cache<String, Object> myExpandValueSetCache;
|
||||
|
||||
/**
|
||||
* Constuctor with default timeouts
|
||||
* Constructor with default timeouts
|
||||
*
|
||||
* @param theWrap The validation support module to wrap
|
||||
*/
|
||||
|
@ -140,6 +140,11 @@ public class CachingValidationSupport extends BaseValidationSupportWrapper imple
|
|||
return loadFromCache(myCache, "fetchStructureDefinition " + theUrl, t -> super.fetchStructureDefinition(theUrl));
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] fetchBinary(String theBinaryKey) {
|
||||
return loadFromCache(myCache, "fetchBinary " + theBinaryKey, t -> super.fetchBinary(theBinaryKey));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends IBaseResource> T fetchResource(@Nullable Class<T> theClass, String theUri) {
|
||||
return loadFromCache(myCache, "fetchResource " + theClass + " " + theUri,
|
||||
|
|
|
@ -4,13 +4,14 @@ import ca.uhn.fhir.context.FhirContext;
|
|||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.util.ClasspathUtil;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4.model.ValueSet;
|
||||
import org.hl7.fhir.utilities.TextFile;
|
||||
import org.hl7.fhir.utilities.npm.NpmPackage;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
|
@ -38,7 +39,14 @@ public class NpmPackageValidationSupport extends PrePopulatedValidationSupport {
|
|||
try (InputStream is = ClasspathUtil.loadResourceAsStream(theClasspath)) {
|
||||
NpmPackage pkg = NpmPackage.fromPackage(is);
|
||||
if (pkg.getFolders().containsKey("package")) {
|
||||
NpmPackage.NpmPackageFolder packageFolder = pkg.getFolders().get("package");
|
||||
loadResourcesFromPackage(pkg);
|
||||
loadBinariesFromPackage(pkg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadResourcesFromPackage(NpmPackage thePackage) {
|
||||
NpmPackage.NpmPackageFolder packageFolder = thePackage.getFolders().get("package");
|
||||
|
||||
for (String nextFile : packageFolder.listFiles()) {
|
||||
if (nextFile.toLowerCase(Locale.US).endsWith(".json")) {
|
||||
|
@ -47,9 +55,12 @@ public class NpmPackageValidationSupport extends PrePopulatedValidationSupport {
|
|||
super.addResource(resource);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadBinariesFromPackage(NpmPackage thePackage) throws IOException {
|
||||
List<String> binaries = thePackage.list("other");
|
||||
for (String binaryName : binaries) {
|
||||
addBinary(TextFile.streamToBytes(thePackage.load("other", binaryName)), binaryName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,15 +34,15 @@ public class PrePopulatedValidationSupport extends BaseStaticResourceValidationS
|
|||
private final Map<String, IBaseResource> myCodeSystems;
|
||||
private final Map<String, IBaseResource> myStructureDefinitions;
|
||||
private final Map<String, IBaseResource> myValueSets;
|
||||
private final Map<String, byte[]> myBinaries;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public PrePopulatedValidationSupport(FhirContext theContext) {
|
||||
this(theContext, new HashMap<>(), new HashMap<>(), new HashMap<>());
|
||||
this(theContext, new HashMap<>(), new HashMap<>(), new HashMap<>(), new HashMap<>());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
|
@ -52,16 +52,49 @@ public class PrePopulatedValidationSupport extends BaseStaticResourceValidationS
|
|||
* the resource itself.
|
||||
* @param theCodeSystems The CodeSystems to be returned by this module. Keys are the logical URL for the resource, and values are
|
||||
* the resource itself.
|
||||
**/
|
||||
public PrePopulatedValidationSupport(
|
||||
FhirContext theFhirContext,
|
||||
Map<String, IBaseResource> theStructureDefinitions,
|
||||
Map<String, IBaseResource> theValueSets,
|
||||
Map<String, IBaseResource> theCodeSystems) {
|
||||
this(theFhirContext, theStructureDefinitions, theValueSets, theCodeSystems, new HashMap<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param theStructureDefinitions The StructureDefinitions to be returned by this module. Keys are the logical URL for the resource, and
|
||||
* values are the resource itself.
|
||||
* @param theValueSets The ValueSets to be returned by this module. Keys are the logical URL for the resource, and values are
|
||||
* the resource itself.
|
||||
* @param theCodeSystems The CodeSystems to be returned by this module. Keys are the logical URL for the resource, and values are
|
||||
* the resource itself.
|
||||
* @param theBinaries The binary files to be returned by this module. Keys are the unique filename for the binary, and values
|
||||
* are the contents of the file as a byte array.
|
||||
*/
|
||||
public PrePopulatedValidationSupport(FhirContext theFhirContext, Map<String, IBaseResource> theStructureDefinitions, Map<String, IBaseResource> theValueSets, Map<String, IBaseResource> theCodeSystems) {
|
||||
public PrePopulatedValidationSupport(
|
||||
FhirContext theFhirContext,
|
||||
Map<String, IBaseResource> theStructureDefinitions,
|
||||
Map<String, IBaseResource> theValueSets,
|
||||
Map<String, IBaseResource> theCodeSystems,
|
||||
Map<String, byte[]> theBinaries) {
|
||||
super(theFhirContext);
|
||||
Validate.notNull(theFhirContext, "theFhirContext must not be null");
|
||||
Validate.notNull(theStructureDefinitions, "theStructureDefinitions must not be null");
|
||||
Validate.notNull(theValueSets, "theValueSets must not be null");
|
||||
Validate.notNull(theCodeSystems, "theCodeSystems must not be null");
|
||||
Validate.notNull(theBinaries, "theBinaries must not be null");
|
||||
myStructureDefinitions = theStructureDefinitions;
|
||||
myValueSets = theValueSets;
|
||||
myCodeSystems = theCodeSystems;
|
||||
myBinaries = theBinaries;
|
||||
}
|
||||
|
||||
public void addBinary(byte[] theBinary, String theBinaryKey) {
|
||||
Validate.notNull(theBinary, "theBinaryKey must not be null");
|
||||
Validate.notNull(theBinary, "the" + theBinaryKey + " must not be null");
|
||||
myBinaries.put(theBinaryKey, theBinary);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -220,6 +253,9 @@ public class PrePopulatedValidationSupport extends BaseStaticResourceValidationS
|
|||
return myStructureDefinitions.get(theUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] fetchBinary(String theBinaryKey) { return myBinaries.get(theBinaryKey); }
|
||||
|
||||
@Override
|
||||
public boolean isCodeSystemSupported(ValidationSupportContext theValidationSupportContext, String theSystem) {
|
||||
return myCodeSystems.containsKey(theSystem);
|
||||
|
|
|
@ -257,6 +257,17 @@ public class ValidationSupportChain implements IValidationSupport {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] fetchBinary(String key) {
|
||||
for (IValidationSupport next : myChain) {
|
||||
byte[] retVal = next.fetchBinary(key);
|
||||
if (retVal != null) {
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBaseResource fetchStructureDefinition(String theUrl) {
|
||||
for (IValidationSupport next : myChain) {
|
||||
|
|
|
@ -131,12 +131,12 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo
|
|||
|
||||
@Override
|
||||
public boolean hasBinaryKey(String s) {
|
||||
throw new UnsupportedOperationException(Msg.code(2126));
|
||||
return myValidationSupportContext.getRootValidationSupport().fetchBinary(s) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getBinaryForKey(String s) {
|
||||
throw new UnsupportedOperationException(Msg.code(2127));
|
||||
return myValidationSupportContext.getRootValidationSupport().fetchBinary(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -258,7 +258,7 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo
|
|||
|
||||
@Override
|
||||
public void cacheResource(Resource res) {
|
||||
throw new UnsupportedOperationException(Msg.code(660));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
package org.hl7.fhir.common.hapi.validation;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.i18n.Msg;
|
||||
import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
public class ValidationSupportChainTest {
|
||||
|
||||
@Test
|
||||
public void testVersionCheck() {
|
||||
|
||||
DefaultProfileValidationSupport ctx3 = new DefaultProfileValidationSupport(FhirContext.forDstu3());
|
||||
DefaultProfileValidationSupport ctx4 = new DefaultProfileValidationSupport(FhirContext.forR4());
|
||||
|
||||
try {
|
||||
new ValidationSupportChain(ctx3, ctx4);
|
||||
} catch (ConfigurationException e) {
|
||||
assertEquals(Msg.code(709) + "Trying to add validation support of version R4 to chain with 1 entries of version DSTU3", e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMissingContext() {
|
||||
IValidationSupport ctx = mock(IValidationSupport.class);
|
||||
try {
|
||||
new ValidationSupportChain(ctx);
|
||||
} catch (ConfigurationException e) {
|
||||
assertEquals(Msg.code(708) + "Can not add validation support: getFhirContext() returns null", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -18,6 +18,8 @@ import static org.awaitility.Awaitility.await;
|
|||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
|
@ -68,5 +70,20 @@ public class CachingValidationSupportTest {
|
|||
assertEquals(1, responses.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fetchBinary_normally_accessesSuperOnlyOnce() {
|
||||
final byte[] EXPECTED_BINARY = "dummyBinaryContent".getBytes();
|
||||
final String EXPECTED_BINARY_KEY = "dummyBinaryKey";
|
||||
when(myValidationSupport.getFhirContext()).thenReturn(ourCtx);
|
||||
when(myValidationSupport.fetchBinary(EXPECTED_BINARY_KEY)).thenReturn(EXPECTED_BINARY);
|
||||
CachingValidationSupport support = new CachingValidationSupport(myValidationSupport);
|
||||
|
||||
final byte[] firstActualBinary = support.fetchBinary(EXPECTED_BINARY_KEY);
|
||||
assertEquals(EXPECTED_BINARY,firstActualBinary);
|
||||
verify(myValidationSupport, times(1)).fetchBinary(EXPECTED_BINARY_KEY);
|
||||
|
||||
final byte[] secondActualBinary = support.fetchBinary(EXPECTED_BINARY_KEY);
|
||||
assertEquals(EXPECTED_BINARY,secondActualBinary);
|
||||
verify(myValidationSupport, times(1)).fetchBinary(EXPECTED_BINARY_KEY);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,9 @@ import org.hl7.fhir.r4.model.StructureDefinition;
|
|||
import org.hl7.fhir.r4.model.ValueSet;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
|
||||
public class PrePopulatedValidationSupportTest {
|
||||
|
@ -33,4 +36,19 @@ public class PrePopulatedValidationSupportTest {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddBinary() {
|
||||
final Map<String, byte[]> EXPECTED_BINARIES_MAP = Map.of(
|
||||
"dummyBinary1.txt", "myDummyContent1".getBytes(),
|
||||
"dummyBinary2.txt", "myDummyContent2".getBytes()
|
||||
);
|
||||
|
||||
for (Map.Entry<String,byte[]> entry : EXPECTED_BINARIES_MAP.entrySet()) {
|
||||
mySvc.addBinary(entry.getValue(),entry.getKey());
|
||||
}
|
||||
|
||||
for (Map.Entry<String,byte[]> entry : EXPECTED_BINARIES_MAP.entrySet()) {
|
||||
assertArrayEquals(entry.getValue(), mySvc.fetchBinary(entry.getKey()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
package org.hl7.fhir.common.hapi.validation.support;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.i18n.Msg;
|
||||
import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class ValidationSupportChainTest {
|
||||
|
||||
|
||||
@Test
|
||||
public void testVersionCheck() {
|
||||
|
||||
DefaultProfileValidationSupport ctx3 = new DefaultProfileValidationSupport(FhirContext.forDstu3());
|
||||
DefaultProfileValidationSupport ctx4 = new DefaultProfileValidationSupport(FhirContext.forR4());
|
||||
|
||||
try {
|
||||
new ValidationSupportChain(ctx3, ctx4);
|
||||
} catch (ConfigurationException e) {
|
||||
assertEquals(Msg.code(709) + "Trying to add validation support of version R4 to chain with 1 entries of version DSTU3", e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMissingContext() {
|
||||
IValidationSupport ctx = mock(IValidationSupport.class);
|
||||
try {
|
||||
new ValidationSupportChain(ctx);
|
||||
} catch (ConfigurationException e) {
|
||||
assertEquals(Msg.code(708) + "Can not add validation support: getFhirContext() returns null", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fetchBinary_normally_returnsExpectedBinaries() {
|
||||
|
||||
final byte[] EXPECTED_BINARY_CONTENT_1 = "dummyBinaryContent1".getBytes();
|
||||
final byte[] EXPECTED_BINARY_CONTENT_2 = "dummyBinaryContent2".getBytes();
|
||||
final String EXPECTED_BINARY_KEY_1 = "dummyBinaryKey1";
|
||||
final String EXPECTED_BINARY_KEY_2 = "dummyBinaryKey2";
|
||||
|
||||
IValidationSupport validationSupport1 = createMockValidationSupportWithSingleBinary(EXPECTED_BINARY_KEY_1, EXPECTED_BINARY_CONTENT_1);
|
||||
IValidationSupport validationSupport2 = createMockValidationSupportWithSingleBinary(EXPECTED_BINARY_KEY_2, EXPECTED_BINARY_CONTENT_2);
|
||||
|
||||
ValidationSupportChain validationSupportChain = new ValidationSupportChain(validationSupport1, validationSupport2);
|
||||
|
||||
final byte[] actualBinaryContent1 = validationSupportChain.fetchBinary(EXPECTED_BINARY_KEY_1 );
|
||||
final byte[] actualBinaryContent2 = validationSupportChain.fetchBinary(EXPECTED_BINARY_KEY_2 );
|
||||
|
||||
assertArrayEquals(EXPECTED_BINARY_CONTENT_1, actualBinaryContent1);
|
||||
assertArrayEquals(EXPECTED_BINARY_CONTENT_2, actualBinaryContent2);
|
||||
assertNull(validationSupportChain.fetchBinary("nonExistentKey"));
|
||||
}
|
||||
|
||||
|
||||
private static IValidationSupport createMockValidationSupport() {
|
||||
IValidationSupport validationSupport;
|
||||
validationSupport = mock(IValidationSupport.class);
|
||||
FhirContext mockContext = mock(FhirContext.class);
|
||||
when(mockContext.getVersion()).thenReturn(FhirVersionEnum.R4.getVersionImplementation());
|
||||
when(validationSupport.getFhirContext()).thenReturn(mockContext);
|
||||
return validationSupport;
|
||||
}
|
||||
|
||||
|
||||
private static IValidationSupport createMockValidationSupportWithSingleBinary(String expected_binary_key, byte[] expected_binary_content) {
|
||||
IValidationSupport validationSupport1 = createMockValidationSupport();
|
||||
when(validationSupport1.fetchBinary(expected_binary_key)).thenReturn(expected_binary_content);
|
||||
return validationSupport1;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
package org.hl7.fhir.common.hapi.validation.validator;
|
||||
|
||||
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.i18n.HapiLocalizer;
|
||||
import org.hl7.fhir.r5.model.Resource;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class VersionSpecificWorkerContextWrapperTest {
|
||||
|
||||
final byte[] EXPECTED_BINARY_CONTENT_1 = "dummyBinaryContent1".getBytes();
|
||||
final byte[] EXPECTED_BINARY_CONTENT_2 = "dummyBinaryContent2".getBytes();
|
||||
final String EXPECTED_BINARY_KEY_1 = "dummyBinaryKey1";
|
||||
final String EXPECTED_BINARY_KEY_2 = "dummyBinaryKey2";
|
||||
final String NON_EXISTENT_BINARY_KEY = "nonExistentBinaryKey";
|
||||
|
||||
@Test
|
||||
public void hasBinaryKey_normally_returnsExpected() {
|
||||
|
||||
IValidationSupport validationSupport = mockValidationSupportWithTwoBinaries();
|
||||
|
||||
ValidationSupportContext mockContext = mockValidationSupportContext(validationSupport);
|
||||
VersionSpecificWorkerContextWrapper.IVersionTypeConverter converter = mock(VersionSpecificWorkerContextWrapper.IVersionTypeConverter.class);
|
||||
|
||||
VersionSpecificWorkerContextWrapper wrapper = new VersionSpecificWorkerContextWrapper(mockContext, converter);
|
||||
|
||||
assertTrue(wrapper.hasBinaryKey(EXPECTED_BINARY_KEY_1), "wrapper should have binary key " + EXPECTED_BINARY_KEY_1 );
|
||||
assertTrue(wrapper.hasBinaryKey(EXPECTED_BINARY_KEY_2), "wrapper should have binary key " + EXPECTED_BINARY_KEY_1 );
|
||||
assertFalse(wrapper.hasBinaryKey(NON_EXISTENT_BINARY_KEY), "wrapper should not have binary key " + NON_EXISTENT_BINARY_KEY);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBinaryForKey_normally_returnsExpected() {
|
||||
|
||||
IValidationSupport validationSupport = mockValidationSupportWithTwoBinaries();
|
||||
|
||||
ValidationSupportContext mockContext = mockValidationSupportContext(validationSupport);
|
||||
VersionSpecificWorkerContextWrapper.IVersionTypeConverter converter = mock(VersionSpecificWorkerContextWrapper.IVersionTypeConverter.class);
|
||||
|
||||
VersionSpecificWorkerContextWrapper wrapper = new VersionSpecificWorkerContextWrapper(mockContext, converter);
|
||||
|
||||
assertArrayEquals(EXPECTED_BINARY_CONTENT_1, wrapper.getBinaryForKey(EXPECTED_BINARY_KEY_1));
|
||||
assertArrayEquals(EXPECTED_BINARY_CONTENT_2, wrapper.getBinaryForKey(EXPECTED_BINARY_KEY_2));
|
||||
assertNull(wrapper.getBinaryForKey(NON_EXISTENT_BINARY_KEY), "wrapper should return null for binary key " + NON_EXISTENT_BINARY_KEY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cacheResource_normally_executesWithoutException() {
|
||||
|
||||
IValidationSupport validationSupport = mockValidationSupport();
|
||||
|
||||
ValidationSupportContext mockContext = mockValidationSupportContext(validationSupport);
|
||||
VersionSpecificWorkerContextWrapper.IVersionTypeConverter converter = mock(VersionSpecificWorkerContextWrapper.IVersionTypeConverter.class);
|
||||
|
||||
VersionSpecificWorkerContextWrapper wrapper = new VersionSpecificWorkerContextWrapper(mockContext, converter);
|
||||
|
||||
wrapper.cacheResource(mock(Resource.class));
|
||||
}
|
||||
|
||||
private IValidationSupport mockValidationSupportWithTwoBinaries() {
|
||||
IValidationSupport validationSupport;
|
||||
validationSupport = mockValidationSupport();
|
||||
when(validationSupport.fetchBinary(EXPECTED_BINARY_KEY_1)).thenReturn(EXPECTED_BINARY_CONTENT_1);
|
||||
when(validationSupport.fetchBinary(EXPECTED_BINARY_KEY_2)).thenReturn(EXPECTED_BINARY_CONTENT_2);
|
||||
return validationSupport;
|
||||
}
|
||||
|
||||
|
||||
private static ValidationSupportContext mockValidationSupportContext(IValidationSupport validationSupport) {
|
||||
ValidationSupportContext mockContext;
|
||||
mockContext = mock(ValidationSupportContext.class);
|
||||
when(mockContext.getRootValidationSupport()).thenReturn(validationSupport);
|
||||
return mockContext;
|
||||
}
|
||||
|
||||
|
||||
private static IValidationSupport mockValidationSupport() {
|
||||
IValidationSupport mockValidationSupport;
|
||||
mockValidationSupport = mock(IValidationSupport.class);
|
||||
FhirContext mockFhirContext = mock(FhirContext.class);
|
||||
when(mockFhirContext.getLocalizer()).thenReturn(new HapiLocalizer());
|
||||
when(mockFhirContext.getVersion()).thenReturn(FhirVersionEnum.R4.getVersionImplementation());
|
||||
when(mockValidationSupport.getFhirContext()).thenReturn(mockFhirContext);
|
||||
return mockValidationSupport;
|
||||
}
|
||||
}
|
|
@ -12,27 +12,34 @@ import org.hl7.fhir.common.hapi.validation.support.SnapshotGeneratingValidationS
|
|||
import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain;
|
||||
import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class NpmPackageValidationSupportTest {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(NpmPackageValidationSupportTest.class);
|
||||
private FhirContext myFhirContext = FhirContext.forR4Cached();
|
||||
|
||||
private Map<String, byte[]> EXPECTED_BINARIES_MAP = Map.of(
|
||||
"dummyBinary1.txt", "myDummyContent1".getBytes(),
|
||||
"dummyBinary2.txt", "myDummyContent2".getBytes()
|
||||
);
|
||||
@Test
|
||||
public void testValidateWithPackage() throws IOException {
|
||||
|
||||
// Create an NPM Package Support module and load one package in from
|
||||
// the classpath
|
||||
NpmPackageValidationSupport npmPackageSupport = new NpmPackageValidationSupport(myFhirContext);
|
||||
npmPackageSupport.loadPackageFromClasspath("classpath:package/UK.Core.r4-1.1.0.tgz");
|
||||
NpmPackageValidationSupport npmPackageSupport = getNpmPackageValidationSupport("classpath:package/UK.Core.r4-1.1.0.tgz");
|
||||
|
||||
// Create a support chain including the NPM Package Support
|
||||
ValidationSupportChain validationSupportChain = new ValidationSupportChain(
|
||||
|
@ -64,4 +71,21 @@ public class NpmPackageValidationSupportTest {
|
|||
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private NpmPackageValidationSupport getNpmPackageValidationSupport(String theClasspath) throws IOException {
|
||||
NpmPackageValidationSupport npmPackageSupport = new NpmPackageValidationSupport(myFhirContext);
|
||||
npmPackageSupport.loadPackageFromClasspath(theClasspath);
|
||||
return npmPackageSupport;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadPackageFromClasspath_normally_loadsExpectedBinaries() throws IOException {
|
||||
NpmPackageValidationSupport npmPackageSupport = getNpmPackageValidationSupport("classpath:package/dummy-package-with-binaries.tgz");
|
||||
|
||||
for (Map.Entry<String, byte[]> entry : EXPECTED_BINARIES_MAP.entrySet()) {
|
||||
byte[] expectedBytes = entry.getValue();
|
||||
byte[] actualBytes = npmPackageSupport.fetchBinary(entry.getKey());
|
||||
assertArrayEquals(expectedBytes, actualBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
package org.hl7.fhir.r4.validation;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.validation.FhirValidator;
|
||||
import ca.uhn.fhir.validation.ValidationResult;
|
||||
import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain;
|
||||
import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator;
|
||||
import org.hl7.fhir.r4.model.MedicationRequest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static ca.uhn.fhir.util.ClasspathUtil.loadResource;
|
||||
|
||||
public class ParserWithValidationR4Test {
|
||||
private static final FhirContext ourCtx = FhirContext.forR4();
|
||||
|
||||
@Test
|
||||
public void testActivityDefinitionElementsOrder() {
|
||||
ourCtx.setValidationSupport(getValidationSupport());
|
||||
MedicationRequest med_req = ourCtx.newJsonParser().parseResource(MedicationRequest.class, loadResource("/r4/amz/medication-request-amz.json"));
|
||||
|
||||
final FhirInstanceValidator instanceValidator = new FhirInstanceValidator(ourCtx);
|
||||
instanceValidator.setNoTerminologyChecks(true);
|
||||
FhirValidator validator = ourCtx.newValidator();
|
||||
|
||||
validator.registerValidatorModule(instanceValidator);
|
||||
ValidationResult validationResult = validator.validateWithResult(med_req);
|
||||
validationResult.getMessages().forEach(System.out::println);
|
||||
}
|
||||
|
||||
private IValidationSupport getValidationSupport() {
|
||||
return new ValidationSupportChain(new DefaultProfileValidationSupport(ourCtx));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
package org.hl7.fhir.r4.validation;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.validation.FhirValidator;
|
||||
import ca.uhn.fhir.validation.ResultSeverityEnum;
|
||||
import ca.uhn.fhir.validation.SingleValidationMessage;
|
||||
import ca.uhn.fhir.validation.ValidationResult;
|
||||
import org.hl7.fhir.common.hapi.validation.support.CachingValidationSupport;
|
||||
import org.hl7.fhir.common.hapi.validation.support.NpmPackageValidationSupport;
|
||||
import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain;
|
||||
import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator;
|
||||
import org.hl7.fhir.r4.model.IntegerType;
|
||||
import org.hl7.fhir.r4.model.MedicationRequest;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static ca.uhn.fhir.util.ClasspathUtil.loadResource;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class XverExtensionsValidationTest {
|
||||
private static final FhirContext ourCtx = FhirContext.forR4();
|
||||
|
||||
@Test
|
||||
public void validation_XverExtensionsAndCachingValidationSupport_ReturnsNoErrors() throws IOException {
|
||||
ourCtx.setValidationSupport(new CachingValidationSupport(getValidationSupport()));
|
||||
MedicationRequest med_req;
|
||||
med_req = getMedicationRequest();
|
||||
FhirValidator validator = getFhirValidator();
|
||||
|
||||
ValidationResult validationResult = validator.validateWithResult(med_req);
|
||||
|
||||
assertEquals(0, validationResult.getMessages().stream().filter(errorMessagePredicate()).count());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void validation_XverExtensions_ReturnsNoErrors() throws IOException {
|
||||
ourCtx.setValidationSupport(getValidationSupport());
|
||||
MedicationRequest med_req = getMedicationRequest();
|
||||
FhirValidator validator = getFhirValidator();
|
||||
|
||||
ValidationResult validationResult = validator.validateWithResult(med_req);
|
||||
|
||||
assertEquals(0, validationResult.getMessages().stream().filter(errorMessagePredicate()).count());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validation_InvalidExtensionValue_ReturnsError() throws IOException {
|
||||
ourCtx.setValidationSupport(getValidationSupport());
|
||||
MedicationRequest med_req = getMedicationRequest();
|
||||
med_req.getMeta().getExtension().get(0).setValue(new IntegerType().setValue(123));
|
||||
FhirValidator validator = getFhirValidator();
|
||||
|
||||
ValidationResult validationResult = validator.validateWithResult(med_req);
|
||||
|
||||
assertEquals(1, validationResult.getMessages().stream().filter(errorMessagePredicate()).count());
|
||||
SingleValidationMessage errorMessage = validationResult.getMessages().stream().filter(errorMessagePredicate()).findFirst().get();
|
||||
assertEquals("Extension_EXT_Type", errorMessage.getMessageId());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Predicate<SingleValidationMessage> errorMessagePredicate() {
|
||||
return message -> message.getSeverity() == ResultSeverityEnum.ERROR;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static FhirValidator getFhirValidator() {
|
||||
FhirValidator validator;
|
||||
final FhirInstanceValidator instanceValidator = new FhirInstanceValidator(ourCtx);
|
||||
instanceValidator.setNoTerminologyChecks(true);
|
||||
validator = ourCtx.newValidator();
|
||||
|
||||
validator.registerValidatorModule(instanceValidator);
|
||||
return validator;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static MedicationRequest getMedicationRequest() {
|
||||
MedicationRequest med_req;
|
||||
med_req = ourCtx.newJsonParser().parseResource(MedicationRequest.class, loadResource("/r4/amz/medication-request-amz.json"));
|
||||
return med_req;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private IValidationSupport getValidationSupport() throws IOException {
|
||||
NpmPackageValidationSupport npmPackageSupport = new NpmPackageValidationSupport(ourCtx);
|
||||
npmPackageSupport.loadPackageFromClasspath("classpath:package/hl7.fhir.xver-extensions-0.0.11.tgz");
|
||||
|
||||
return new ValidationSupportChain(
|
||||
new DefaultProfileValidationSupport(ourCtx),
|
||||
npmPackageSupport
|
||||
);
|
||||
}
|
||||
}
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"resourceType" : "MedicationRequest",
|
||||
"meta" : {
|
||||
"versionId" : "1",
|
||||
"tag" : [
|
||||
{
|
||||
"system" : "https://smarthealthit.org/tags",
|
||||
"code" : "synthea-8-2017"
|
||||
}
|
||||
],
|
||||
"lastUpdated" : "2021-04-17T02:39:06.885-04:00",
|
||||
"extension" : [
|
||||
{
|
||||
"valueUri" : "https://r2.smarthealthit.org",
|
||||
"url" : "http://hl7.org/fhir/4.0/StructureDefinition/extension-Meta.source"
|
||||
}
|
||||
]
|
||||
},
|
||||
"encounter" : {
|
||||
"reference" : "Encounter/a83e5350-6462-4331-adba-dc72552cecbd"
|
||||
},
|
||||
"subject" : {
|
||||
"reference" : "Patient/ab606cad-b848-4a24-b3b6-61075563fadf"
|
||||
},
|
||||
"authoredOn" : "2018-02-19",
|
||||
"status" : "stopped",
|
||||
"medicationCodeableConcept" : {
|
||||
"coding" : [
|
||||
{
|
||||
"display" : "Atorvastatin",
|
||||
"system" : "http://www.nlm.nih.gov/research/umls/rxnorm",
|
||||
"code" : "259255"
|
||||
}
|
||||
],
|
||||
"text" : "Atorvastatin"
|
||||
},
|
||||
"intent" : "order"
|
||||
}
|
Loading…
Reference in New Issue