Merge validator
This commit is contained in:
parent
a0c40cf98a
commit
adc73e75c9
|
@ -123,6 +123,19 @@ public class BundleUtil {
|
|||
return null;
|
||||
}
|
||||
|
||||
public static Integer getTotal(FhirContext theContext, IBaseBundle theBundle) {
|
||||
RuntimeResourceDefinition def = theContext.getResourceDefinition(theBundle);
|
||||
BaseRuntimeChildDefinition entryChild = def.getChildByName("total");
|
||||
List<IBase> entries = entryChild.getAccessor().getValues(theBundle);
|
||||
if (entries.size() > 0) {
|
||||
IPrimitiveType<Number> typeElement = (IPrimitiveType<Number>) entries.get(0);
|
||||
if (typeElement != null && typeElement.getValue() != null) {
|
||||
return typeElement.getValue().intValue();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract all of the resources from a given bundle
|
||||
*/
|
||||
|
|
|
@ -33,6 +33,11 @@
|
|||
<type>jar</type>
|
||||
<classifier>classes</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-igpacks</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
|
|
|
@ -21,6 +21,7 @@ package ca.uhn.fhir.cli;
|
|||
*/
|
||||
|
||||
import org.hl7.fhir.instance.hapi.validation.IValidationSupport;
|
||||
import org.hl7.fhir.instance.model.StructureDefinition;
|
||||
import org.hl7.fhir.instance.model.ValueSet;
|
||||
import org.hl7.fhir.instance.model.ValueSet.ConceptSetComponent;
|
||||
import org.hl7.fhir.instance.model.ValueSet.ValueSetExpansionComponent;
|
||||
|
@ -31,12 +32,20 @@ import ca.uhn.fhir.rest.client.api.IGenericClient;
|
|||
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class LoadingValidationSupportDstu2 implements IValidationSupport {
|
||||
|
||||
private FhirContext myCtx = FhirContext.forDstu2Hl7Org();
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(LoadingValidationSupportDstu2.class);
|
||||
|
||||
@Override
|
||||
public List<StructureDefinition> allStructures() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) {
|
||||
return null;
|
||||
|
|
|
@ -21,21 +21,29 @@ package ca.uhn.fhir.cli;
|
|||
*/
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.igpacks.parser.IgPackParserDstu2;
|
||||
import ca.uhn.fhir.igpacks.parser.IgPackParserDstu3;
|
||||
import ca.uhn.fhir.parser.LenientErrorHandler;
|
||||
import ca.uhn.fhir.validation.FhirValidator;
|
||||
import ca.uhn.fhir.validation.SingleValidationMessage;
|
||||
import ca.uhn.fhir.validation.ValidationResult;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.phloc.commons.io.file.FileUtils;
|
||||
import org.apache.commons.cli.*;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.text.WordUtils;
|
||||
import org.fusesource.jansi.Ansi.Color;
|
||||
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport;
|
||||
import org.hl7.fhir.dstu3.hapi.validation.DefaultProfileValidationSupport;
|
||||
import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator;
|
||||
import org.hl7.fhir.dstu3.hapi.validation.ValidationSupportChain;
|
||||
import org.hl7.fhir.dstu3.model.StructureDefinition;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.*;
|
||||
import static org.fusesource.jansi.Ansi.ansi;
|
||||
|
@ -71,10 +79,24 @@ public class ValidateCommand extends BaseCommand {
|
|||
"Allow fetching remote resources (in other words, if a resource being validated refers to an external StructureDefinition, Questionnaire, etc. this flag allows the validator to access the internet to try and fetch this resource)");
|
||||
retVal.addOption(new Option("l", "fetch-local", true, "Fetch a profile locally and use it if referenced"));
|
||||
retVal.addOption("e", "encoding", false, "File encoding (default is UTF-8)");
|
||||
|
||||
addOptionalOption(retVal, null, "igpack", true, "If specified, provides the filename of an IGPack file to include in validation");
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private String loadFile(String theFileName) throws ParseException {
|
||||
return new String(loadFileAsByteArray(theFileName), Charsets.UTF_8);
|
||||
}
|
||||
|
||||
private byte[] loadFileAsByteArray(String theFileName) throws ParseException {
|
||||
byte[] input;
|
||||
try {
|
||||
input = IOUtils.toByteArray(new FileInputStream(new File(theFileName)));
|
||||
} catch (IOException e) {
|
||||
throw new ParseException("Failed to load file '" + theFileName + "' - Error: " + e.toString());
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(CommandLine theCommandLine) throws ParseException {
|
||||
parseFhirContext(theCommandLine);
|
||||
|
@ -112,16 +134,18 @@ public class ValidateCommand extends BaseCommand {
|
|||
if (theCommandLine.hasOption("l")) {
|
||||
String localProfile = theCommandLine.getOptionValue("l");
|
||||
ourLog.info("Loading profile: {}", localProfile);
|
||||
String input;
|
||||
try {
|
||||
input = IOUtils.toString(new FileReader(new File(localProfile)));
|
||||
} catch (IOException e) {
|
||||
throw new ParseException("Failed to load file '" + localProfile + "' - Error: " + e.toString());
|
||||
}
|
||||
String input = loadFile(localProfile);
|
||||
|
||||
localProfileResource = ca.uhn.fhir.rest.api.EncodingEnum.detectEncodingNoDefault(input).newParser(ctx).parseResource(input);
|
||||
}
|
||||
|
||||
byte[] igPack = null;
|
||||
String igpackFilename = null;
|
||||
if (theCommandLine.hasOption("igpack")) {
|
||||
igpackFilename = theCommandLine.getOptionValue("igpack");
|
||||
igPack = loadFileAsByteArray(igpackFilename);
|
||||
}
|
||||
|
||||
if (theCommandLine.hasOption("p")) {
|
||||
switch (ctx.getVersion().getVersion()) {
|
||||
case DSTU2: {
|
||||
|
@ -129,6 +153,14 @@ public class ValidateCommand extends BaseCommand {
|
|||
val.registerValidatorModule(instanceValidator);
|
||||
org.hl7.fhir.instance.hapi.validation.ValidationSupportChain validationSupport = new org.hl7.fhir.instance.hapi.validation.ValidationSupportChain(
|
||||
new org.hl7.fhir.instance.hapi.validation.DefaultProfileValidationSupport());
|
||||
if (igPack != null) {
|
||||
FhirContext hl7orgCtx = FhirContext.forDstu2Hl7Org();
|
||||
hl7orgCtx.setParserErrorHandler(new LenientErrorHandler(false));
|
||||
IgPackParserDstu2 parser = new IgPackParserDstu2(hl7orgCtx);
|
||||
org.hl7.fhir.instance.hapi.validation.IValidationSupport igValidationSupport = parser.parseIg(igPack, igpackFilename);
|
||||
validationSupport.addValidationSupport(igValidationSupport);
|
||||
}
|
||||
|
||||
if (localProfileResource != null) {
|
||||
org.hl7.fhir.instance.model.StructureDefinition convertedSd = FhirContext.forDstu2Hl7Org().newXmlParser().parseResource(org.hl7.fhir.instance.model.StructureDefinition.class, ctx.newXmlParser().encodeResourceToString(localProfileResource));
|
||||
instanceValidator.setStructureDefintion(convertedSd);
|
||||
|
@ -143,6 +175,12 @@ public class ValidateCommand extends BaseCommand {
|
|||
FhirInstanceValidator instanceValidator = new FhirInstanceValidator();
|
||||
val.registerValidatorModule(instanceValidator);
|
||||
ValidationSupportChain validationSupport = new ValidationSupportChain(new DefaultProfileValidationSupport());
|
||||
if (igPack != null) {
|
||||
IgPackParserDstu3 parser = new IgPackParserDstu3(getFhirContext());
|
||||
IValidationSupport igValidationSupport = parser.parseIg(igPack, igpackFilename);
|
||||
validationSupport.addValidationSupport(igValidationSupport);
|
||||
}
|
||||
|
||||
if (localProfileResource != null) {
|
||||
instanceValidator.setStructureDefintion((StructureDefinition) localProfileResource);
|
||||
}
|
||||
|
@ -193,7 +231,7 @@ public class ValidateCommand extends BaseCommand {
|
|||
if (results.isSuccessful()) {
|
||||
ourLog.info("Validation successful!");
|
||||
} else {
|
||||
ourLog.warn("Validation FAILED");
|
||||
throw new CommandFailureException("Validation failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ package ca.uhn.fhir.cli;
|
|||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class ValidateTest {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValidateTest.class);
|
||||
|
||||
|
@ -16,8 +18,43 @@ public class ValidateTest {
|
|||
String resourcePath = ValidateTest.class.getResource("/patient-uslab-example1.xml").getFile();
|
||||
ourLog.info(resourcePath);
|
||||
|
||||
App.main(new String[] {"validate", "-v", "dstu3", "-p", "-n", resourcePath});
|
||||
App.main(new String[] {
|
||||
"validate",
|
||||
"-v", "dstu3",
|
||||
"-p",
|
||||
"-n", resourcePath});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateUsingIgPackSucceedingDstu2() {
|
||||
String resourcePath = ValidateTest.class.getResource("/argo-dstu2-observation-good.json").getFile();
|
||||
ourLog.info(resourcePath);
|
||||
|
||||
App.main(new String[] {
|
||||
"validate",
|
||||
"-v", "dstu2",
|
||||
"-p",
|
||||
"--igpack", "src/test/resources/argo-dstu2.pack",
|
||||
"-n", resourcePath});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateUsingIgPackFailingDstu2() {
|
||||
String resourcePath = ValidateTest.class.getResource("/argo-dstu2-observation-bad.json").getFile();
|
||||
ourLog.info(resourcePath);
|
||||
|
||||
try {
|
||||
App.main(new String[] {
|
||||
"validate",
|
||||
"-v", "dstu2",
|
||||
"-p",
|
||||
"--igpack", "src/test/resources/argo-dstu2.pack",
|
||||
"-n", resourcePath});
|
||||
// Should not get here
|
||||
fail();
|
||||
} catch (CommandFailureException e) {
|
||||
// good
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"resourceType": "Observation",
|
||||
"id": "head-circumference",
|
||||
"meta": {
|
||||
"profile": [
|
||||
"http://fhir.org/guides/argonaut/StructureDefinition/argo-vitalsigns"
|
||||
]
|
||||
},
|
||||
"text": {
|
||||
"status": "generated",
|
||||
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\"><p><b>Generated Narrative with Details</b></p><p><b>id</b>: head-circumference</p><p><b>meta</b>: </p><p><b>status</b>: final</p><p><b>category</b>: Vital Signs <span style=\"background: LightGoldenRodYellow\">(Details : {http://hl7.org/fhir/observation-category code 'vital-signs' = 'Vital Signs', given as 'Vital Signs'})</span></p><p><b>code</b>: head_circumference <span style=\"background: LightGoldenRodYellow\">(Details : {LOINC code '8287-5' = 'Head Occipital-frontal circumference by Tape measure', given as 'head_circumference'})</span></p><p><b>subject</b>: <a href=\"Patient-peter-chalmers.html\">Generated Summary: id: peter-chalmers; Medical Record Number = 1032702; active; Peter James Chalmers (OFFICIAL); ph: (555)555-5555(WORK), person@example.org(WORK); gender: male; birthDate: 19/06/1964</a></p><p><b>encounter</b>: <a href=\"Encounter-691.html\">Generated Summary: id: 691; status: in-progress; class: inpatient</a></p><p><b>effective</b>: 12/08/2010</p><p><b>value</b>: 51.2 cm<span style=\"background: LightGoldenRodYellow\"> (Details: UCUM code cm = 'cm')</span></p></div>"
|
||||
},
|
||||
"status": "final",
|
||||
"code": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://loinc.org",
|
||||
"code": "8287-5",
|
||||
"display": "head_circumference"
|
||||
}
|
||||
],
|
||||
"text": "head_circumference"
|
||||
},
|
||||
"subject": {
|
||||
"reference": "Patient/peter-chalmers"
|
||||
},
|
||||
"encounter": {
|
||||
"reference": "Encounter/691"
|
||||
},
|
||||
"effectiveDateTime": "2010-08-12",
|
||||
"valueQuantity": {
|
||||
"value": 51.2,
|
||||
"unit": "cm",
|
||||
"system": "http://unitsofmeasure.org",
|
||||
"code": "cm"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
{
|
||||
"resourceType": "Observation",
|
||||
"id": "head-circumference",
|
||||
"meta": {
|
||||
"profile": [
|
||||
"http://fhir.org/guides/argonaut/StructureDefinition/argo-vitalsigns"
|
||||
]
|
||||
},
|
||||
"text": {
|
||||
"status": "generated",
|
||||
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\"><p><b>Generated Narrative with Details</b></p><p><b>id</b>: head-circumference</p><p><b>meta</b>: </p><p><b>status</b>: final</p><p><b>category</b>: Vital Signs <span style=\"background: LightGoldenRodYellow\">(Details : {http://hl7.org/fhir/observation-category code 'vital-signs' = 'Vital Signs', given as 'Vital Signs'})</span></p><p><b>code</b>: head_circumference <span style=\"background: LightGoldenRodYellow\">(Details : {LOINC code '8287-5' = 'Head Occipital-frontal circumference by Tape measure', given as 'head_circumference'})</span></p><p><b>subject</b>: <a href=\"Patient-peter-chalmers.html\">Generated Summary: id: peter-chalmers; Medical Record Number = 1032702; active; Peter James Chalmers (OFFICIAL); ph: (555)555-5555(WORK), person@example.org(WORK); gender: male; birthDate: 19/06/1964</a></p><p><b>encounter</b>: <a href=\"Encounter-691.html\">Generated Summary: id: 691; status: in-progress; class: inpatient</a></p><p><b>effective</b>: 12/08/2010</p><p><b>value</b>: 51.2 cm<span style=\"background: LightGoldenRodYellow\"> (Details: UCUM code cm = 'cm')</span></p></div>"
|
||||
},
|
||||
"status": "final",
|
||||
"category": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://hl7.org/fhir/observation-category",
|
||||
"code": "vital-signs",
|
||||
"display": "Vital Signs"
|
||||
}
|
||||
],
|
||||
"text": "Vital Signs"
|
||||
},
|
||||
"code": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://loinc.org",
|
||||
"code": "8287-5",
|
||||
"display": "head_circumference"
|
||||
}
|
||||
],
|
||||
"text": "head_circumference"
|
||||
},
|
||||
"subject": {
|
||||
"reference": "Patient/peter-chalmers"
|
||||
},
|
||||
"encounter": {
|
||||
"reference": "Encounter/691"
|
||||
},
|
||||
"effectiveDateTime": "2010-08-12",
|
||||
"valueQuantity": {
|
||||
"value": 51.2,
|
||||
"unit": "cm",
|
||||
"system": "http://unitsofmeasure.org",
|
||||
"code": "cm"
|
||||
}
|
||||
}
|
Binary file not shown.
|
@ -0,0 +1,159 @@
|
|||
package ca.uhn.fhir.igpacks.parser;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* hapi-fhir-igpacks
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2018 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.util.StopWatch;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.dstu3.model.IdType;
|
||||
import org.hl7.fhir.dstu3.model.ImplementationGuide;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public abstract class BaseIgPackParser<T> {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(BaseIgPackParser.class);
|
||||
private final FhirContext myCtx;
|
||||
|
||||
public BaseIgPackParser(FhirContext theCtx) {
|
||||
FhirVersionEnum expectedVersion = provideExpectedVersion();
|
||||
Validate.isTrue(theCtx.getVersion().getVersion() == expectedVersion, "theCtx is not for the correct version, expecting " + expectedVersion);
|
||||
|
||||
myCtx = theCtx;
|
||||
}
|
||||
|
||||
protected abstract T createValidationSupport(Map<IIdType, IBaseResource> theIgResources);
|
||||
|
||||
private IBaseResource findResource(Map<String, IBaseResource> theCandidateResources, IIdType theId) {
|
||||
IBaseResource retVal = theCandidateResources.get(theId.toUnqualifiedVersionless().getValue());
|
||||
if (retVal == null) {
|
||||
throw new InternalErrorException("Unknown reference in ImplementationGuide: " + theId);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param theIgInputStream The "validator.pack" ZIP file
|
||||
* @param theDescription A description (just used for logs)
|
||||
*/
|
||||
public T parseIg(InputStream theIgInputStream, String theDescription) {
|
||||
Validate.notNull(theIgInputStream, "theIdInputStream must not be null");
|
||||
String igResourceName = "ImplementationGuide-ig.json";
|
||||
|
||||
ourLog.info("Parsing IGPack: {}", theDescription);
|
||||
StopWatch sw = new StopWatch();
|
||||
|
||||
ZipInputStream zipInputStream = new ZipInputStream(theIgInputStream);
|
||||
ZipEntry entry;
|
||||
try {
|
||||
|
||||
Map<String, IBaseResource> candidateResources = new HashMap<>();
|
||||
Map<IIdType, IBaseResource> igResources = new HashMap<>();
|
||||
|
||||
while ((entry = zipInputStream.getNextEntry()) != null) {
|
||||
if (entry.getName().endsWith(".json")) {
|
||||
|
||||
IBaseResource parsed;
|
||||
InputStreamReader nextReader = new InputStreamReader(zipInputStream, Constants.CHARSET_UTF8);
|
||||
|
||||
if (entry.getName().equals(igResourceName)) {
|
||||
parsed = FhirContext.forDstu3().newJsonParser().parseResource(ImplementationGuide.class, nextReader);
|
||||
} else {
|
||||
parsed = myCtx.newJsonParser().parseResource(nextReader);
|
||||
}
|
||||
|
||||
candidateResources.put(entry.getName(), parsed);
|
||||
}
|
||||
}
|
||||
|
||||
ourLog.info("Parsed {} candidateResources in {}ms", candidateResources.size(), sw.getMillis());
|
||||
|
||||
ImplementationGuide ig = (ImplementationGuide) candidateResources.get(igResourceName);
|
||||
|
||||
if (ig == null) {
|
||||
throw new InternalErrorException("IG Pack '" + theDescription + "' does not contain a resource named: " + igResourceName);
|
||||
}
|
||||
|
||||
HashMap<String, IBaseResource> newCandidateResources = new HashMap<>();
|
||||
for (IBaseResource next : candidateResources.values()) {
|
||||
newCandidateResources.put(next.getIdElement().toUnqualifiedVersionless().getValue(), next);
|
||||
}
|
||||
candidateResources = newCandidateResources;
|
||||
|
||||
for (ImplementationGuide.ImplementationGuidePackageComponent nextPackage : ig.getPackage()) {
|
||||
ourLog.info("Processing package {}", nextPackage.getName());
|
||||
|
||||
for (ImplementationGuide.ImplementationGuidePackageResourceComponent nextResource : nextPackage.getResource()) {
|
||||
if (isNotBlank(nextResource.getSourceReference().getReference())) {
|
||||
IdType id = new IdType(nextResource.getSourceReference().getReference());
|
||||
if (isNotBlank(id.getResourceType())) {
|
||||
switch (id.getResourceType()) {
|
||||
case "CodeSystem":
|
||||
case "ConceptMap":
|
||||
case "StructureDefinition":
|
||||
case "ValueSet":
|
||||
IBaseResource resource = findResource(candidateResources, id);
|
||||
igResources.put(id.toUnqualifiedVersionless(), resource);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ourLog.info("IG contains {} resources", igResources.size());
|
||||
return createValidationSupport(igResources);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new InternalErrorException("Failure while parsing IG: " + e, e);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param theIgPack The "validator.pack" ZIP file
|
||||
* @param theDescription A description (just used for logs)
|
||||
*/
|
||||
public T parseIg(byte[] theIgPack, String theDescription) {
|
||||
return parseIg(new ByteArrayInputStream(theIgPack), theDescription);
|
||||
}
|
||||
|
||||
protected abstract FhirVersionEnum provideExpectedVersion();
|
||||
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
package ca.uhn.fhir.igpacks.parser;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* hapi-fhir-igpacks
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2018 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import org.hl7.fhir.instance.hapi.validation.IValidationSupport;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class IgPackParserDstu2 extends BaseIgPackParser<IValidationSupport> {
|
||||
|
||||
public IgPackParserDstu2(FhirContext theCtx) {
|
||||
super(massage(theCtx));
|
||||
}
|
||||
|
||||
protected IValidationSupport createValidationSupport(Map<IIdType, IBaseResource> theIgResources) {
|
||||
return new IgPackValidationSupportDstu2(theIgResources);
|
||||
}
|
||||
|
||||
protected FhirVersionEnum provideExpectedVersion() {
|
||||
return FhirVersionEnum.DSTU2_HL7ORG;
|
||||
}
|
||||
|
||||
private static FhirContext massage(FhirContext theCtx) {
|
||||
if (theCtx.getVersion().getVersion() == FhirVersionEnum.DSTU2) {
|
||||
return FhirContext.forDstu2Hl7Org();
|
||||
} else {
|
||||
return theCtx;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -23,119 +23,26 @@ package ca.uhn.fhir.igpacks.parser;
|
|||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.util.StopWatch;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport;
|
||||
import org.hl7.fhir.dstu3.model.IdType;
|
||||
import org.hl7.fhir.dstu3.model.ImplementationGuide;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public class IgPackParserDstu3 {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(IgPackParserDstu3.class);
|
||||
private final FhirContext myCtx;
|
||||
public class IgPackParserDstu3 extends BaseIgPackParser<IValidationSupport> {
|
||||
|
||||
public IgPackParserDstu3(FhirContext theCtx) {
|
||||
FhirVersionEnum expectedVersion = FhirVersionEnum.DSTU3;
|
||||
Validate.isTrue(theCtx.getVersion().getVersion() == expectedVersion, "theCtx is not for the correct version, expecting " + expectedVersion);
|
||||
|
||||
myCtx = theCtx;
|
||||
super(theCtx);
|
||||
}
|
||||
|
||||
private IBaseResource findResource(Map<String, IBaseResource> theCandidateResources, IIdType theId) {
|
||||
IBaseResource retVal = theCandidateResources.get(theId.toUnqualifiedVersionless().getValue());
|
||||
if (retVal == null) {
|
||||
throw new InternalErrorException("Unknown reference in ImplementationGuide: " + theId);
|
||||
}
|
||||
return retVal;
|
||||
protected IValidationSupport createValidationSupport(Map<IIdType, IBaseResource> theIgResources) {
|
||||
return new IgPackValidationSupportDstu3(theIgResources);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param theIgInputStream The "validator.pack" ZIP file
|
||||
* @param theDescription A description (just used for logs)
|
||||
*/
|
||||
public IValidationSupport parseIg(InputStream theIgInputStream, String theDescription) {
|
||||
Validate.notNull(theIgInputStream, "theIdInputStream must not be null");
|
||||
|
||||
ourLog.info("Parsing IGPack: {}", theDescription);
|
||||
StopWatch sw = new StopWatch();
|
||||
|
||||
ZipInputStream zipInputStream = new ZipInputStream(theIgInputStream);
|
||||
ZipEntry entry;
|
||||
try {
|
||||
|
||||
Map<String, IBaseResource> candidateResources = new HashMap<>();
|
||||
Map<IIdType, IBaseResource> igResources = new HashMap<>();
|
||||
|
||||
while ((entry = zipInputStream.getNextEntry()) != null) {
|
||||
if (entry.getName().endsWith(".json")) {
|
||||
InputStreamReader nextReader = new InputStreamReader(zipInputStream, Constants.CHARSET_UTF8);
|
||||
IBaseResource parsed = myCtx.newJsonParser().parseResource(nextReader);
|
||||
candidateResources.put(entry.getName(), parsed);
|
||||
}
|
||||
}
|
||||
|
||||
ourLog.info("Parsed {} candidateResources in {}ms", candidateResources.size(), sw.getMillis());
|
||||
|
||||
String igResourceName = "ImplementationGuide-ig.json";
|
||||
ImplementationGuide ig = (ImplementationGuide) candidateResources.get(igResourceName);
|
||||
|
||||
if (ig == null) {
|
||||
throw new InternalErrorException("IG Pack '" + theDescription + "' does not contain a resource named: " + igResourceName);
|
||||
}
|
||||
|
||||
// ourLog.info(myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(ig));
|
||||
|
||||
HashMap<String, IBaseResource> newCandidateResources = new HashMap<>();
|
||||
for (IBaseResource next : candidateResources.values()) {
|
||||
newCandidateResources.put(next.getIdElement().toUnqualifiedVersionless().getValue(), next);
|
||||
}
|
||||
candidateResources = newCandidateResources;
|
||||
|
||||
for (ImplementationGuide.ImplementationGuidePackageComponent nextPackage : ig.getPackage()) {
|
||||
ourLog.info("Processing package {}", nextPackage.getName());
|
||||
|
||||
for (ImplementationGuide.ImplementationGuidePackageResourceComponent nextResource : nextPackage.getResource()) {
|
||||
if (isNotBlank(nextResource.getSourceReference().getReference())) {
|
||||
IdType id = new IdType(nextResource.getSourceReference().getReference());
|
||||
if (isNotBlank(id.getResourceType())) {
|
||||
switch (id.getResourceType()) {
|
||||
case "CodeSystem":
|
||||
case "ConceptMap":
|
||||
case "StructureDefinition":
|
||||
case "ValueSet":
|
||||
IBaseResource resource = findResource(candidateResources, id);
|
||||
igResources.put(id.toUnqualifiedVersionless(), resource);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ourLog.info("IG contains {} resources", igResources.size());
|
||||
return new IgPackValidationSupportDstu3(igResources);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new InternalErrorException("Failure while parsing IG: " + e);
|
||||
}
|
||||
|
||||
|
||||
protected FhirVersionEnum provideExpectedVersion() {
|
||||
return FhirVersionEnum.DSTU3;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
package ca.uhn.fhir.igpacks.parser;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* hapi-fhir-igpacks
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2018 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import org.hl7.fhir.instance.hapi.validation.IValidationSupport;
|
||||
import org.hl7.fhir.instance.model.ConceptMap;
|
||||
import org.hl7.fhir.instance.model.StructureDefinition;
|
||||
import org.hl7.fhir.instance.model.ValueSet;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class IgPackValidationSupportDstu2 implements IValidationSupport {
|
||||
private final Map<IIdType, IBaseResource> myIgResources;
|
||||
|
||||
public IgPackValidationSupportDstu2(Map<IIdType, IBaseResource> theIgResources) {
|
||||
myIgResources = theIgResources;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StructureDefinition> allStructures() {
|
||||
ArrayList<StructureDefinition> retVal = new ArrayList<>();
|
||||
|
||||
for (Map.Entry<IIdType, IBaseResource> next : myIgResources.entrySet()) {
|
||||
if (next.getKey().getResourceType().equals("StructureDefinition")) {
|
||||
retVal.add((StructureDefinition) next.getValue());
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueSet.ValueSetExpansionComponent expandValueSet(FhirContext theContext, ValueSet.ConceptSetComponent theInclude) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueSet fetchCodeSystem(FhirContext theContext, String theSystem) {
|
||||
for (Map.Entry<IIdType, IBaseResource> next : myIgResources.entrySet()) {
|
||||
if (next.getKey().getResourceType().equals("ValueSet")) {
|
||||
ValueSet nextVs = (ValueSet) next.getValue();
|
||||
if (theSystem.equals(nextVs.getUrl())) {
|
||||
return nextVs;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
|
||||
for (Map.Entry<IIdType, IBaseResource> next : myIgResources.entrySet()) {
|
||||
if (theClass.equals(ConceptMap.class)) {
|
||||
if (theClass.isAssignableFrom(next.getValue().getClass())) {
|
||||
ConceptMap sd = ((ConceptMap) next.getValue());
|
||||
if (sd.getUrl().equals(theUri)) {
|
||||
return (T) sd;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (theClass.equals(StructureDefinition.class)) {
|
||||
if (theClass.isAssignableFrom(next.getValue().getClass())) {
|
||||
StructureDefinition sd = ((StructureDefinition) next.getValue());
|
||||
if (sd.getUrl().equals(theUri)) {
|
||||
return (T) sd;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (theClass.equals(ValueSet.class)) {
|
||||
if (theClass.isAssignableFrom(next.getValue().getClass())) {
|
||||
ValueSet sd = ((ValueSet) next.getValue());
|
||||
if (sd.getUrl().equals(theUri)) {
|
||||
return (T) sd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
||||
return null;
|
||||
}
|
||||
}
|
Binary file not shown.
|
@ -12,6 +12,7 @@ import org.hl7.fhir.instance.hapi.validation.DefaultProfileValidationSupport;
|
|||
import org.hl7.fhir.instance.hapi.validation.FhirInstanceValidator;
|
||||
import org.hl7.fhir.instance.hapi.validation.ValidationSupportChain;
|
||||
import org.hl7.fhir.instance.utils.IResourceValidator.BestPracticeWarningLevel;
|
||||
import org.hl7.fhir.r4.utils.IResourceValidator;
|
||||
import org.springframework.beans.factory.annotation.Autowire;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
@ -74,7 +75,7 @@ public class BaseDstu2Config extends BaseConfig {
|
|||
@Lazy
|
||||
public IValidatorModule instanceValidatorDstu2() {
|
||||
FhirInstanceValidator retVal = new FhirInstanceValidator();
|
||||
retVal.setBestPracticeWarningLevel(BestPracticeWarningLevel.Warning);
|
||||
retVal.setBestPracticeWarningLevel(IResourceValidator.BestPracticeWarningLevel.Warning);
|
||||
retVal.setValidationSupport(new ValidationSupportChain(new DefaultProfileValidationSupport(), jpaValidationSupportDstu2()));
|
||||
return retVal;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
|||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
import ca.uhn.fhir.rest.param.UriParam;
|
||||
import org.hl7.fhir.instance.model.IdType;
|
||||
import org.hl7.fhir.instance.model.StructureDefinition;
|
||||
import org.hl7.fhir.instance.model.ValueSet;
|
||||
import org.hl7.fhir.instance.model.ValueSet.ConceptSetComponent;
|
||||
import org.hl7.fhir.instance.model.ValueSet.ValueSetExpansionComponent;
|
||||
|
@ -15,6 +16,8 @@ import org.springframework.beans.factory.annotation.Qualifier;
|
|||
|
||||
import javax.transaction.Transactional;
|
||||
import javax.transaction.Transactional.TxType;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
|
@ -61,6 +64,11 @@ public class JpaValidationSupportDstu2 implements IJpaValidationSupportDstu2 {
|
|||
@Qualifier("myFhirContextDstu2")
|
||||
private FhirContext myDstu2Ctx;
|
||||
|
||||
@Override
|
||||
public List<StructureDefinition> allStructures() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(value = TxType.SUPPORTS)
|
||||
public ValueSetExpansionComponent expandValueSet(FhirContext theCtx, ConceptSetComponent theInclude) {
|
||||
|
|
|
@ -23,6 +23,9 @@ package ca.uhn.fhir.rest.server.provider;
|
|||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.annotation.*;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.param.TokenAndListParam;
|
||||
import ca.uhn.fhir.rest.param.TokenOrListParam;
|
||||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
|
@ -32,6 +35,7 @@ import org.slf4j.Logger;
|
|||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* This class is a simple implementation of the resource provider
|
||||
|
@ -56,6 +60,11 @@ public class HashMapResourceProvider<T extends IBaseResource> implements IResour
|
|||
private final String myResourceName;
|
||||
private Map<String, TreeMap<Long, T>> myIdToVersionToResourceMap = new HashMap<>();
|
||||
private long myNextId;
|
||||
private AtomicLong myDeleteCount = new AtomicLong(0);
|
||||
private AtomicLong mySearchCount = new AtomicLong(0);
|
||||
private AtomicLong myUpdateCount = new AtomicLong(0);
|
||||
private AtomicLong myCreateCount = new AtomicLong(0);
|
||||
private AtomicLong myReadCount = new AtomicLong(0);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -79,6 +88,17 @@ public class HashMapResourceProvider<T extends IBaseResource> implements IResour
|
|||
myIdToVersionToResourceMap.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the counts used by {@link #getCountRead()} and other count methods
|
||||
*/
|
||||
public void clearCounts() {
|
||||
myReadCount.set(0L);
|
||||
myUpdateCount.set(0L);
|
||||
myCreateCount.set(0L);
|
||||
myDeleteCount.set(0L);
|
||||
mySearchCount.set(0L);
|
||||
}
|
||||
|
||||
@Create
|
||||
public MethodOutcome create(@ResourceParam T theResource) {
|
||||
long idPart = myNextId++;
|
||||
|
@ -87,6 +107,8 @@ public class HashMapResourceProvider<T extends IBaseResource> implements IResour
|
|||
|
||||
IIdType id = store(theResource, idPartAsString, versionIdPart);
|
||||
|
||||
myCreateCount.incrementAndGet();
|
||||
|
||||
return new MethodOutcome()
|
||||
.setCreated(true)
|
||||
.setId(id);
|
||||
|
@ -99,13 +121,56 @@ public class HashMapResourceProvider<T extends IBaseResource> implements IResour
|
|||
throw new ResourceNotFoundException(theId);
|
||||
}
|
||||
|
||||
|
||||
long nextVersion = versions.lastEntry().getKey() + 1L;
|
||||
IIdType id = store(null, theId.getIdPart(), nextVersion);
|
||||
|
||||
myDeleteCount.incrementAndGet();
|
||||
|
||||
return new MethodOutcome()
|
||||
.setId(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns a simple operation count. This is mostly
|
||||
* useful for testing purposes.
|
||||
*/
|
||||
public long getCountCreate() {
|
||||
return myCreateCount.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns a simple operation count. This is mostly
|
||||
* useful for testing purposes.
|
||||
*/
|
||||
public long getCountDelete() {
|
||||
return myDeleteCount.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns a simple operation count. This is mostly
|
||||
* useful for testing purposes.
|
||||
*/
|
||||
public long getCountRead() {
|
||||
return myReadCount.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns a simple operation count. This is mostly
|
||||
* useful for testing purposes.
|
||||
*/
|
||||
public long getCountSearch() {
|
||||
return mySearchCount.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns a simple operation count. This is mostly
|
||||
* useful for testing purposes.
|
||||
*/
|
||||
public long getCountUpdate() {
|
||||
return myUpdateCount.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return myResourceType;
|
||||
|
@ -113,7 +178,7 @@ public class HashMapResourceProvider<T extends IBaseResource> implements IResour
|
|||
|
||||
private synchronized TreeMap<Long, T> getVersionToResource(String theIdPart) {
|
||||
if (!myIdToVersionToResourceMap.containsKey(theIdPart)) {
|
||||
myIdToVersionToResourceMap.put(theIdPart, new TreeMap<Long, T>());
|
||||
myIdToVersionToResourceMap.put(theIdPart, new TreeMap<>());
|
||||
}
|
||||
return myIdToVersionToResourceMap.get(theIdPart);
|
||||
}
|
||||
|
@ -125,6 +190,7 @@ public class HashMapResourceProvider<T extends IBaseResource> implements IResour
|
|||
throw new ResourceNotFoundException(theId);
|
||||
}
|
||||
|
||||
T retVal;
|
||||
if (theId.hasVersionIdPart()) {
|
||||
Long versionId = theId.getVersionIdPartAsLong();
|
||||
if (!versions.containsKey(versionId)) {
|
||||
|
@ -134,23 +200,52 @@ public class HashMapResourceProvider<T extends IBaseResource> implements IResour
|
|||
if (resource == null) {
|
||||
throw new ResourceGoneException(theId);
|
||||
}
|
||||
return resource;
|
||||
retVal = resource;
|
||||
}
|
||||
|
||||
} else {
|
||||
return versions.lastEntry().getValue();
|
||||
retVal = versions.lastEntry().getValue();
|
||||
}
|
||||
|
||||
myReadCount.incrementAndGet();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Search
|
||||
public List<IBaseResource> search() {
|
||||
public List<IBaseResource> search(
|
||||
@OptionalParam(name = "_id") TokenAndListParam theIds) {
|
||||
|
||||
List<IBaseResource> retVal = new ArrayList<>();
|
||||
|
||||
for (TreeMap<Long, T> next : myIdToVersionToResourceMap.values()) {
|
||||
if (next.isEmpty() == false) {
|
||||
retVal.add(next.lastEntry().getValue());
|
||||
T nextResource = next.lastEntry().getValue();
|
||||
|
||||
boolean matches = true;
|
||||
if (theIds != null && theIds.getValuesAsQueryTokens().size() > 0) {
|
||||
for (TokenOrListParam nextIdAnd : theIds.getValuesAsQueryTokens()) {
|
||||
matches = false;
|
||||
for (TokenParam nextOr : nextIdAnd.getValuesAsQueryTokens()) {
|
||||
if (nextOr.getValue().equals(nextResource.getIdElement().getIdPart())) {
|
||||
matches = true;
|
||||
}
|
||||
}
|
||||
if (!matches) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!matches) {
|
||||
continue;
|
||||
}
|
||||
|
||||
retVal.add(nextResource);
|
||||
}
|
||||
}
|
||||
|
||||
mySearchCount.incrementAndGet();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
@ -188,6 +283,8 @@ public class HashMapResourceProvider<T extends IBaseResource> implements IResour
|
|||
|
||||
IIdType id = store(theResource, idPartAsString, versionIdPart);
|
||||
|
||||
myUpdateCount.incrementAndGet();
|
||||
|
||||
return new MethodOutcome()
|
||||
.setCreated(created)
|
||||
.setId(id);
|
||||
|
|
|
@ -140,6 +140,8 @@ public class ContactPoint extends Type implements ICompositeType {
|
|||
return ContactPointSystem.PAGER;
|
||||
if ("other".equals(codeString))
|
||||
return ContactPointSystem.OTHER;
|
||||
if ("url".equals(codeString))
|
||||
return ContactPointSystem.OTHER;
|
||||
throw new IllegalArgumentException("Unknown ContactPointSystem code '"+codeString+"'");
|
||||
}
|
||||
public Enumeration<ContactPointSystem> fromType(Base code) throws FHIRException {
|
||||
|
|
|
@ -118,6 +118,8 @@ public class StructureDefinition extends DomainResource {
|
|||
return StructureDefinitionKind.RESOURCE;
|
||||
if ("logical".equals(codeString))
|
||||
return StructureDefinitionKind.LOGICAL;
|
||||
if ("complex-type".equals(codeString))
|
||||
return StructureDefinitionKind.DATATYPE;
|
||||
throw new IllegalArgumentException("Unknown StructureDefinitionKind code '"+codeString+"'");
|
||||
}
|
||||
public Enumeration<StructureDefinitionKind> fromType(Base code) throws FHIRException {
|
||||
|
|
|
@ -21,7 +21,10 @@ import org.junit.BeforeClass;
|
|||
import org.junit.Test;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.hamcrest.Matchers.matchesPattern;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
|
@ -31,11 +34,14 @@ public class HashMapResourceProviderTest {
|
|||
private static Server ourListenerServer;
|
||||
private static IGenericClient ourClient;
|
||||
private static FhirContext ourCtx = FhirContext.forR4();
|
||||
|
||||
private static HashMapResourceProvider<Patient> myPatientResourceProvider;
|
||||
private static HashMapResourceProvider<Observation> myObservationResourceProvider;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
ourRestServer.clearData();
|
||||
myPatientResourceProvider.clearCounts();
|
||||
myObservationResourceProvider.clearCounts();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -50,6 +56,8 @@ public class HashMapResourceProviderTest {
|
|||
// Read
|
||||
p = (Patient) ourClient.read().resource("Patient").withId(id).execute();
|
||||
assertEquals(true, p.getActive());
|
||||
|
||||
assertEquals(1, myPatientResourceProvider.getCountRead());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -76,8 +84,12 @@ public class HashMapResourceProviderTest {
|
|||
assertThat(id.getIdPart(), matchesPattern("[0-9]+"));
|
||||
assertEquals("1", id.getVersionIdPart());
|
||||
|
||||
assertEquals(0, myPatientResourceProvider.getCountDelete());
|
||||
|
||||
ourClient.delete().resourceById(id.toUnqualifiedVersionless()).execute();
|
||||
|
||||
assertEquals(1, myPatientResourceProvider.getCountDelete());
|
||||
|
||||
// Read
|
||||
ourClient.read().resource("Patient").withId(id.withVersion("1")).execute();
|
||||
try {
|
||||
|
@ -103,6 +115,56 @@ public class HashMapResourceProviderTest {
|
|||
Bundle resp = ourClient.search().forResource("Patient").returnBundle(Bundle.class).execute();
|
||||
assertEquals(100, resp.getTotal());
|
||||
assertEquals(100, resp.getEntry().size());
|
||||
|
||||
assertEquals(1, myPatientResourceProvider.getCountSearch());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchById() {
|
||||
// Create
|
||||
for (int i = 0; i < 100; i++) {
|
||||
Patient p = new Patient();
|
||||
p.addName().setFamily("FAM" + i);
|
||||
IIdType id = ourClient.create().resource(p).execute().getId();
|
||||
assertThat(id.getIdPart(), matchesPattern("[0-9]+"));
|
||||
assertEquals("1", id.getVersionIdPart());
|
||||
}
|
||||
|
||||
// Search
|
||||
Bundle resp = ourClient
|
||||
.search()
|
||||
.forResource("Patient")
|
||||
.where(Patient.RES_ID.exactly().codes("2", "3"))
|
||||
.returnBundle(Bundle.class).execute();
|
||||
assertEquals(2, resp.getTotal());
|
||||
assertEquals(2, resp.getEntry().size());
|
||||
List<String> respIds = resp.getEntry().stream().map(t -> t.getResource().getIdElement().toUnqualifiedVersionless().getValue()).collect(Collectors.toList());
|
||||
assertThat(respIds, containsInAnyOrder("Patient/2", "Patient/3"));
|
||||
|
||||
// Search
|
||||
resp = ourClient
|
||||
.search()
|
||||
.forResource("Patient")
|
||||
.where(Patient.RES_ID.exactly().codes("2", "3"))
|
||||
.where(Patient.RES_ID.exactly().codes("2", "3"))
|
||||
.returnBundle(Bundle.class).execute();
|
||||
assertEquals(2, resp.getTotal());
|
||||
assertEquals(2, resp.getEntry().size());
|
||||
respIds = resp.getEntry().stream().map(t -> t.getResource().getIdElement().toUnqualifiedVersionless().getValue()).collect(Collectors.toList());
|
||||
assertThat(respIds, containsInAnyOrder("Patient/2", "Patient/3"));
|
||||
|
||||
resp = ourClient
|
||||
.search()
|
||||
.forResource("Patient")
|
||||
.where(Patient.RES_ID.exactly().codes("2", "3"))
|
||||
.where(Patient.RES_ID.exactly().codes("4", "3"))
|
||||
.returnBundle(Bundle.class).execute();
|
||||
respIds = resp.getEntry().stream().map(t -> t.getResource().getIdElement().toUnqualifiedVersionless().getValue()).collect(Collectors.toList());
|
||||
assertThat(respIds, containsInAnyOrder("Patient/3"));
|
||||
assertEquals(1, resp.getTotal());
|
||||
assertEquals(1, resp.getEntry().size());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -122,6 +184,9 @@ public class HashMapResourceProviderTest {
|
|||
assertThat(id.getIdPart(), matchesPattern("[0-9]+"));
|
||||
assertEquals("2", id.getVersionIdPart());
|
||||
|
||||
assertEquals(1, myPatientResourceProvider.getCountCreate());
|
||||
assertEquals(1, myPatientResourceProvider.getCountUpdate());
|
||||
|
||||
// Read
|
||||
p = (Patient) ourClient.read().resource("Patient").withId(id.withVersion("1")).execute();
|
||||
assertEquals(true, p.getActive());
|
||||
|
@ -147,6 +212,8 @@ public class HashMapResourceProviderTest {
|
|||
ourRestServer = new MyRestfulServer();
|
||||
String ourBase = "http://localhost:" + ourListenerPort + "/";
|
||||
ourListenerServer = new Server(ourListenerPort);
|
||||
|
||||
ourCtx.getRestfulClientFactory().setSocketTimeout(120000);
|
||||
ourClient = ourCtx.newRestfulGenericClient(ourBase);
|
||||
|
||||
ServletContextHandler proxyHandler = new ServletContextHandler();
|
||||
|
@ -161,6 +228,7 @@ public class HashMapResourceProviderTest {
|
|||
}
|
||||
|
||||
private static class MyRestfulServer extends RestfulServer {
|
||||
|
||||
MyRestfulServer() {
|
||||
super(ourCtx);
|
||||
}
|
||||
|
@ -177,8 +245,10 @@ public class HashMapResourceProviderTest {
|
|||
protected void initialize() throws ServletException {
|
||||
super.initialize();
|
||||
|
||||
registerProvider(new HashMapResourceProvider<>(ourCtx, Patient.class));
|
||||
registerProvider(new HashMapResourceProvider<>(ourCtx, Observation.class));
|
||||
myPatientResourceProvider = new HashMapResourceProvider<>(ourCtx, Patient.class);
|
||||
myObservationResourceProvider = new HashMapResourceProvider<>(ourCtx, Observation.class);
|
||||
registerProvider(myPatientResourceProvider);
|
||||
registerProvider(myObservationResourceProvider);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -25,6 +25,19 @@ public class BundleUtilTest {
|
|||
Assert.assertEquals(null, BundleUtil.getLinkUrlOfType(ourCtx, b, "next"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTotal() {
|
||||
Bundle b = new Bundle();
|
||||
b.setTotal(999);
|
||||
Assert.assertEquals(999, BundleUtil.getTotal(ourCtx, b).intValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTotalNull() {
|
||||
Bundle b = new Bundle();
|
||||
Assert.assertEquals(null, BundleUtil.getTotal(ourCtx, b));
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() {
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
|
|
|
@ -3,11 +3,8 @@ package org.hl7.fhir.instance.hapi.validation;
|
|||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.hl7.fhir.instance.model.Bundle;
|
||||
import org.hl7.fhir.instance.model.*;
|
||||
import org.hl7.fhir.instance.model.Bundle.BundleEntryComponent;
|
||||
import org.hl7.fhir.instance.model.CodeType;
|
||||
import org.hl7.fhir.instance.model.IdType;
|
||||
import org.hl7.fhir.instance.model.ValueSet;
|
||||
import org.hl7.fhir.instance.model.ValueSet.ConceptSetComponent;
|
||||
import org.hl7.fhir.instance.model.ValueSet.ValueSetExpansionComponent;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
@ -16,8 +13,7 @@ import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
|
|||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
|
@ -25,6 +21,13 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
|
|||
|
||||
private Map<String, ValueSet> myDefaultValueSets;
|
||||
private Map<String, ValueSet> myCodeSystems;
|
||||
private static final Set<String> ourResourceNames;
|
||||
private static final FhirContext ourHl7OrgCtx;
|
||||
|
||||
static {
|
||||
ourHl7OrgCtx = FhirContext.forDstu2Hl7Org();
|
||||
ourResourceNames = FhirContext.forDstu2().getResourceNames();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -33,6 +36,18 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
|
|||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StructureDefinition> allStructures() {
|
||||
ArrayList<StructureDefinition> retVal = new ArrayList<>();
|
||||
|
||||
for (String next : ourResourceNames) {
|
||||
StructureDefinition profile = FhirInstanceValidator.loadProfileOrReturnNull(null, ourHl7OrgCtx, next);
|
||||
retVal.add(profile);
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) {
|
||||
return null;
|
||||
|
@ -43,7 +58,7 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
|
|||
synchronized (this) {
|
||||
Map<String, ValueSet> valueSets = myCodeSystems;
|
||||
if (valueSets == null) {
|
||||
valueSets = new HashMap<String, ValueSet>();
|
||||
valueSets = new HashMap<>();
|
||||
|
||||
loadValueSets(theContext, valueSets, "/org/hl7/fhir/instance/model/valueset/valuesets.xml");
|
||||
loadValueSets(theContext, valueSets, "/org/hl7/fhir/instance/model/valueset/v2-tables.xml");
|
||||
|
@ -78,7 +93,7 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
|
|||
throw new InternalErrorException("UTF-8 encoding not supported on this platform", e);
|
||||
}
|
||||
|
||||
defaultValueSets = new HashMap<String, ValueSet>();
|
||||
defaultValueSets = new HashMap<>();
|
||||
|
||||
FhirContext ctx = FhirInstanceValidator.getHl7OrgDstu2Ctx(theContext);
|
||||
Bundle bundle = ctx.newXmlParser().parseResource(Bundle.class, reader);
|
||||
|
|
|
@ -1,45 +1,73 @@
|
|||
package org.hl7.fhir.instance.hapi.validation;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.instance.model.StructureDefinition;
|
||||
import org.hl7.fhir.instance.utils.IResourceValidator.BestPracticeWarningLevel;
|
||||
import org.hl7.fhir.instance.utils.IResourceValidator.IdStatus;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
|
||||
import org.w3c.dom.*;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import com.google.gson.*;
|
||||
|
||||
import ca.uhn.fhir.context.*;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.validation.IValidationContext;
|
||||
import ca.uhn.fhir.validation.IValidatorModule;
|
||||
import com.github.benmanes.caffeine.cache.CacheLoader;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonObject;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.hl7.fhir.convertors.VersionConvertorAdvisor40;
|
||||
import org.hl7.fhir.convertors.VersionConvertor_10_40;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.exceptions.TerminologyServiceException;
|
||||
import org.hl7.fhir.instance.model.*;
|
||||
import org.hl7.fhir.r4.context.IWorkerContext;
|
||||
import org.hl7.fhir.r4.formats.IParser;
|
||||
import org.hl7.fhir.r4.formats.ParserType;
|
||||
import org.hl7.fhir.r4.model.CodeSystem;
|
||||
import org.hl7.fhir.r4.terminologies.ValueSetExpander;
|
||||
import org.hl7.fhir.r4.utils.FHIRPathEngine;
|
||||
import org.hl7.fhir.r4.utils.INarrativeGenerator;
|
||||
import org.hl7.fhir.r4.utils.IResourceValidator;
|
||||
import org.hl7.fhir.r4.utils.IResourceValidator.BestPracticeWarningLevel;
|
||||
import org.hl7.fhir.r4.utils.IResourceValidator.IdStatus;
|
||||
import org.hl7.fhir.r4.validation.InstanceValidator;
|
||||
import org.hl7.fhir.utilities.TranslationServices;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.StringReader;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
|
||||
public class FhirInstanceValidator extends BaseValidatorBridge implements IValidatorModule {
|
||||
|
||||
private static FhirContext ourHl7OrgCtx;
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirInstanceValidator.class);
|
||||
private static final FhirContext FHIR_CONTEXT = FhirContext.forDstu2();
|
||||
private static FhirContext ourHl7OrgCtx;
|
||||
|
||||
private boolean myAnyExtensionsAllowed = true;
|
||||
private BestPracticeWarningLevel myBestPracticeWarningLevel;
|
||||
private DocumentBuilderFactory myDocBuilderFactory;
|
||||
private StructureDefinition myStructureDefintion;
|
||||
private IValidationSupport myValidationSupport;
|
||||
private boolean noTerminologyChecks = false;
|
||||
private volatile WorkerContextWrapper myWrappedWorkerContext;
|
||||
private VersionConvertorAdvisor40 myAdvisor = new NullAdvisor();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* Uses {@link DefaultProfileValidationSupport} for {@link IValidationSupport
|
||||
* validation support}
|
||||
* <p>
|
||||
* Uses {@link DefaultProfileValidationSupport} for {@link IValidationSupport validation support}
|
||||
*/
|
||||
public FhirInstanceValidator() {
|
||||
this(new DefaultProfileValidationSupport());
|
||||
|
@ -48,8 +76,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
/**
|
||||
* Constructor which uses the given validation support
|
||||
*
|
||||
* @param theValidationSupport
|
||||
* The validation support
|
||||
* @param theValidationSupport The validation support
|
||||
*/
|
||||
public FhirInstanceValidator(IValidationSupport theValidationSupport) {
|
||||
myDocBuilderFactory = DocumentBuilderFactory.newInstance();
|
||||
|
@ -57,6 +84,35 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
myValidationSupport = theValidationSupport;
|
||||
}
|
||||
|
||||
private CodeSystem convertCodeSystem(ValueSet theFetched) {
|
||||
CodeSystem retVal = new CodeSystem();
|
||||
|
||||
retVal.setUrl(theFetched.getCodeSystem().getSystem());
|
||||
retVal.setVersion(theFetched.getVersion());
|
||||
|
||||
List<ValueSet.ConceptDefinitionComponent> sourceConceptList = theFetched.getCodeSystem().getConcept();
|
||||
List<CodeSystem.ConceptDefinitionComponent> targetConceptList = retVal.getConcept();
|
||||
convertConceptList(sourceConceptList, targetConceptList);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private CodeSystem.ConceptDefinitionComponent convertConceptDefinition(ValueSet.ConceptDefinitionComponent next) {
|
||||
CodeSystem.ConceptDefinitionComponent convertedConceptDef = new CodeSystem.ConceptDefinitionComponent();
|
||||
convertedConceptDef.setCode(next.getCode());
|
||||
convertedConceptDef.setDisplay(next.getDisplay());
|
||||
|
||||
convertConceptList(next.getConcept(), convertedConceptDef.getConcept());
|
||||
return convertedConceptDef;
|
||||
}
|
||||
|
||||
private void convertConceptList(List<ValueSet.ConceptDefinitionComponent> theSourceConceptList, List<CodeSystem.ConceptDefinitionComponent> theTargetConceptList) {
|
||||
for (ValueSet.ConceptDefinitionComponent next : theSourceConceptList) {
|
||||
CodeSystem.ConceptDefinitionComponent convertedConceptDef = convertConceptDefinition(next);
|
||||
theTargetConceptList.add(convertedConceptDef);
|
||||
}
|
||||
}
|
||||
|
||||
private String determineResourceName(Document theDocument) {
|
||||
Element root = null;
|
||||
|
||||
|
@ -71,15 +127,18 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
return root.getLocalName();
|
||||
}
|
||||
|
||||
private StructureDefinition findStructureDefinitionForResourceName(final FhirContext theCtx, String resourceName) {
|
||||
String sdName = "http://hl7.org/fhir/StructureDefinition/" + resourceName;
|
||||
StructureDefinition profile = myStructureDefintion != null ? myStructureDefintion : myValidationSupport.fetchResource(theCtx, StructureDefinition.class, sdName);
|
||||
return profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the "best practice" warning level (default is
|
||||
* {@link BestPracticeWarningLevel#Hint}).
|
||||
* Returns the "best practice" warning level (default is {@link BestPracticeWarningLevel#Hint}).
|
||||
* <p>
|
||||
* The FHIR Instance Validator has a number of checks for best practices in
|
||||
* terms of FHIR usage. If this setting is set to
|
||||
* {@link BestPracticeWarningLevel#Error}, any resource data which does not
|
||||
* meet these best practices will be reported at the ERROR level. If this
|
||||
* setting is set to {@link BestPracticeWarningLevel#Ignore}, best practice
|
||||
* The FHIR Instance Validator has a number of checks for best practices in terms of FHIR usage. If this setting is
|
||||
* set to {@link BestPracticeWarningLevel#Error}, any resource data which does not meet these best practices will be
|
||||
* reported at the ERROR level. If this setting is set to {@link BestPracticeWarningLevel#Ignore}, best practice
|
||||
* guielines will be ignored.
|
||||
* </p>
|
||||
*
|
||||
|
@ -90,67 +149,98 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link IValidationSupport validation support} in use by this
|
||||
* validator. Default is an instance of
|
||||
* {@link DefaultProfileValidationSupport} if the no-arguments constructor for
|
||||
* this object was used.
|
||||
*/
|
||||
public IValidationSupport getValidationSupport() {
|
||||
return myValidationSupport;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the "best practice warning level". When validating, any deviations
|
||||
* from best practices will be reported at this level.
|
||||
* Sets the "best practice warning level". When validating, any deviations from best practices will be reported at
|
||||
* this level.
|
||||
* <p>
|
||||
* The FHIR Instance Validator has a number of checks for best practices in
|
||||
* terms of FHIR usage. If this setting is set to
|
||||
* {@link BestPracticeWarningLevel#Error}, any resource data which does not
|
||||
* meet these best practices will be reported at the ERROR level. If this
|
||||
* setting is set to {@link BestPracticeWarningLevel#Ignore}, best practice
|
||||
* The FHIR Instance Validator has a number of checks for best practices in terms of FHIR usage. If this setting is
|
||||
* set to {@link BestPracticeWarningLevel#Error}, any resource data which does not meet these best practices will be
|
||||
* reported at the ERROR level. If this setting is set to {@link BestPracticeWarningLevel#Ignore}, best practice
|
||||
* guielines will be ignored.
|
||||
* </p>
|
||||
*
|
||||
* @param theBestPracticeWarningLevel
|
||||
* The level, must not be <code>null</code>
|
||||
* @param theBestPracticeWarningLevel The level, must not be <code>null</code>
|
||||
*/
|
||||
public void setBestPracticeWarningLevel(BestPracticeWarningLevel theBestPracticeWarningLevel) {
|
||||
Validate.notNull(theBestPracticeWarningLevel);
|
||||
myBestPracticeWarningLevel = theBestPracticeWarningLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link IValidationSupport validation support} in use by this validator. Default is an instance of
|
||||
* {@link DefaultProfileValidationSupport} if the no-arguments constructor for this object was used.
|
||||
*/
|
||||
public IValidationSupport getValidationSupport() {
|
||||
return myValidationSupport;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link IValidationSupport validation support} in use by this validator. Default is an instance of
|
||||
* {@link DefaultProfileValidationSupport} if the no-arguments constructor for this object was used.
|
||||
*/
|
||||
public void setValidationSupport(IValidationSupport theValidationSupport) {
|
||||
myValidationSupport = theValidationSupport;
|
||||
myWrappedWorkerContext = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to {@literal true} (default is true) extensions which are not known to the
|
||||
* validator (e.g. because they have not been explicitly declared in a profile) will
|
||||
* be validated but will not cause an error.
|
||||
*/
|
||||
public boolean isAnyExtensionsAllowed() {
|
||||
return myAnyExtensionsAllowed;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to {@literal true} (default is true) extensions which are not known to the
|
||||
* validator (e.g. because they have not been explicitly declared in a profile) will
|
||||
* be validated but will not cause an error.
|
||||
*/
|
||||
public void setAnyExtensionsAllowed(boolean theAnyExtensionsAllowed) {
|
||||
myAnyExtensionsAllowed = theAnyExtensionsAllowed;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to {@literal true} (default is false) the valueSet will not be validate
|
||||
*/
|
||||
public boolean isNoTerminologyChecks() {
|
||||
return noTerminologyChecks;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to {@literal true} (default is false) the valueSet will not be validate
|
||||
*/
|
||||
public void setNoTerminologyChecks(final boolean theNoTerminologyChecks) {
|
||||
noTerminologyChecks = theNoTerminologyChecks;
|
||||
}
|
||||
|
||||
public void setStructureDefintion(StructureDefinition theStructureDefintion) {
|
||||
myStructureDefintion = theStructureDefintion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link IValidationSupport validation support} in use by this
|
||||
* validator. Default is an instance of
|
||||
* {@link DefaultProfileValidationSupport} if the no-arguments constructor for
|
||||
* this object was used.
|
||||
*/
|
||||
public void setValidationSupport(IValidationSupport theValidationSupport) {
|
||||
myValidationSupport = theValidationSupport;
|
||||
}
|
||||
|
||||
protected List<ValidationMessage> validate(final FhirContext theCtx, String theInput, EncodingEnum theEncoding) {
|
||||
HapiWorkerContext workerContext = new HapiWorkerContext(theCtx, myValidationSupport);
|
||||
|
||||
org.hl7.fhir.instance.validation.InstanceValidator v;
|
||||
WorkerContextWrapper wrappedWorkerContext = myWrappedWorkerContext;
|
||||
if (wrappedWorkerContext == null) {
|
||||
HapiWorkerContext workerContext = new HapiWorkerContext(theCtx, myValidationSupport);
|
||||
wrappedWorkerContext = new WorkerContextWrapper(workerContext);
|
||||
}
|
||||
myWrappedWorkerContext = wrappedWorkerContext;
|
||||
|
||||
InstanceValidator v;
|
||||
FHIRPathEngine.IEvaluationContext evaluationCtx = new org.hl7.fhir.r4.hapi.validation.FhirInstanceValidator.NullEvaluationContext();
|
||||
try {
|
||||
v = new org.hl7.fhir.instance.validation.InstanceValidator(workerContext);
|
||||
v = new InstanceValidator(wrappedWorkerContext, evaluationCtx);
|
||||
} catch (Exception e) {
|
||||
throw new ConfigurationException(e);
|
||||
}
|
||||
|
||||
// v.setShouldCheckForIdPresence(false);
|
||||
// v.setRequireResourceId(false);
|
||||
v.setBestPracticeWarningLevel(getBestPracticeWarningLevel());
|
||||
v.setAnyExtensionsAllowed(isAnyExtensionsAllowed());
|
||||
v.setResourceIdRule(IdStatus.OPTIONAL);
|
||||
v.setBestPracticeWarningLevel(myBestPracticeWarningLevel);
|
||||
v.setAnyExtensionsAllowed(true);
|
||||
v.setSuppressLoincSnomedMessages(true);
|
||||
v.setNoTerminologyChecks(isNoTerminologyChecks());
|
||||
|
||||
List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
|
||||
List<ValidationMessage> messages = new ArrayList<>();
|
||||
|
||||
if (theEncoding == EncodingEnum.XML) {
|
||||
Document document;
|
||||
|
@ -167,10 +257,10 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
}
|
||||
|
||||
String resourceName = determineResourceName(document);
|
||||
StructureDefinition profile = myStructureDefintion != null ? myStructureDefintion : loadProfileOrReturnNull(messages, theCtx, resourceName);
|
||||
StructureDefinition profile = findStructureDefinitionForResourceName(theCtx, resourceName);
|
||||
if (profile != null) {
|
||||
try {
|
||||
v.validate(messages, document, profile);
|
||||
v.validate(null, messages, document, profile.getUrl());
|
||||
} catch (Exception e) {
|
||||
throw new InternalErrorException("Unexpected failure while validating resource", e);
|
||||
}
|
||||
|
@ -180,10 +270,10 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
JsonObject json = gson.fromJson(theInput, JsonObject.class);
|
||||
|
||||
String resourceName = json.get("resourceType").getAsString();
|
||||
StructureDefinition profile = myStructureDefintion != null ? myStructureDefintion : loadProfileOrReturnNull(messages, theCtx, resourceName);
|
||||
StructureDefinition profile = findStructureDefinitionForResourceName(theCtx, resourceName);
|
||||
if (profile != null) {
|
||||
try {
|
||||
v.validate(messages, json, profile);
|
||||
v.validate(null, messages, json, profile.getUrl());
|
||||
} catch (Exception e) {
|
||||
throw new InternalErrorException("Unexpected failure while validating resource", e);
|
||||
}
|
||||
|
@ -198,6 +288,10 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
messages.remove(i);
|
||||
i--;
|
||||
}
|
||||
if (next.getLocation().contains("text")) {
|
||||
messages.remove(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
return messages;
|
||||
}
|
||||
|
@ -232,8 +326,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
String profileClasspath = theCtx.getVersion().getPathToSchemaDefinitions().replace("/schema", "/profile");
|
||||
String profileCpName = profileClasspath + '/' + theResourceName.toLowerCase() + ".profile.xml";
|
||||
String profileText;
|
||||
try {
|
||||
InputStream inputStream = FhirInstanceValidator.class.getResourceAsStream(profileCpName);
|
||||
try (InputStream inputStream = FhirInstanceValidator.class.getResourceAsStream(profileCpName)) {
|
||||
if (inputStream == null) {
|
||||
if (theMessages != null) {
|
||||
theMessages.add(new ValidationMessage().setLevel(IssueSeverity.FATAL)
|
||||
|
@ -256,4 +349,472 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
return profile;
|
||||
}
|
||||
|
||||
private class WorkerContextWrapper implements IWorkerContext {
|
||||
private final HapiWorkerContext myWrap;
|
||||
private final VersionConvertor_10_40 myConverter;
|
||||
private volatile List<org.hl7.fhir.r4.model.StructureDefinition> myAllStructures;
|
||||
private LoadingCache<ResourceKey, org.hl7.fhir.r4.model.Resource> myFetchResourceCache
|
||||
= Caffeine.newBuilder()
|
||||
.expireAfterWrite(10, TimeUnit.SECONDS)
|
||||
.maximumSize(10000)
|
||||
.build(new CacheLoader<ResourceKey, org.hl7.fhir.r4.model.Resource>() {
|
||||
@Override
|
||||
public org.hl7.fhir.r4.model.Resource load(FhirInstanceValidator.ResourceKey key) throws Exception {
|
||||
org.hl7.fhir.instance.model.Resource fetched;
|
||||
switch (key.getResourceName()) {
|
||||
case "StructureDefinition":
|
||||
fetched = myWrap.fetchResource(StructureDefinition.class, key.getUri());
|
||||
break;
|
||||
case "ValueSet":
|
||||
fetched = myWrap.fetchResource(ValueSet.class, key.getUri());
|
||||
break;
|
||||
case "CodeSystem":
|
||||
fetched = myWrap.fetchResource(ValueSet.class, key.getUri());
|
||||
break;
|
||||
case "Questionnaire":
|
||||
fetched = myWrap.fetchResource(Questionnaire.class, key.getUri());
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedOperationException("Don't know how to fetch " + key.getResourceName());
|
||||
}
|
||||
|
||||
if (fetched == null) {
|
||||
if (key.getUri().equals("http://hl7.org/fhir/StructureDefinition/xhtml")) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
org.hl7.fhir.r4.model.Resource converted = new VersionConvertor_10_40(myAdvisor).convertResource(fetched);
|
||||
|
||||
if (fetched instanceof StructureDefinition) {
|
||||
StructureDefinition fetchedSd = (StructureDefinition) fetched;
|
||||
StructureDefinition.StructureDefinitionKind kind = fetchedSd.getKind();
|
||||
if (kind == StructureDefinition.StructureDefinitionKind.DATATYPE) {
|
||||
BaseRuntimeElementDefinition<?> element = FHIR_CONTEXT.getElementDefinition(fetchedSd.getName());
|
||||
if (element instanceof RuntimePrimitiveDatatypeDefinition) {
|
||||
org.hl7.fhir.r4.model.StructureDefinition convertedSd = (org.hl7.fhir.r4.model.StructureDefinition) converted;
|
||||
convertedSd.setKind(org.hl7.fhir.r4.model.StructureDefinition.StructureDefinitionKind.PRIMITIVETYPE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return converted;
|
||||
} catch (FHIRException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
public WorkerContextWrapper(HapiWorkerContext theWorkerContext) {
|
||||
myWrap = theWorkerContext;
|
||||
myConverter = new VersionConvertor_10_40(myAdvisor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<org.hl7.fhir.r4.model.MetadataResource> allConformanceResources() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<org.hl7.fhir.r4.model.StructureDefinition> allStructures() {
|
||||
|
||||
List<org.hl7.fhir.r4.model.StructureDefinition> retVal = myAllStructures;
|
||||
if (retVal == null) {
|
||||
retVal = new ArrayList<>();
|
||||
for (StructureDefinition next : myWrap.allStructures()) {
|
||||
try {
|
||||
org.hl7.fhir.r4.model.StructureDefinition converted = new VersionConvertor_10_40(myAdvisor).convertStructureDefinition(next);
|
||||
if (converted != null) {
|
||||
retVal.add(converted);
|
||||
}
|
||||
} catch (FHIRException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
}
|
||||
myAllStructures = retVal;
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cacheResource(org.hl7.fhir.r4.model.Resource res) throws FHIRException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private ValidationResult convertValidationResult(org.hl7.fhir.instance.utils.IWorkerContext.ValidationResult theResult) {
|
||||
IssueSeverity issueSeverity = theResult.getSeverity();
|
||||
String message = theResult.getMessage();
|
||||
org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionComponent conceptDefinition = null;
|
||||
if (theResult.asConceptDefinition() != null) {
|
||||
conceptDefinition = convertConceptDefinition(theResult.asConceptDefinition());
|
||||
}
|
||||
|
||||
ValidationResult retVal = new ValidationResult(issueSeverity, message, conceptDefinition);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueSetExpander.ValueSetExpansionOutcome expandVS(org.hl7.fhir.r4.model.ValueSet source, boolean cacheOk, boolean heiarchical) {
|
||||
ValueSet convertedSource = null;
|
||||
try {
|
||||
convertedSource = new VersionConvertor_10_40(myAdvisor).convertValueSet(source);
|
||||
} catch (FHIRException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
org.hl7.fhir.instance.terminologies.ValueSetExpander.ValueSetExpansionOutcome expanded = myWrap.expandVS(convertedSource, cacheOk);
|
||||
|
||||
org.hl7.fhir.r4.model.ValueSet convertedResult = null;
|
||||
if (expanded.getValueset() != null) {
|
||||
try {
|
||||
convertedResult = new VersionConvertor_10_40(myAdvisor).convertValueSet(expanded.getValueset());
|
||||
} catch (FHIRException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
}
|
||||
|
||||
String error = expanded.getError();
|
||||
ValueSetExpander.TerminologyServiceErrorClass result = null;
|
||||
|
||||
return new ValueSetExpander.ValueSetExpansionOutcome(convertedResult, error, result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueSetExpander.ValueSetExpansionOutcome expandVS(org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionBindingComponent binding, boolean cacheOk, boolean heiarchical) throws FHIRException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent expandVS(org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent inc, boolean heirarchical) throws TerminologyServiceException {
|
||||
ValueSet.ConceptSetComponent convertedInc = null;
|
||||
if (inc != null) {
|
||||
try {
|
||||
convertedInc = new VersionConvertor_10_40(myAdvisor).convertConceptSetComponent(inc);
|
||||
} catch (FHIRException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
}
|
||||
|
||||
ValueSet.ValueSetExpansionComponent expansion = myWrap.expandVS(convertedInc);
|
||||
org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent retVal = null;
|
||||
if (expansion != null) {
|
||||
try {
|
||||
retVal = new VersionConvertor_10_40(myAdvisor).convertValueSetExpansionComponent(expansion);
|
||||
} catch (FHIRException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.hl7.fhir.r4.model.CodeSystem fetchCodeSystem(String system) {
|
||||
ValueSet fetched = myWrap.fetchCodeSystem(system);
|
||||
if (fetched == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return convertCodeSystem(fetched);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends org.hl7.fhir.r4.model.Resource> T fetchResource(Class<T> class_, String uri) {
|
||||
|
||||
ResourceKey key = new ResourceKey(class_.getSimpleName(), uri);
|
||||
@SuppressWarnings("unchecked")
|
||||
T retVal = (T) myFetchResourceCache.get(key);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.hl7.fhir.r4.model.Resource fetchResourceById(String type, String uri) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends org.hl7.fhir.r4.model.Resource> T fetchResourceWithException(Class<T> class_, String uri) throws FHIRException {
|
||||
T retVal = fetchResource(class_, uri);
|
||||
if (retVal == null) {
|
||||
throw new FHIRException("Can not find resource of type " + class_.getSimpleName() + " with uri " + uri);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<org.hl7.fhir.r4.model.ConceptMap> findMapsForSource(String url) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAbbreviation(String name) {
|
||||
return myWrap.getAbbreviation(name);
|
||||
}
|
||||
|
||||
public VersionConvertor_10_40 getConverter() {
|
||||
return myConverter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.hl7.fhir.r4.model.ExpansionProfile getExpansionProfile() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExpansionProfile(org.hl7.fhir.r4.model.ExpansionProfile expProfile) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public INarrativeGenerator getNarrativeGenerator(String prefix, String basePath) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IParser getParser(ParserType type) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IParser getParser(String type) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getResourceNames() {
|
||||
return myWrap.getResourceNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getResourceNamesAsSet() {
|
||||
return new HashSet<>(myWrap.getResourceNames());
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.hl7.fhir.r4.model.StructureMap getTransform(String url) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTypeNames() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVersion() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCache() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends org.hl7.fhir.r4.model.Resource> boolean hasResource(Class<T> class_, String uri) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNoTerminologyServer() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<org.hl7.fhir.r4.model.StructureMap> listTransforms() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IParser newJsonParser() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IResourceValidator newValidator() throws FHIRException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IParser newXmlParser() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String oid2Uri(String code) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLogger(ILoggingService logger) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsSystem(String system) throws TerminologyServiceException {
|
||||
return myWrap.supportsSystem(system);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TranslationServices translator() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> typeTails() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValidationResult validateCode(String system, String code, String display) {
|
||||
org.hl7.fhir.instance.utils.IWorkerContext.ValidationResult result = myWrap.validateCode(system, code, display);
|
||||
return convertValidationResult(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValidationResult validateCode(String system, String code, String display, org.hl7.fhir.r4.model.ValueSet vs) {
|
||||
ValueSet convertedVs = null;
|
||||
|
||||
try {
|
||||
if (vs != null) {
|
||||
convertedVs = new VersionConvertor_10_40(myAdvisor).convertValueSet(vs);
|
||||
}
|
||||
} catch (FHIRException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
|
||||
org.hl7.fhir.instance.utils.IWorkerContext.ValidationResult result = myWrap.validateCode(system, code, display, convertedVs);
|
||||
return convertValidationResult(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValidationResult validateCode(org.hl7.fhir.r4.model.Coding code, org.hl7.fhir.r4.model.ValueSet vs) {
|
||||
Coding convertedCode = null;
|
||||
ValueSet convertedVs = null;
|
||||
|
||||
try {
|
||||
if (code != null) {
|
||||
convertedCode = new VersionConvertor_10_40(myAdvisor).convertCoding(code);
|
||||
}
|
||||
if (vs != null) {
|
||||
convertedVs = new VersionConvertor_10_40(myAdvisor).convertValueSet(vs);
|
||||
}
|
||||
} catch (FHIRException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
|
||||
org.hl7.fhir.instance.utils.IWorkerContext.ValidationResult result = myWrap.validateCode(convertedCode, convertedVs);
|
||||
return convertValidationResult(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValidationResult validateCode(org.hl7.fhir.r4.model.CodeableConcept code, org.hl7.fhir.r4.model.ValueSet vs) {
|
||||
CodeableConcept convertedCode = null;
|
||||
ValueSet convertedVs = null;
|
||||
|
||||
try {
|
||||
if (code != null) {
|
||||
convertedCode = new VersionConvertor_10_40(myAdvisor).convertCodeableConcept(code);
|
||||
}
|
||||
if (vs != null) {
|
||||
convertedVs = new VersionConvertor_10_40(myAdvisor).convertValueSet(vs);
|
||||
}
|
||||
} catch (FHIRException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
|
||||
org.hl7.fhir.instance.utils.IWorkerContext.ValidationResult result = myWrap.validateCode(convertedCode, convertedVs);
|
||||
return convertValidationResult(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValidationResult validateCode(String system, String code, String display, org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent vsi) {
|
||||
ValueSet.ConceptSetComponent conceptSetComponent = null;
|
||||
if (vsi != null) {
|
||||
try {
|
||||
conceptSetComponent = new VersionConvertor_10_40(myAdvisor).convertConceptSetComponent(vsi);
|
||||
} catch (FHIRException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
}
|
||||
|
||||
org.hl7.fhir.instance.utils.IWorkerContext.ValidationResult result = myWrap.validateCode(system, code, display, conceptSetComponent);
|
||||
return convertValidationResult(result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class NullAdvisor implements VersionConvertorAdvisor40 {
|
||||
@Override
|
||||
public Resource convertR2(org.hl7.fhir.r4.model.Resource resource) throws FHIRException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.hl7.fhir.dstu3.model.Resource convertR3(org.hl7.fhir.r4.model.Resource resource) throws FHIRException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodeSystem getCodeSystem(org.hl7.fhir.r4.model.ValueSet src) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCodeSystem(CodeSystem tgtcs, org.hl7.fhir.r4.model.ValueSet source) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean ignoreEntry(org.hl7.fhir.r4.model.Bundle.BundleEntryComponent src) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ResourceKey {
|
||||
private final int myHashCode;
|
||||
private String myResourceName;
|
||||
private String myUri;
|
||||
|
||||
private ResourceKey(String theResourceName, String theUri) {
|
||||
myResourceName = theResourceName;
|
||||
myUri = theUri;
|
||||
myHashCode = new HashCodeBuilder(17, 37)
|
||||
.append(myResourceName)
|
||||
.append(myUri)
|
||||
.toHashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object theO) {
|
||||
if (this == theO) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (theO == null || getClass() != theO.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ResourceKey that = (ResourceKey) theO;
|
||||
|
||||
return new EqualsBuilder()
|
||||
.append(myResourceName, that.myResourceName)
|
||||
.append(myUri, that.myUri)
|
||||
.isEquals();
|
||||
}
|
||||
|
||||
public String getResourceName() {
|
||||
return myResourceName;
|
||||
}
|
||||
|
||||
public String getUri() {
|
||||
return myUri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return myHashCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander
|
|||
|
||||
@Override
|
||||
public List<StructureDefinition> allStructures() {
|
||||
throw new UnsupportedOperationException();
|
||||
return myValidationSupport.allStructures();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.hl7.fhir.instance.hapi.validation;
|
||||
|
||||
import org.hl7.fhir.instance.model.StructureDefinition;
|
||||
import org.hl7.fhir.instance.model.ValueSet;
|
||||
import org.hl7.fhir.instance.model.ValueSet.ConceptDefinitionComponent;
|
||||
import org.hl7.fhir.instance.model.ValueSet.ConceptSetComponent;
|
||||
|
@ -9,8 +10,15 @@ import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
|
|||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface IValidationSupport {
|
||||
|
||||
/**
|
||||
* Fetch all structuredefinitions
|
||||
*/
|
||||
List<StructureDefinition> allStructures();
|
||||
|
||||
/**
|
||||
* Expands the given portion of a ValueSet
|
||||
*
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
package org.hl7.fhir.instance.hapi.validation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import org.hl7.fhir.instance.model.StructureDefinition;
|
||||
import org.hl7.fhir.instance.model.ValueSet;
|
||||
import org.hl7.fhir.instance.model.ValueSet.ConceptSetComponent;
|
||||
import org.hl7.fhir.instance.model.ValueSet.ValueSetExpansionComponent;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ValidationSupportChain implements IValidationSupport {
|
||||
|
||||
|
@ -37,6 +37,15 @@ public class ValidationSupportChain implements IValidationSupport {
|
|||
myChain.add(theValidationSupport);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StructureDefinition> allStructures() {
|
||||
ArrayList<StructureDefinition> retVal = new ArrayList<>();
|
||||
for (IValidationSupport next : myChain) {
|
||||
retVal.addAll(next.allStructures());
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueSetExpansionComponent expandValueSet(FhirContext theCtx, ConceptSetComponent theInclude) {
|
||||
for (IValidationSupport next : myChain) {
|
||||
|
|
2
pom.xml
2
pom.xml
|
@ -2025,6 +2025,7 @@
|
|||
<module>hapi-fhir-validation-resources-dstu3</module>
|
||||
<module>hapi-fhir-structures-r4</module>
|
||||
<module>hapi-fhir-validation-resources-r4</module>
|
||||
<module>hapi-fhir-igpacks</module>
|
||||
<module>hapi-fhir-jaxrsserver-base</module>
|
||||
<module>hapi-fhir-jaxrsserver-example</module>
|
||||
<module>hapi-fhir-jpaserver-base</module>
|
||||
|
@ -2048,7 +2049,6 @@
|
|||
<module>example-projects/hapi-fhir-jpaserver-dynamic</module>
|
||||
<module>tests/hapi-fhir-base-test-mindeps-client</module>
|
||||
<module>tests/hapi-fhir-base-test-mindeps-server</module>
|
||||
<module>hapi-fhir-igpacks</module>
|
||||
<module>hapi-fhir-spring-boot</module>
|
||||
<!--<module>hapi-fhir-osgi-core</module>-->
|
||||
</modules>
|
||||
|
|
|
@ -45,6 +45,13 @@
|
|||
This work was sponsored by the Regenstrief Institute. Thanks
|
||||
to Regenstrief for their support!
|
||||
</action>
|
||||
<action type="add">
|
||||
The DSTU2 validator has been refactored to use the same codebase
|
||||
as the DSTU3/R4 validator (which were harmonized in HAPI FHIR 3.3.0).
|
||||
This means that we now have a single codebase for all validators, which
|
||||
improves maintainability and brings a number of improvements
|
||||
to the accuracy of DSTU2 resource validation.
|
||||
</action>
|
||||
<action type="fix">
|
||||
When encoding a resource that had contained resources with user-supplied
|
||||
local IDs (e.g. resource.setId("#1")) as well as contained resources
|
||||
|
|
Loading…
Reference in New Issue