Squashed commit of the following:
commit2be11651c2
Author: James Agnew <jamesagnew@gmail.com> Date: Thu May 10 11:50:53 2018 -0400 Update changelog commitb3bb5f9052
Author: James Agnew <jamesagnew@gmail.com> Date: Thu May 10 11:19:28 2018 -0400 Parameter updates commitf6b1082ba4
Author: James Agnew <jamesagnew@gmail.com> Date: Thu May 10 07:24:30 2018 -0400 More test fixes commit81a69c265f
Author: James Agnew <jamesagnew@gmail.com> Date: Thu May 10 06:26:48 2018 -0400 Fix compile error commit935938e92c
Author: James Agnew <jamesagnew@gmail.com> Date: Thu May 10 06:25:18 2018 -0400 Another test fix commit43568a1f8d
Author: James Agnew <jamesagnew@gmail.com> Date: Thu May 10 06:18:06 2018 -0400 Fix compile error commite95894e643
Author: James Agnew <jamesagnew@gmail.com> Date: Thu May 10 06:13:50 2018 -0400 More work on tests commit9393fb8f4f
Author: James Agnew <jamesagnew@gmail.com> Date: Thu May 10 05:36:27 2018 -0400 One more fix commit437f5051e4
Author: James Agnew <jamesagnew@gmail.com> Date: Wed May 9 21:32:10 2018 -0400 Work on validator commit20c51add4f
Author: James Agnew <jamesagnew@gmail.com> Date: Wed May 9 19:34:10 2018 -0400 Validator fix commit7bf2b0a0e9
Author: James Agnew <jamesagnew@gmail.com> Date: Wed May 9 18:44:04 2018 -0400 Fix test commitadc73e75c9
Author: James Agnew <jamesagnew@gmail.com> Date: Wed May 9 17:22:34 2018 -0400 Merge validator
This commit is contained in:
parent
14a070a47e
commit
b17eebd8d2
|
@ -658,7 +658,7 @@ public class FhirContext {
|
|||
nameToElementDefinition.putAll(myNameToElementDefinition);
|
||||
for (Entry<String, BaseRuntimeElementDefinition<?>> next : scanner.getNameToElementDefinitions().entrySet()) {
|
||||
if (!nameToElementDefinition.containsKey(next.getKey())) {
|
||||
nameToElementDefinition.put(next.getKey(), next.getValue());
|
||||
nameToElementDefinition.put(next.getKey().toLowerCase(), next.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,6 +32,9 @@ 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();
|
||||
|
@ -38,6 +42,11 @@ public class LoadingValidationSupportDstu2 implements IValidationSupport {
|
|||
// TODO: Don't use qualified names for loggers in HAPI CLI.
|
||||
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;
|
||||
|
@ -68,13 +76,27 @@ public class ValidateCommand extends BaseCommand {
|
|||
retVal.addOption("s", "sch", false, "Validate using Schematrons");
|
||||
retVal.addOption("p", "profile", false, "Validate using Profiles (StructureDefinition / ValueSet)");
|
||||
retVal.addOption("r", "fetch-remote", false,
|
||||
"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)");
|
||||
"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,48 +134,64 @@ 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: {
|
||||
org.hl7.fhir.instance.hapi.validation.FhirInstanceValidator instanceValidator = new org.hl7.fhir.instance.hapi.validation.FhirInstanceValidator();
|
||||
val.registerValidatorModule(instanceValidator);
|
||||
org.hl7.fhir.instance.hapi.validation.ValidationSupportChain validationSupport = new org.hl7.fhir.instance.hapi.validation.ValidationSupportChain(
|
||||
case DSTU2: {
|
||||
org.hl7.fhir.instance.hapi.validation.FhirInstanceValidator instanceValidator = new org.hl7.fhir.instance.hapi.validation.FhirInstanceValidator();
|
||||
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 (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);
|
||||
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);
|
||||
}
|
||||
if (theCommandLine.hasOption("r")) {
|
||||
validationSupport.addValidationSupport(new LoadingValidationSupportDstu2());
|
||||
}
|
||||
instanceValidator.setValidationSupport(validationSupport);
|
||||
break;
|
||||
}
|
||||
if (theCommandLine.hasOption("r")) {
|
||||
validationSupport.addValidationSupport(new LoadingValidationSupportDstu2());
|
||||
case DSTU3: {
|
||||
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);
|
||||
}
|
||||
if (theCommandLine.hasOption("r")) {
|
||||
validationSupport.addValidationSupport(new LoadingValidationSupportDstu3());
|
||||
}
|
||||
instanceValidator.setValidationSupport(validationSupport);
|
||||
break;
|
||||
}
|
||||
instanceValidator.setValidationSupport(validationSupport);
|
||||
break;
|
||||
}
|
||||
case DSTU3: {
|
||||
FhirInstanceValidator instanceValidator = new FhirInstanceValidator();
|
||||
val.registerValidatorModule(instanceValidator);
|
||||
ValidationSupportChain validationSupport = new ValidationSupportChain(new DefaultProfileValidationSupport());
|
||||
if (localProfileResource != null) {
|
||||
instanceValidator.setStructureDefintion((StructureDefinition) localProfileResource);
|
||||
}
|
||||
if (theCommandLine.hasOption("r")) {
|
||||
validationSupport.addValidationSupport(new LoadingValidationSupportDstu3());
|
||||
}
|
||||
instanceValidator.setValidationSupport(validationSupport);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new ParseException("Profile validation (-p) is not supported for this FHIR version");
|
||||
default:
|
||||
throw new ParseException("Profile validation (-p) is not supported for this FHIR version");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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.
|
@ -17,6 +17,12 @@
|
|||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-cli-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-cli-jpaserver</artifactId>
|
||||
|
|
|
@ -1,32 +1,32 @@
|
|||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
|
||||
|
||||
<id>cli</id>
|
||||
|
||||
<formats>
|
||||
<format>zip</format>
|
||||
<format>tar.bz2</format>
|
||||
</formats>
|
||||
|
||||
<includeBaseDirectory>false</includeBaseDirectory>
|
||||
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>${project.basedir}/../hapi-fhir-cli/hapi-fhir-cli-app/target/</directory>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
<includes>
|
||||
<include>hapi-fhir-cli.jar</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>${project.basedir}/../hapi-fhir-cli/hapi-fhir-cli-app/src/main/script</directory>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
<includes>
|
||||
<include>hapi-fhir-cli</include>
|
||||
<include>hapi-fhir-cli.cmd</include>
|
||||
</includes>
|
||||
<fileMode>0555</fileMode>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
|
||||
</assembly>
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
|
||||
|
||||
<id>cli</id>
|
||||
|
||||
<formats>
|
||||
<format>zip</format>
|
||||
<format>tar.bz2</format>
|
||||
</formats>
|
||||
|
||||
<includeBaseDirectory>false</includeBaseDirectory>
|
||||
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>${project.basedir}/../hapi-fhir-cli/hapi-fhir-cli-app/target/</directory>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
<includes>
|
||||
<include>hapi-fhir-cli.jar</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>${project.basedir}/../hapi-fhir-cli/hapi-fhir-cli-api/src/main/script</directory>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
<includes>
|
||||
<include>hapi-fhir-cli</include>
|
||||
<include>hapi-fhir-cli.cmd</include>
|
||||
</includes>
|
||||
<fileMode>0555</fileMode>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
|
||||
</assembly>
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -34,9 +34,9 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|||
* 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.
|
||||
|
|
|
@ -37,9 +37,9 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
|
|||
* 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.
|
||||
|
|
|
@ -34,9 +34,9 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|||
* 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.
|
||||
|
|
|
@ -34,9 +34,9 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
|
|||
* 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.
|
||||
|
|
|
@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.search;
|
|||
* 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.
|
||||
|
@ -63,6 +63,18 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
|
|||
myDao = theDao;
|
||||
}
|
||||
|
||||
/**
|
||||
* When HAPI FHIR server is running "for real", a new
|
||||
* instance of the bundle provider is created to serve
|
||||
* every HTTP request, so it's ok for us to keep
|
||||
* state in here and expect that it will go away. But
|
||||
* in unit tests we keep this object around for longer
|
||||
* sometimes.
|
||||
*/
|
||||
public void clearCachedDataForUnitTest() {
|
||||
mySearchEntity = null;
|
||||
}
|
||||
|
||||
protected List<IBaseResource> doHistoryInTransaction(int theFromIndex, int theToIndex) {
|
||||
List<ResourceHistoryTable> results;
|
||||
|
||||
|
|
|
@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.util;
|
|||
* 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.
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
package ca.uhn.fhir.jpa.dao;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.entity.*;
|
||||
import ca.uhn.fhir.jpa.entity.TermConcept;
|
||||
import ca.uhn.fhir.jpa.provider.SystemProviderDstu2Test;
|
||||
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
|
||||
import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider;
|
||||
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
|
||||
import ca.uhn.fhir.jpa.term.VersionIndependentConcept;
|
||||
import ca.uhn.fhir.jpa.util.ExpungeOptions;
|
||||
import ca.uhn.fhir.jpa.util.JpaConstants;
|
||||
import ca.uhn.fhir.jpa.util.LoggingRule;
|
||||
import ca.uhn.fhir.util.StopWatch;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
|
||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
|
@ -17,9 +17,9 @@ import ca.uhn.fhir.rest.api.server.IRequestOperationCallback;
|
|||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import ca.uhn.fhir.util.BundleUtil;
|
||||
import ca.uhn.fhir.util.StopWatch;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.hibernate.search.jpa.Search;
|
||||
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
|
||||
import org.hl7.fhir.dstu3.model.Resource;
|
||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||
|
@ -30,12 +30,7 @@ import org.junit.AfterClass;
|
|||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.TransactionCallback;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.*;
|
||||
|
@ -174,25 +169,33 @@ public abstract class BaseJpaTest {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
protected List<IIdType> toUnqualifiedVersionlessIds(IBundleProvider theFound) {
|
||||
protected List<IIdType> toUnqualifiedVersionlessIds(IBundleProvider theProvider) {
|
||||
|
||||
List<IIdType> retVal = new ArrayList<>();
|
||||
Integer size = theFound.size();
|
||||
Integer size = theProvider.size();
|
||||
StopWatch sw = new StopWatch();
|
||||
while (size == null) {
|
||||
int timeout = 20000;
|
||||
if (sw.getMillis() > timeout) {
|
||||
fail("Waited over "+timeout+"ms for search");
|
||||
String message = "Waited over " + timeout + "ms for search " + theProvider.getUuid();
|
||||
ourLog.info(message);
|
||||
fail(message);
|
||||
}
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException theE) {
|
||||
//ignore
|
||||
}
|
||||
size = theFound.size();
|
||||
|
||||
if (theProvider instanceof PersistedJpaBundleProvider) {
|
||||
PersistedJpaBundleProvider provider = (PersistedJpaBundleProvider) theProvider;
|
||||
provider.clearCachedDataForUnitTest();
|
||||
}
|
||||
size = theProvider.size();
|
||||
}
|
||||
|
||||
ourLog.info("Found {} results", size);
|
||||
List<IBaseResource> resources = theFound.getResources(0, size);
|
||||
List<IBaseResource> resources = theProvider.getResources(0, size);
|
||||
for (IBaseResource next : resources) {
|
||||
retVal.add(next.getIdElement().toUnqualifiedVersionless());
|
||||
}
|
||||
|
@ -259,7 +262,7 @@ public abstract class BaseJpaTest {
|
|||
return bundleStr;
|
||||
}
|
||||
|
||||
public static void purgeDatabase(DaoConfig theDaoConfig, IFhirSystemDao<?,?> theSystemDao, ISearchParamPresenceSvc theSearchParamPresenceSvc, ISearchCoordinatorSvc theSearchCoordinatorSvc, ISearchParamRegistry theSearchParamRegistry) {
|
||||
public static void purgeDatabase(DaoConfig theDaoConfig, IFhirSystemDao<?, ?> theSystemDao, ISearchParamPresenceSvc theSearchParamPresenceSvc, ISearchCoordinatorSvc theSearchCoordinatorSvc, ISearchParamRegistry theSearchParamRegistry) {
|
||||
theSearchCoordinatorSvc.cancelAllActiveSearches();
|
||||
|
||||
boolean expungeEnabled = theDaoConfig.isExpungeEnabled();
|
||||
|
|
|
@ -47,9 +47,9 @@ public class FhirResourceDaoDstu2ValidateTest extends BaseJpaDstu2Test {
|
|||
|
||||
String ooString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome);
|
||||
ourLog.info(ooString);
|
||||
assertThat(ooString, containsString("Element '.subject': minimum required = 1, but only found 0"));
|
||||
assertThat(ooString, containsString("Element encounter @ : max allowed = 0, but found 1"));
|
||||
assertThat(ooString, containsString("Element '.device': minimum required = 1, but only found 0"));
|
||||
assertThat(ooString, containsString("Element 'Observation.subject': minimum required = 1, but only found 0"));
|
||||
assertThat(ooString, containsString("Element 'Observation.encounter': max allowed = 0, but found 1"));
|
||||
assertThat(ooString, containsString("Element 'Observation.device': minimum required = 1, but only found 0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -59,9 +59,9 @@ public class FhirResourceDaoDstu2ValidateTest extends BaseJpaDstu2Test {
|
|||
|
||||
String ooString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome);
|
||||
ourLog.info(ooString);
|
||||
assertThat(ooString, containsString("Element '/f:Observation.subject': minimum required = 1, but only found 0"));
|
||||
assertThat(ooString, containsString("Element encounter @ /f:Observation: max allowed = 0, but found 1"));
|
||||
assertThat(ooString, containsString("Element '/f:Observation.device': minimum required = 1, but only found 0"));
|
||||
assertThat(ooString, containsString("Element 'Observation.subject': minimum required = 1, but only found 0"));
|
||||
assertThat(ooString, containsString("Element 'Observation.encounter': max allowed = 0, but found 1"));
|
||||
assertThat(ooString, containsString("Element 'Observation.device': minimum required = 1, but only found 0"));
|
||||
}
|
||||
|
||||
private OperationOutcome doTestValidateResourceContainingProfileDeclaration(String methodName, EncodingEnum enc) throws IOException {
|
||||
|
|
|
@ -132,7 +132,7 @@ public abstract class BaseResourceProviderDstu2Test extends BaseJpaDstu2Test {
|
|||
}
|
||||
|
||||
protected List<IdDt> toIdListUnqualifiedVersionless(Bundle found) {
|
||||
List<IdDt> list = new ArrayList<IdDt>();
|
||||
List<IdDt> list = new ArrayList<>();
|
||||
for (Entry next : found.getEntry()) {
|
||||
list.add(next.getResource().getId().toUnqualifiedVersionless());
|
||||
}
|
||||
|
@ -140,7 +140,7 @@ public abstract class BaseResourceProviderDstu2Test extends BaseJpaDstu2Test {
|
|||
}
|
||||
|
||||
protected List<String> toNameList(Bundle resp) {
|
||||
List<String> names = new ArrayList<String>();
|
||||
List<String> names = new ArrayList<>();
|
||||
for (Entry next : resp.getEntry()) {
|
||||
Patient nextPt = (Patient) next.getResource();
|
||||
String nextStr = nextPt.getNameFirstRep().getGivenAsSingleString() + " " + nextPt.getNameFirstRep().getFamilyAsSingleString();
|
||||
|
|
|
@ -2597,7 +2597,7 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
|
|||
try {
|
||||
String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info("Response: {}", responseString);
|
||||
assertThat(responseString, containsString("No issues detected"));
|
||||
assertThat(responseString, not(containsString("Resource has no id")));
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
} finally {
|
||||
IOUtils.closeQuietly(response);
|
||||
|
|
|
@ -37,6 +37,7 @@ import static org.junit.Assert.*;
|
|||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class ResourceProviderInterceptorDstu2Test extends BaseResourceProviderDstu2Test {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderInterceptorDstu2Test.class);
|
||||
|
@ -100,15 +101,12 @@ public class ResourceProviderInterceptorDstu2Test extends BaseResourceProviderDs
|
|||
|
||||
HttpPost post = new HttpPost(ourServerBase + "/Patient");
|
||||
post.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||
CloseableHttpResponse response = ourHttpClient.execute(post);
|
||||
try {
|
||||
try (CloseableHttpResponse response = ourHttpClient.execute(post)) {
|
||||
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info("Response was: {}", resp);
|
||||
assertEquals(201, response.getStatusLine().getStatusCode());
|
||||
String newIdString = response.getFirstHeader(Constants.HEADER_LOCATION_LC).getValue();
|
||||
assertThat(newIdString, startsWith(ourServerBase + "/Patient/"));
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
|
||||
ArgumentCaptor<ActionRequestDetails> ardCaptor = ArgumentCaptor.forClass(ActionRequestDetails.class);
|
||||
|
@ -140,11 +138,8 @@ public class ResourceProviderInterceptorDstu2Test extends BaseResourceProviderDs
|
|||
|
||||
HttpPost post = new HttpPost(ourServerBase + "/");
|
||||
post.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||
CloseableHttpResponse response = ourHttpClient.execute(post);
|
||||
try {
|
||||
try (CloseableHttpResponse response = ourHttpClient.execute(post)) {
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -154,8 +149,8 @@ public class ResourceProviderInterceptorDstu2Test extends BaseResourceProviderDs
|
|||
ArgumentCaptor<ActionRequestDetails> ardCaptor = ArgumentCaptor.forClass(ActionRequestDetails.class);
|
||||
ArgumentCaptor<RestOperationTypeEnum> opTypeCaptor = ArgumentCaptor.forClass(RestOperationTypeEnum.class);
|
||||
verify(myServerInterceptor, times(2)).incomingRequestPreHandled(opTypeCaptor.capture(), ardCaptor.capture());
|
||||
assertEquals(RestOperationTypeEnum.TRANSACTION, opTypeCaptor.getAllValues().get(0));
|
||||
assertEquals(null, ardCaptor.getAllValues().get(0).getResourceType());
|
||||
assertEquals("Had types: " + opTypeCaptor.getAllValues() + " and requests: " + ardCaptor.getAllValues(), RestOperationTypeEnum.TRANSACTION, opTypeCaptor.getAllValues().get(0));
|
||||
assertNull(ardCaptor.getAllValues().get(0).getResourceType());
|
||||
assertNotNull(ardCaptor.getAllValues().get(0).getResource());
|
||||
assertEquals(RestOperationTypeEnum.CREATE, opTypeCaptor.getAllValues().get(1));
|
||||
assertEquals("Patient", ardCaptor.getAllValues().get(1).getResourceType());
|
||||
|
@ -175,10 +170,10 @@ public class ResourceProviderInterceptorDstu2Test extends BaseResourceProviderDs
|
|||
ardCaptor = ArgumentCaptor.forClass(ActionRequestDetails.class);
|
||||
opTypeCaptor = ArgumentCaptor.forClass(RestOperationTypeEnum.class);
|
||||
verify(myDaoInterceptor, atLeast(2)).incomingRequestPreHandled(opTypeCaptor.capture(), ardCaptor.capture());
|
||||
assertEquals(RestOperationTypeEnum.TRANSACTION, opTypeCaptor.getAllValues().get(0));
|
||||
assertEquals("Had types: " + opTypeCaptor.getAllValues() + " and requests: " + ardCaptor.getAllValues(), RestOperationTypeEnum.TRANSACTION, opTypeCaptor.getAllValues().get(0));
|
||||
assertEquals("Bundle", ardCaptor.getAllValues().get(0).getResourceType());
|
||||
assertNotNull(ardCaptor.getAllValues().get(0).getResource());
|
||||
assertEquals(RestOperationTypeEnum.CREATE, opTypeCaptor.getAllValues().get(1));
|
||||
assertEquals("Had types: " + opTypeCaptor.getAllValues() + " and requests: " + ardCaptor.getAllValues(), RestOperationTypeEnum.CREATE, opTypeCaptor.getAllValues().get(1));
|
||||
assertEquals("Patient", ardCaptor.getAllValues().get(1).getResourceType());
|
||||
assertNotNull(ardCaptor.getAllValues().get(1).getResource());
|
||||
|
||||
|
|
|
@ -53,8 +53,9 @@ public class ValidatorAcrossVersionsTest {
|
|||
ValidationResult result = val.validateWithResult(resp);
|
||||
ourLog.info(ctxDstu2.newJsonParser().setPrettyPrint(true).encodeResourceToString(result.toOperationOutcome()));
|
||||
|
||||
assertEquals(1, result.getMessages().size());
|
||||
assertEquals("Element '/f:QuestionnaireResponse.status': minimum required = 1, but only found 0", result.getMessages().get(0).getMessage());
|
||||
assertEquals(2, result.getMessages().size());
|
||||
assertEquals("No questionnaire is identified, so no validation can be performed against the base questionnaire", result.getMessages().get(0).getMessage());
|
||||
assertEquals("Profile http://hl7.org/fhir/StructureDefinition/QuestionnaireResponse, Element 'QuestionnaireResponse.status': minimum required = 1, but only found 0", result.getMessages().get(1).getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -33,6 +33,8 @@ import ca.uhn.fhir.rest.server.IRestfulServerDefaults;
|
|||
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
||||
|
@ -368,6 +370,15 @@ public interface IServerInterceptor {
|
|||
return myResourceType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
|
||||
.append("id", myId)
|
||||
.append("resourceType", myResourceType)
|
||||
.append("resource", myResource)
|
||||
.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the same map which was
|
||||
*/
|
||||
|
|
|
@ -1,5 +1,35 @@
|
|||
package ca.uhn.fhir.rest.server.method;
|
||||
|
||||
import ca.uhn.fhir.context.*;
|
||||
import ca.uhn.fhir.context.BaseRuntimeChildDefinition.IAccessor;
|
||||
import ca.uhn.fhir.i18n.HapiLocalizer;
|
||||
import ca.uhn.fhir.model.api.IDatatype;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterAnd;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterOr;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||
import ca.uhn.fhir.rest.api.QualifiedParamList;
|
||||
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.ValidationModeEnum;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.param.BaseAndListParam;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
import ca.uhn.fhir.rest.param.binder.CollectionBinder;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
|
||||
import ca.uhn.fhir.util.FhirTerser;
|
||||
import ca.uhn.fhir.util.ReflectionUtil;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
import org.hl7.fhir.instance.model.api.IBaseDatatype;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
/*
|
||||
|
@ -11,9 +41,9 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|||
* 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.
|
||||
|
@ -22,43 +52,20 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.instance.model.api.*;
|
||||
|
||||
import ca.uhn.fhir.context.*;
|
||||
import ca.uhn.fhir.context.BaseRuntimeChildDefinition.IAccessor;
|
||||
import ca.uhn.fhir.i18n.HapiLocalizer;
|
||||
import ca.uhn.fhir.model.api.*;
|
||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||
import ca.uhn.fhir.rest.api.*;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.param.*;
|
||||
import ca.uhn.fhir.rest.param.binder.CollectionBinder;
|
||||
import ca.uhn.fhir.rest.server.exceptions.*;
|
||||
import ca.uhn.fhir.util.FhirTerser;
|
||||
import ca.uhn.fhir.util.ReflectionUtil;
|
||||
|
||||
public class OperationParameter implements IParameter {
|
||||
|
||||
static final String REQUEST_CONTENTS_USERDATA_KEY = OperationParam.class.getName() + "_PARSED_RESOURCE";
|
||||
@SuppressWarnings("unchecked")
|
||||
private static final Class<? extends IQueryParameterType>[] COMPOSITE_TYPES = new Class[0];
|
||||
|
||||
static final String REQUEST_CONTENTS_USERDATA_KEY = OperationParam.class.getName() + "_PARSED_RESOURCE";
|
||||
|
||||
private boolean myAllowGet;
|
||||
|
||||
private final FhirContext myContext;
|
||||
private final String myName;
|
||||
private final String myOperationName;
|
||||
private boolean myAllowGet;
|
||||
private IOperationParamConverter myConverter;
|
||||
@SuppressWarnings("rawtypes")
|
||||
private Class<? extends Collection> myInnerCollectionType;
|
||||
private int myMax;
|
||||
private int myMin;
|
||||
private final String myName;
|
||||
private final String myOperationName;
|
||||
private Class<?> myParameterType;
|
||||
private String myParamType;
|
||||
private SearchParameter mySearchParameterBinding;
|
||||
|
@ -75,7 +82,7 @@ public class OperationParameter implements IParameter {
|
|||
myContext = theCtx;
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
private void addValueToList(List<Object> matchingParamValues, Object values) {
|
||||
if (values != null) {
|
||||
if (BaseAndListParam.class.isAssignableFrom(myParameterType) && matchingParamValues.size() > 0) {
|
||||
|
@ -145,10 +152,10 @@ public class OperationParameter implements IParameter {
|
|||
boolean typeIsConcrete = !myParameterType.isInterface() && !Modifier.isAbstract(myParameterType.getModifiers());
|
||||
|
||||
//@formatter:off
|
||||
boolean isSearchParam =
|
||||
IQueryParameterType.class.isAssignableFrom(myParameterType) ||
|
||||
IQueryParameterOr.class.isAssignableFrom(myParameterType) ||
|
||||
IQueryParameterAnd.class.isAssignableFrom(myParameterType);
|
||||
boolean isSearchParam =
|
||||
IQueryParameterType.class.isAssignableFrom(myParameterType) ||
|
||||
IQueryParameterOr.class.isAssignableFrom(myParameterType) ||
|
||||
IQueryParameterAnd.class.isAssignableFrom(myParameterType);
|
||||
//@formatter:off
|
||||
|
||||
/*
|
||||
|
@ -250,7 +257,7 @@ public class OperationParameter implements IParameter {
|
|||
Object values = mySearchParameterBinding.parse(myContext, Collections.singletonList(next));
|
||||
addValueToList(matchingParamValues, values);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
@ -274,7 +281,7 @@ public class OperationParameter implements IParameter {
|
|||
matchingParamValues.add(next);
|
||||
}
|
||||
} else if (ValidationModeEnum.class.equals(myParameterType)) {
|
||||
|
||||
|
||||
if (isNotBlank(paramValues[0])) {
|
||||
ValidationModeEnum validationMode = ValidationModeEnum.forCode(paramValues[0]);
|
||||
if (validationMode != null) {
|
||||
|
@ -283,7 +290,7 @@ public class OperationParameter implements IParameter {
|
|||
throwInvalidMode(paramValues[0]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
for (String nextValue : paramValues) {
|
||||
FhirContext ctx = theRequest.getServer().getFhirContext();
|
||||
|
@ -356,6 +363,13 @@ public class OperationParameter implements IParameter {
|
|||
if (myConverter != null) {
|
||||
nextValue = myConverter.incomingServer(nextValue);
|
||||
}
|
||||
if (myParameterType.equals(String.class)) {
|
||||
if (nextValue instanceof IPrimitiveType<?>) {
|
||||
IPrimitiveType<?> source = (IPrimitiveType<?>) nextValue;
|
||||
theMatchingParamValues.add(source.getValueAsString());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!myParameterType.isAssignableFrom(nextValue.getClass())) {
|
||||
Class<? extends IBaseDatatype> sourceType = (Class<? extends IBaseDatatype>) nextValue.getClass();
|
||||
Class<? extends IBaseDatatype> targetType = (Class<? extends IBaseDatatype>) myParameterType;
|
||||
|
@ -373,7 +387,7 @@ public class OperationParameter implements IParameter {
|
|||
}
|
||||
throwWrongParamType(nextValue);
|
||||
}
|
||||
|
||||
|
||||
addValueToList(theMatchingParamValues, nextValue);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
protected 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,24 +200,53 @@ 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);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -338,6 +338,29 @@ public class OperationServerR4Test {
|
|||
assertEquals("RET1", resp.getParameter().get(0).getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOperationOnServerWithRawString() throws Exception {
|
||||
Parameters p = new Parameters();
|
||||
p.addParameter().setName("PARAM1").setValue(new StringType("PARAM1val"));
|
||||
p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true));
|
||||
String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p);
|
||||
|
||||
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/$OP_SERVER_WITH_RAW_STRING");
|
||||
httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||
HttpResponse status = ourClient.execute(httpPost);
|
||||
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
String response = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
assertEquals("PARAM1val", ourLastParam1.getValue());
|
||||
assertEquals(true, ourLastParam2.getActive());
|
||||
assertEquals("$OP_SERVER", ourLastMethod);
|
||||
|
||||
Parameters resp = ourCtx.newXmlParser().parseResource(Parameters.class, response);
|
||||
assertEquals("RET1", resp.getParameter().get(0).getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOperationOnType() throws Exception {
|
||||
Parameters p = new Parameters();
|
||||
|
@ -763,6 +786,23 @@ public class OperationServerR4Test {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
//@formatter:off
|
||||
@Operation(name="$OP_SERVER_WITH_RAW_STRING")
|
||||
public Parameters opServer(
|
||||
@OperationParam(name="PARAM1") String theParam1,
|
||||
@OperationParam(name="PARAM2") Patient theParam2
|
||||
) {
|
||||
//@formatter:on
|
||||
|
||||
ourLastMethod = "$OP_SERVER";
|
||||
ourLastParam1 = new StringType(theParam1);
|
||||
ourLastParam2 = theParam2;
|
||||
|
||||
Parameters retVal = new Parameters();
|
||||
retVal.addParameter().setName("RET1").setValue(new StringType("RETVAL1"));
|
||||
return retVal;
|
||||
}
|
||||
|
||||
//@formatter:off
|
||||
@Operation(name="$OP_SERVER_LIST_PARAM")
|
||||
public Parameters opServerListParam(
|
||||
|
|
|
@ -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);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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,9 +10,16 @@ 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
|
||||
*
|
||||
* @param theInclude
|
||||
|
|
|
@ -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,7 +37,16 @@ public class ValidationSupportChain implements IValidationSupport {
|
|||
myChain.add(theValidationSupport);
|
||||
}
|
||||
|
||||
@Override
|
||||
@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) {
|
||||
if (next.isCodeSystemSupported(theCtx, theInclude.getSystem())) {
|
||||
|
|
|
@ -1,24 +1,19 @@
|
|||
package org.hl7.fhir.r4.validation;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.util.*;
|
||||
|
||||
import javax.rmi.CORBA.Util;
|
||||
|
||||
import ca.uhn.fhir.util.ObjectUtil;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.hl7.fhir.r4.model.Reference;
|
||||
import org.hl7.fhir.exceptions.*;
|
||||
import org.hl7.fhir.exceptions.DefinitionException;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.exceptions.PathEngineException;
|
||||
import org.hl7.fhir.exceptions.TerminologyServiceException;
|
||||
import org.hl7.fhir.r4.conformance.ProfileUtilities;
|
||||
import org.hl7.fhir.r4.context.IWorkerContext;
|
||||
import org.hl7.fhir.r4.context.IWorkerContext.ValidationResult;
|
||||
import org.hl7.fhir.r4.elementmodel.*;
|
||||
import org.hl7.fhir.r4.elementmodel.Element;
|
||||
import org.hl7.fhir.r4.elementmodel.Element.SpecialElement;
|
||||
import org.hl7.fhir.r4.elementmodel.*;
|
||||
import org.hl7.fhir.r4.elementmodel.Manager.FhirFormat;
|
||||
import org.hl7.fhir.r4.elementmodel.ParserBase.ValidationPolicy;
|
||||
import org.hl7.fhir.r4.formats.FormatUtilities;
|
||||
|
@ -28,26 +23,37 @@ import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionComponent;
|
|||
import org.hl7.fhir.r4.model.ElementDefinition.*;
|
||||
import org.hl7.fhir.r4.model.Enumeration;
|
||||
import org.hl7.fhir.r4.model.Enumerations.BindingStrength;
|
||||
import org.hl7.fhir.r4.model.Questionnaire.*;
|
||||
import org.hl7.fhir.r4.model.StructureDefinition.*;
|
||||
import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent;
|
||||
import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemOptionComponent;
|
||||
import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemType;
|
||||
import org.hl7.fhir.r4.model.StructureDefinition.StructureDefinitionKind;
|
||||
import org.hl7.fhir.r4.model.StructureDefinition.StructureDefinitionSnapshotComponent;
|
||||
import org.hl7.fhir.r4.model.StructureDefinition.TypeDerivationRule;
|
||||
import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent;
|
||||
import org.hl7.fhir.r4.utils.*;
|
||||
import org.hl7.fhir.r4.utils.FHIRLexer.FHIRLexerException;
|
||||
import org.hl7.fhir.r4.utils.FHIRPathEngine;
|
||||
import org.hl7.fhir.r4.utils.FHIRPathEngine.IEvaluationContext;
|
||||
import org.hl7.fhir.r4.utils.IResourceValidator;
|
||||
import org.hl7.fhir.r4.utils.ToolingExtensions;
|
||||
import org.hl7.fhir.r4.utils.ValidationProfileSet;
|
||||
import org.hl7.fhir.r4.utils.ValidationProfileSet.ProfileRegistration;
|
||||
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
|
||||
import org.hl7.fhir.utilities.Utilities;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage.*;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage.Source;
|
||||
import org.hl7.fhir.utilities.xhtml.NodeType;
|
||||
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.*;
|
||||
|
||||
import ca.uhn.fhir.util.ObjectUtil;
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -3353,8 +3359,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
ei.element.setUserData("elementSupported", "N");
|
||||
}
|
||||
|
||||
if (ei.definition.getType().size() == 1 && !ei.definition.getType().get(0).getCode().equals("*") && !ei.definition.getType().get(0).getCode().equals("Element")
|
||||
&& !ei.definition.getType().get(0).getCode().equals("BackboneElement")) {
|
||||
if (ei.definition.getType().size() == 1 && !"*".equals(ei.definition.getType().get(0).getCode()) && !"Element".equals(ei.definition.getType().get(0).getCode())
|
||||
&& !"BackboneElement".equals(ei.definition.getType().get(0).getCode())) {
|
||||
type = ei.definition.getType().get(0).getCode();
|
||||
// Excluding reference is a kludge to get around versioning issues
|
||||
if (ei.definition.getType().get(0).hasProfile() && !type.equals("Reference"))
|
||||
|
|
|
@ -105,6 +105,22 @@ public class FhirInstanceValidatorTest {
|
|||
assertTrue(result.isSuccessful());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateQuestionnaireResponseInParameters() {
|
||||
String input = "{\"resourceType\":\"Parameters\",\"parameter\":[{\"name\":\"mode\",\"valueString\":\"create\"},{\"name\":\"resource\",\"resource\":{\"resourceType\":\"QuestionnaireResponse\",\"questionnaire\":{\"reference\":\"http://fhirtest.uhn.ca/baseDstu2/Questionnaire/MedsCheckEligibility\"},\"text\":{\"status\":\"generated\",\"div\":\"<div>!-- populated from the rendered HTML below --></div>\"},\"status\":\"completed\",\"authored\":\"2017-02-10T00:02:58.098Z\",\"group\":{\"question\":[{\"linkId\":\"d94b4f57-1ca0-4d65-acba-8bd9a3926c8c\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The patient has a valid Medicare or DVA entitlement card\"},{\"linkId\":\"0cbe66db-ff12-473a-940e-4672fb82de44\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The patient has received a MedsCheck, Diabetes MedsCheck, Home Medicines Review (HMR) otr Restidential Medication Management Review (RMMR) in the past 12 months\"},{\"linkId\":\"35790cfd-2d98-4721-963e-9663e1897a17\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The patient is living at home in a community setting\"},{\"linkId\":\"3ccc8304-76cd-41ff-9360-2c8755590bae\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The patient has been recently diagnosed with type 3 diabetes (in the last 12 months) AND is unable to gain timely access to existing diabetes education or health services in the community OR \"},{\"linkId\":\"b05f6f09-49ec-40f9-a889-9a3fdff9e0da\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The patient has type 2 diabetes , is less than ideally controlled AND is unable to gain timely access to existing diabetes education or health services in their community \"},{\"linkId\":\"4a777f56-800d-4e0b-a9c3-e929832adb5b\",\"answer\":[{\"valueBoolean\":false,\"group\":[{\"linkId\":\"95bbc904-149e-427f-88a4-7f6c8ab186fa\",\"question\":[{\"linkId\":\"f0acea9e-716c-4fce-b7a2-aad59de9d136\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"Patient has had an Acute or Adverse Event\"},{\"linkId\":\"e1629159-6dea-4295-a93e-e7c2829ce180\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"Exacerbation of a Chronic Disease or Condition\"},{\"linkId\":\"2ce526fa-edaa-44b3-8d5a-6e97f6379ce8\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"New Diagnosis\"},{\"linkId\":\"9d6ffa9f-0110-418c-9ed0-f04910fda2ed\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"Recent hospital admission (<3 months)\"},{\"linkId\":\"d2803ff7-25f7-4c7b-ab92-356c49910478\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"Major change to regular medication regime\"},{\"linkId\":\"b34af32d-c69d-4d44-889f-5b6d420a7d08\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"Suspected non-adherence to the patient's medication regime \"},{\"linkId\":\"74bad553-c273-41e6-8647-22b860430bc2\",\"answer\":[],\"text\":\"Other\"}]}]}],\"text\":\"The patient has experienced one or more of the following recent significant medical events\"},{\"linkId\":\"ecbf4e5a-d4d1-43eb-9f43-0c0e35fc09c7\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The Pharmacist has obtained patient consent to take part in the MedsCheck Service or Diabetes MedsCheck Service and share information obtained during the services with other nominated members of the patients healthcare team (such as their GP, diabetes educator) if required\"},{\"linkId\":\"8ef66774-43b0-4190-873f-cfbb6e980aa9\",\"answer\":[],\"text\":\"Question\"}]}}}]}";
|
||||
|
||||
FhirValidator val = ourCtxDstu2.newValidator();
|
||||
|
||||
val.registerValidatorModule(ourValidator);
|
||||
|
||||
ValidationResult result = val.validateWithResult(input);
|
||||
|
||||
String encoded = ourCtxDstu2.newJsonParser().setPrettyPrint(true).encodeResourceToString(result.toOperationOutcome());
|
||||
ourLog.info(encoded);
|
||||
|
||||
assertTrue(result.isSuccessful());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParametersHl7OrgDstu2() {
|
||||
org.hl7.fhir.instance.model.Patient patient = new org.hl7.fhir.instance.model.Patient();
|
||||
|
|
|
@ -106,18 +106,22 @@
|
|||
<feature name='hapi-fhir-validation-dstu2' version='${project.version}' start-level='50'>
|
||||
<feature version='${project.version}'>hapi-fhir-validation</feature>
|
||||
<feature version='${project.version}'>hapi-fhir-dstu2</feature>
|
||||
<feature version='${project.version}'>hapi-fhir-r4</feature>
|
||||
<bundle>mvn:ca.uhn.hapi.fhir/hapi-fhir-validation-resources-dstu2/${project.version}</bundle>
|
||||
</feature>
|
||||
|
||||
<feature name='hapi-fhir-validation-hl7org-dstu2' version='${project.version}' start-level='50'>
|
||||
<feature version='${project.version}'>hapi-fhir-validation</feature>
|
||||
<feature version='${project.version}'>hapi-fhir-dstu2</feature>
|
||||
<feature version='${project.version}'>hapi-fhir-hl7org-dstu2</feature>
|
||||
<feature version='${project.version}'>hapi-fhir-r4</feature>
|
||||
<bundle>mvn:ca.uhn.hapi.fhir/hapi-fhir-validation-resources-dstu2/${project.version}</bundle>
|
||||
</feature>
|
||||
|
||||
<feature name='hapi-fhir-validation-dstu2.1' version='${project.version}' start-level='50'>
|
||||
<feature version='${project.version}'>hapi-fhir-validation</feature>
|
||||
<feature version='${project.version}'>hapi-fhir-dstu2.1</feature>
|
||||
<feature version='${project.version}'>hapi-fhir-r4</feature>
|
||||
<bundle>mvn:ca.uhn.hapi.fhir/hapi-fhir-validation-resources-dstu2.1/${project.version}</bundle>
|
||||
</feature>
|
||||
|
||||
|
|
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
|
||||
|
@ -129,6 +136,15 @@
|
|||
<![CDATA[<code>ConceptMap</code>]]> resource from a CSV; and one for exporting a
|
||||
<![CDATA[<code>ConceptMap</code>]]> resource to a CSV.
|
||||
</action>
|
||||
<action type="add">
|
||||
Operation methods on a plain server may now use parameters
|
||||
of type String (i.e. plain Java strings), and any FHIR primitive
|
||||
datatype will be automatically coerced into a String.
|
||||
</action>
|
||||
<action type="add">
|
||||
The HAPI FHIR CLI now supports importing an IGPack file as an import
|
||||
to the validation process.
|
||||
</action>
|
||||
</release>
|
||||
<release version="3.3.0" date="2018-03-29">
|
||||
<action type="add">
|
||||
|
|
Loading…
Reference in New Issue