Merge branch 'master' of github.com:jamesagnew/hapi-fhir

This commit is contained in:
James 2017-09-09 17:23:32 -04:00
commit 4b78fd8029
58 changed files with 2477 additions and 927 deletions

View File

@ -35,6 +35,11 @@
<artifactId>hapi-fhir-structures-dstu3</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-r4</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-hl7org-dstu2</artifactId>

View File

@ -0,0 +1,61 @@
package example;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import org.hl7.fhir.r4.model.Enumerations;
import org.hl7.fhir.r4.model.SearchParameter;
public class GenomicsUploader {
public static void main(String[] theArgs) {
FhirContext ctx = FhirContext.forR4();
IGenericClient client = ctx.newRestfulGenericClient("http://fhirtest.uhn.ca/baseR4");
client.registerInterceptor(new LoggingInterceptor(false));
SearchParameter dnaSequenceVariantName = new SearchParameter();
dnaSequenceVariantName.setId("SearchParameter/dnaSequenceVariantName");
dnaSequenceVariantName.setStatus(Enumerations.PublicationStatus.ACTIVE);
dnaSequenceVariantName.addBase("Observation");
dnaSequenceVariantName.setCode("dnaSequenceVariantName");
dnaSequenceVariantName.setType(Enumerations.SearchParamType.TOKEN);
dnaSequenceVariantName.setTitle("DNASequenceVariantName");
dnaSequenceVariantName.setExpression("Observation.extension('http://hl7.org/fhir/StructureDefinition/observation-geneticsDNASequenceVariantName')");
dnaSequenceVariantName.setXpathUsage(SearchParameter.XPathUsageType.NORMAL);
client.update().resource(dnaSequenceVariantName).execute();
SearchParameter dNAVariantId = new SearchParameter();
dNAVariantId.setId("SearchParameter/dNAVariantId");
dNAVariantId.setStatus(Enumerations.PublicationStatus.ACTIVE);
dNAVariantId.addBase("Observation");
dNAVariantId.setCode("dnaVariantId");
dNAVariantId.setType(Enumerations.SearchParamType.TOKEN);
dNAVariantId.setTitle("DNAVariantId");
dNAVariantId.setExpression("Observation.extension('http://hl7.org/fhir/StructureDefinition/observation-geneticsDNAVariantId')");
dNAVariantId.setXpathUsage(SearchParameter.XPathUsageType.NORMAL);
client.update().resource(dNAVariantId).execute();
SearchParameter gene = new SearchParameter();
gene.setId("SearchParameter/gene");
gene.setStatus(Enumerations.PublicationStatus.ACTIVE);
gene.addBase("Observation");
gene.setCode("gene");
gene.setType(Enumerations.SearchParamType.TOKEN);
gene.setTitle("Gene");
gene.setExpression("Observation.extension('http://hl7.org/fhir/StructureDefinition/observation-geneticsGene')");
gene.setXpathUsage(SearchParameter.XPathUsageType.NORMAL);
client.update().resource(gene).execute();
SearchParameter alleleName = new SearchParameter();
alleleName.setId("SearchParameter/alleleName");
alleleName.setStatus(Enumerations.PublicationStatus.ACTIVE);
alleleName.addBase("Observation");
alleleName.setCode("alleleName");
alleleName.setType(Enumerations.SearchParamType.TOKEN);
alleleName.setTitle("AlleleName");
alleleName.setExpression("Observation.extension('http://hl7.org/fhir/StructureDefinition/observation-geneticsAlleleName')");
alleleName.setXpathUsage(SearchParameter.XPathUsageType.NORMAL);
client.update().resource(alleleName).execute();
}
}

View File

@ -214,6 +214,12 @@ public class ValidatorExamples {
return null;
}
@Override
public List<IBaseResource> fetchAllConformanceResources(FhirContext theContext) {
// TODO: implement
return null;
}
@Override
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
// TODO: implement

View File

@ -20,117 +20,113 @@ package ca.uhn.fhir.context.support;
* #L%
*/
import java.util.List;
import ca.uhn.fhir.context.FhirContext;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
import java.util.List;
public interface IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST> {
/**
* Expands the given portion of a ValueSet
*
* @param theInclude
* The portion to include
* @return The expansion
*/
EVS_OUT expandValueSet(FhirContext theContext, EVS_IN theInclude);
/**
* Expands the given portion of a ValueSet
*
* @param theInclude The portion to include
* @return The expansion
*/
EVS_OUT expandValueSet(FhirContext theContext, EVS_IN theInclude);
/**
* Load and return all possible structure definitions
*/
List<SDT> fetchAllStructureDefinitions(FhirContext theContext);
/**
* Load and return all conformance resources associated with this
* validation support module. This method may return null if it doesn't
* make sense for a given module.
*/
List<IBaseResource> fetchAllConformanceResources(FhirContext theContext);
/**
* Load and return all possible structure definitions
*/
List<SDT> fetchAllStructureDefinitions(FhirContext theContext);
/**
* Fetch a code system by ID
*
* @param theSystem
* The code system
* @return The valueset (must not be null, but can be an empty ValueSet)
*/
CST fetchCodeSystem(FhirContext theContext, String theSystem);
/**
* Fetch a code system by ID
*
* @param theSystem The code system
* @return The valueset (must not be null, but can be an empty ValueSet)
*/
CST fetchCodeSystem(FhirContext theContext, String theSystem);
/**
* Loads a resource needed by the validation (a StructureDefinition, or a
* ValueSet)
*
* @param theContext
* The HAPI FHIR Context object current in use by the validator
* @param theClass
* The type of the resource to load
* @param theUri
* The resource URI
* @return Returns the resource, or <code>null</code> if no resource with the
* given URI can be found
*/
<T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri);
/**
* Loads a resource needed by the validation (a StructureDefinition, or a
* ValueSet)
*
* @param theContext The HAPI FHIR Context object current in use by the validator
* @param theClass The type of the resource to load
* @param theUri The resource URI
* @return Returns the resource, or <code>null</code> if no resource with the
* given URI can be found
*/
<T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri);
SDT fetchStructureDefinition(FhirContext theCtx, String theUrl);
SDT fetchStructureDefinition(FhirContext theCtx, String theUrl);
/**
* Returns <code>true</code> if codes in the given code system can be expanded
* or validated
*
* @param theSystem
* The URI for the code system, e.g. <code>"http://loinc.org"</code>
* @return Returns <code>true</code> if codes in the given code system can be
* validated
*/
boolean isCodeSystemSupported(FhirContext theContext, String theSystem);
/**
* Returns <code>true</code> if codes in the given code system can be expanded
* or validated
*
* @param theSystem The URI for the code system, e.g. <code>"http://loinc.org"</code>
* @return Returns <code>true</code> if codes in the given code system can be
* validated
*/
boolean isCodeSystemSupported(FhirContext theContext, String theSystem);
/**
* Validates that the given code exists and if possible returns a display
* name. This method is called to check codes which are found in "example"
* binding fields (e.g. <code>Observation.code</code> in the default profile.
*
* @param theCodeSystem
* The code system, e.g. "<code>http://loinc.org</code>"
* @param theCode
* The code, e.g. "<code>1234-5</code>"
* @param theDisplay
* The display name, if it should also be validated
* @return Returns a validation result object
*/
CodeValidationResult<CDCT, IST> validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay);
/**
* Validates that the given code exists and if possible returns a display
* name. This method is called to check codes which are found in "example"
* binding fields (e.g. <code>Observation.code</code> in the default profile.
*
* @param theCodeSystem The code system, e.g. "<code>http://loinc.org</code>"
* @param theCode The code, e.g. "<code>1234-5</code>"
* @param theDisplay The display name, if it should also be validated
* @return Returns a validation result object
*/
CodeValidationResult<CDCT, IST> validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay);
public class CodeValidationResult<CDCT, IST> {
private CDCT definition;
private String message;
private IST severity;
private CDCT definition;
private String message;
private IST severity;
public CodeValidationResult(CDCT theNext) {
this.definition = theNext;
}
public CodeValidationResult(CDCT theNext) {
this.definition = theNext;
}
public CodeValidationResult(IST severity, String message) {
this.severity = severity;
this.message = message;
}
public CodeValidationResult(IST severity, String message) {
this.severity = severity;
this.message = message;
}
public CodeValidationResult(IST severity, String message, CDCT definition) {
this.severity = severity;
this.message = message;
this.definition = definition;
}
public CodeValidationResult(IST severity, String message, CDCT definition) {
this.severity = severity;
this.message = message;
this.definition = definition;
}
public CDCT asConceptDefinition() {
return definition;
}
public CDCT asConceptDefinition() {
return definition;
}
public String getMessage() {
return message;
}
public String getMessage() {
return message;
}
public IST getSeverity() {
return severity;
}
public IST getSeverity() {
return severity;
}
public boolean isOk() {
return definition != null;
}
public boolean isOk() {
return definition != null;
}
}
}
}

View File

@ -73,6 +73,11 @@
<artifactId>hapi-fhir-validation-resources-dstu3</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-igpacks</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>

View File

@ -41,6 +41,7 @@ public class App {
ourCommands.add(new ValidationDataUploader());
ourCommands.add(new WebsocketSubscribeCommand());
ourCommands.add(new UploadTerminologyCommand());
ourCommands.add(new IgPackUploader());
Collections.sort(ourCommands);
}

View File

@ -1,53 +1,99 @@
package ca.uhn.fhir.cli;
import org.apache.commons.cli.*;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.fusesource.jansi.Ansi;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public abstract class BaseCommand implements Comparable<BaseCommand> {
private static final String SPEC_DEFAULT_VERSION = "dstu3";
private static final Logger ourLog = LoggerFactory.getLogger(BaseCommand.class);
private FhirContext myFhirCtx;
public BaseCommand() {
super();
}
protected void addFhirVersionOption(Options theOptions) {
Option opt = new Option("f", "fhirversion", true, "Spec version to upload (default is '" + SPEC_DEFAULT_VERSION + "')");
opt.setRequired(false);
theOptions.addOption(opt);
}
@Override
public int compareTo(BaseCommand theO) {
return getCommandName().compareTo(theO.getCommandName());
}
private void downloadFileFromInternet(CloseableHttpResponse result, File localFile) throws IOException {
FileOutputStream buffer = FileUtils.openOutputStream(localFile);
try {
long maxLength = result.getEntity().getContentLength();
long nextLog = -1;
// ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int nRead;
byte[] data = new byte[16384];
while ((nRead = result.getEntity().getContent().read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
long fileSize = FileUtils.sizeOf(localFile);
if (fileSize > nextLog) {
System.err.print("\r" + Ansi.ansi().eraseLine());
System.err.print(FileUtils.byteCountToDisplaySize(fileSize));
if (maxLength > 0) {
System.err.print(" [");
int stars = (int) (50.0f * ((float) fileSize / (float) maxLength));
for (int i = 0; i < stars; i++) {
System.err.print("*");
}
for (int i = stars; i < 50; i++) {
System.err.print(" ");
}
System.err.print("]");
}
System.err.flush();
nextLog += 100000;
}
}
buffer.flush();
System.err.println();
System.err.flush();
} finally {
IOUtils.closeQuietly(buffer);
}
}
public abstract String getCommandDescription();
public abstract String getCommandName();
public abstract Options getOptions();
protected IGenericClient newClient(FhirContext ctx, String theBaseUrl) {
ctx.getRestfulClientFactory().setSocketTimeout(10 * 60 * 1000);
IGenericClient fhirClient = ctx.newRestfulGenericClient(theBaseUrl);
return fhirClient;
}
public abstract void run(CommandLine theCommandLine) throws ParseException, Exception;
// public FhirContext getFhirCtx() {
// if (myFhirCtx == null) {
// myFhirCtx = FhirContext.forDstu2();
// }
// return myFhirCtx;
// }
protected void addFhirVersionOption(Options theOptions) {
Option opt = new Option("f", "fhirversion", true, "Spec version to upload (default is '" + SPEC_DEFAULT_VERSION + "')");
opt.setRequired(false);
theOptions.addOption(opt);
}
protected FhirContext getSpecVersionContext(CommandLine theCommandLine) throws ParseException {
if (myFhirCtx == null) {
String specVersion = theCommandLine.getOptionValue("f", SPEC_DEFAULT_VERSION);
@ -68,4 +114,82 @@ public abstract class BaseCommand implements Comparable<BaseCommand> {
return myFhirCtx;
}
// public FhirContext getFhirCtx() {
// if (myFhirCtx == null) {
// myFhirCtx = FhirContext.forDstu2();
// }
// return myFhirCtx;
// }
protected Collection<File> loadFile(FhirContext theCtx, String theSpecUrl, String theFilepath, boolean theCacheFile) throws IOException {
String userHomeDir = System.getProperty("user.home");
File applicationDir = new File(userHomeDir + File.separator + "." + "hapi-fhir-cli");
FileUtils.forceMkdir(applicationDir);
Collection<File> inputFiles;
if (isNotBlank(theFilepath)) {
ourLog.info("Loading from local path: {}", theFilepath);
if (theFilepath.startsWith("~" + File.separator)) {
theFilepath = userHomeDir + theFilepath.substring(1);
}
File suppliedFile = new File(FilenameUtils.normalize(theFilepath));
if (suppliedFile.isDirectory()) {
inputFiles = FileUtils.listFiles(suppliedFile, new String[]{"zip"}, false);
} else {
inputFiles = Collections.singletonList(suppliedFile);
}
} else {
File cacheDir = new File(applicationDir, "cache");
FileUtils.forceMkdir(cacheDir);
File inputFile = new File(cacheDir, "examples-json-" + theCtx.getVersion().getVersion() + ".zip");
Date cacheExpiryDate = DateUtils.addHours(new Date(), -12);
if (!inputFile.exists() | (theCacheFile && FileUtils.isFileOlder(inputFile, cacheExpiryDate))) {
File exampleFileDownloading = new File(cacheDir, "examples-json-" + theCtx.getVersion().getVersion() + ".zip.partial");
HttpGet get = new HttpGet(theSpecUrl);
CloseableHttpClient client = HttpClientBuilder.create().build();
CloseableHttpResponse result = client.execute(get);
if (result.getStatusLine().getStatusCode() != 200) {
throw new CommandFailureException("Got HTTP " + result.getStatusLine().getStatusCode() + " response code loading " + theSpecUrl);
}
ourLog.info("Downloading from remote url: {}", theSpecUrl);
downloadFileFromInternet(result, exampleFileDownloading);
FileUtils.deleteQuietly(inputFile);
FileUtils.moveFile(exampleFileDownloading, inputFile);
if (!theCacheFile) {
inputFile.deleteOnExit();
}
ourLog.info("Successfully Loaded example pack ({})", FileUtils.byteCountToDisplaySize(FileUtils.sizeOf(inputFile)));
IOUtils.closeQuietly(result.getEntity().getContent());
}
inputFiles = Collections.singletonList(inputFile);
}
return inputFiles;
}
protected IGenericClient newClient(FhirContext ctx, String theBaseUrl) {
ctx.getRestfulClientFactory().setSocketTimeout(10 * 60 * 1000);
IGenericClient fhirClient = ctx.newRestfulGenericClient(theBaseUrl);
return fhirClient;
}
public abstract void run(CommandLine theCommandLine) throws ParseException, Exception;
}

View File

@ -127,79 +127,17 @@ public class ExampleDataUploader extends BaseCommand {
boolean cacheFile = theCommandLine.hasOption('c');
String userHomeDir = System.getProperty("user.home");
File applicationDir = new File(userHomeDir + File.separator + "." + "hapi-fhir-cli");
FileUtils.forceMkdir(applicationDir);
if (isNotBlank(filepath)) {
ourLog.info("Loading from local path: {}", filepath);
if (filepath.startsWith("~" + File.separator)) {
filepath = userHomeDir + filepath.substring(1);
}
File suppliedFile = new File(FilenameUtils.normalize(filepath));
if (suppliedFile.isDirectory()) {
Collection<File> inputFiles;
inputFiles = FileUtils.listFiles(suppliedFile, new String[]{"zip"}, false);
for (File inputFile : inputFiles) {
IBaseBundle bundle = getBundleFromFile(limit, inputFile, ctx);
processBundle(ctx, bundle);
sendBundleToTarget(targetServer, ctx, bundle);
}
} else {
IBaseBundle bundle = getBundleFromFile(limit, suppliedFile, ctx);
processBundle(ctx, bundle);
sendBundleToTarget(targetServer, ctx, bundle);
}
} else {
File cacheDir = new File(applicationDir, "cache");
FileUtils.forceMkdir(cacheDir);
File inputFile = new File(cacheDir, "examples-json-" + ctx.getVersion().getVersion() + ".zip");
Date cacheExpiryDate = DateUtils.addHours(new Date(), -12);
if (!inputFile.exists() | (cacheFile && FileUtils.isFileOlder(inputFile, cacheExpiryDate))) {
File exampleFileDownloading = new File(cacheDir, "examples-json-" + ctx.getVersion().getVersion() + ".zip.partial");
HttpGet get = new HttpGet(specUrl);
CloseableHttpClient client = HttpClientBuilder.create().build();
CloseableHttpResponse result = client.execute(get);
if (result.getStatusLine().getStatusCode() != 200) {
throw new CommandFailureException("Got HTTP " + result.getStatusLine().getStatusCode() + " response code loading " + specUrl);
}
ourLog.info("Downloading from remote url: {}", specUrl);
downloadFileFromInternet(result, exampleFileDownloading);
FileUtils.deleteQuietly(inputFile);
FileUtils.moveFile(exampleFileDownloading, inputFile);
if (!cacheFile) {
inputFile.deleteOnExit();
}
ourLog.info("Successfully Loaded example pack ({})", FileUtils.byteCountToDisplaySize(FileUtils.sizeOf(inputFile)));
IOUtils.closeQuietly(result.getEntity().getContent());
}
Collection<File> inputFiles = loadFile(ctx, specUrl, filepath, cacheFile);
for (File inputFile : inputFiles) {
IBaseBundle bundle = getBundleFromFile(limit, inputFile, ctx);
processBundle(ctx, bundle);
sendBundleToTarget(targetServer, ctx, bundle);
}
}
private IBaseBundle getBundleFromFile(Integer theLimit, File theSuppliedFile, FhirContext theCtx) throws ParseException, IOException {
switch (theCtx.getVersion().getVersion()) {
case DSTU2:
@ -791,43 +729,5 @@ public class ExampleDataUploader extends BaseCommand {
return bundle;
}
private void downloadFileFromInternet(CloseableHttpResponse result, File localFile) throws IOException {
FileOutputStream buffer = FileUtils.openOutputStream(localFile);
try {
long maxLength = result.getEntity().getContentLength();
long nextLog = -1;
// ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int nRead;
byte[] data = new byte[16384];
while ((nRead = result.getEntity().getContent().read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
long fileSize = FileUtils.sizeOf(localFile);
if (fileSize > nextLog) {
System.err.print("\r" + Ansi.ansi().eraseLine());
System.err.print(FileUtils.byteCountToDisplaySize(fileSize));
if (maxLength > 0) {
System.err.print(" [");
int stars = (int) (50.0f * ((float) fileSize / (float) maxLength));
for (int i = 0; i < stars; i++) {
System.err.print("*");
}
for (int i = stars; i < 50; i++) {
System.err.print(" ");
}
System.err.print("]");
}
System.err.flush();
nextLog += 100000;
}
}
buffer.flush();
System.err.println();
System.err.flush();
} finally {
IOUtils.closeQuietly(buffer);
}
}
}

View File

@ -0,0 +1,82 @@
package ca.uhn.fhir.cli;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.hl7.fhir.dstu3.hapi.validation.IValidationSupport;
import org.hl7.fhir.dstu3.model.StructureDefinition;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import parser.IgPackParserDstu3;
import parser.IgPackValidationSupportDstu3;
import java.io.File;
import java.io.FileInputStream;
import java.util.Collection;
public class IgPackUploader extends BaseCommand {
private static final Logger ourLog = LoggerFactory.getLogger(IgPackUploader.class);
@Override
public String getCommandDescription() {
return "Uploads an Implementation Guide Validation Pack";
}
@Override
public String getCommandName() {
return "upload-igpack";
}
@Override
public Options getOptions() {
Options options = new Options();
addFhirVersionOption(options);
Option opt = new Option("t", "target", true, "Base URL for the target server (e.g. \"http://example.com/fhir\")");
opt.setRequired(false);
options.addOption(opt);
opt = new Option("u", "url", true, "The URL to the validation.pack file, e.g. http://hl7.org/fhir/us/core/validator.pack");
opt.setRequired(true);
options.addOption(opt);
return options;
}
@Override
public void run(CommandLine theCommandLine) throws ParseException, Exception {
FhirContext ctx = getSpecVersionContext(theCommandLine);
String targetServer = theCommandLine.getOptionValue("t");
IGenericClient client = ctx.newRestfulGenericClient(targetServer);
String url = theCommandLine.getOptionValue("u");
Collection<File> files = loadFile(ctx, url, null, false);
for (File nextFile : files) {
switch (ctx.getVersion().getVersion()) {
case DSTU3:
IgPackParserDstu3 packParser = new IgPackParserDstu3(ctx);
IValidationSupport ig = packParser.parseIg(new FileInputStream(nextFile), nextFile.getName());
Iterable<IBaseResource> conformanceResources = ig.fetchAllConformanceResources(ctx);
for (IBaseResource nextResource : conformanceResources) {
String nextResourceUrl = ((IPrimitiveType<?>)ctx.newTerser().getSingleValueOrNull(nextResource, "url")).getValueAsString();
ourLog.info("Uploading resource: {}", nextResourceUrl);
client
.update()
.resource(nextResource)
.conditional()
.and(StructureDefinition.URL.matches().value(nextResourceUrl))
.execute();
}
default:
throw new ParseException("This command does not support FHIR version " + ctx.getVersion().getVersion());
}
}
}
}

View File

@ -1,8 +1,9 @@
package ca.uhn.fhir.cli;
import java.util.Collections;
import java.util.List;
import ca.uhn.fhir.context.FhirContext;
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 org.hl7.fhir.dstu3.hapi.validation.IValidationSupport;
import org.hl7.fhir.dstu3.model.CodeSystem;
import org.hl7.fhir.dstu3.model.StructureDefinition;
@ -10,10 +11,8 @@ import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
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.Collections;
import java.util.List;
public class LoadingValidationSupportDstu3 implements IValidationSupport {
@ -26,6 +25,11 @@ public class LoadingValidationSupportDstu3 implements IValidationSupport {
return null;
}
@Override
public List<IBaseResource> fetchAllConformanceResources(FhirContext theContext) {
return null;
}
@Override
public CodeSystem fetchCodeSystem(FhirContext theContext, String theSystem) {
return null;

View File

@ -1,30 +1,38 @@
package ca.uhn.fhir.cli;
import java.util.Collections;
import java.util.List;
import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.StructureDefinition;
import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
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 org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.StructureDefinition;
import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent;
import java.util.Collections;
import java.util.List;
public class LoadingValidationSupportR4 implements org.hl7.fhir.r4.hapi.ctx.IValidationSupport {
private FhirContext myCtx = FhirContext.forR4();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(LoadingValidationSupportR4.class);
private FhirContext myCtx = FhirContext.forR4();
@Override
public ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) {
return null;
}
@Override
public List<IBaseResource> fetchAllConformanceResources(FhirContext theContext) {
return null;
}
@Override
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
return Collections.emptyList();
}
@Override
public CodeSystem fetchCodeSystem(FhirContext theContext, String theSystem) {
return null;
@ -63,9 +71,4 @@ public class LoadingValidationSupportR4 implements org.hl7.fhir.r4.hapi.ctx.IVal
return null;
}
@Override
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
return Collections.emptyList();
}
}

View File

@ -0,0 +1,11 @@
import org.junit.Test;
public class InstallIgPackTest {
@Test
public void testInstallIgPack() {
}
}

View File

@ -13,12 +13,9 @@ public class ValidateTest {
@Test
public void testValidateLocalProfile() {
// String profilePath = ValidateTest.class.getResource("/uslab-patient.profile.xml").getFile();
String resourcePath = ValidateTest.class.getResource("/patient-uslab-example1.xml").getFile();
// ourLog.info(profilePath);
ourLog.info(resourcePath);
// App.main(new String[] {"validate", "-p", "-n", resourcePath, "-l", profilePath});
App.main(new String[] {"validate", "-p", "-n", resourcePath});
}

View File

@ -26,6 +26,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.Constants;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.Validate;
@ -107,7 +108,12 @@ public class LoggingInterceptor implements IClientInterceptor {
*/
List<String> locationHeaders = theResponse.getHeaders(Constants.HEADER_LOCATION);
if (locationHeaders != null && locationHeaders.size() > 0) {
respLocation = " (Location: " + locationHeaders.get(0) + ")";
String locationValue = locationHeaders.get(0);
IdDt locationValueId = new IdDt(locationValue);
if (locationValueId.hasBaseUrl() && locationValueId.hasIdPart()) {
locationValue = locationValueId.toUnqualified().getValue();
}
respLocation = " (" + locationValue + ")";
}
myLog.info("Client response: {}{}", message, respLocation);
}

46
hapi-fhir-igpacks/pom.xml Normal file
View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>hapi-deployable-pom</artifactId>
<groupId>ca.uhn.hapi.fhir</groupId>
<version>3.0.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>hapi-fhir-igpacks</artifactId>
<dependencies>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jpaserver-base</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu3</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
</plugins>
</build>
</project>

View File

@ -0,0 +1,121 @@
package parser;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.jpa.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.validation.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 IgPackParserDstu3(FhirContext theCtx) {
FhirVersionEnum expectedVersion = FhirVersionEnum.DSTU3;
Validate.isTrue(theCtx.getVersion().getVersion() == expectedVersion, "theCtx is not for the correct version, expecting " + expectedVersion);
myCtx = 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;
}
/**
* @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);
}
}
}

View File

@ -0,0 +1,104 @@
package parser;
import ca.uhn.fhir.context.FhirContext;
import org.hl7.fhir.dstu3.hapi.validation.IValidationSupport;
import org.hl7.fhir.dstu3.model.CodeSystem;
import org.hl7.fhir.dstu3.model.ConceptMap;
import org.hl7.fhir.dstu3.model.StructureDefinition;
import org.hl7.fhir.dstu3.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 IgPackValidationSupportDstu3 implements IValidationSupport {
private final Map<IIdType, IBaseResource> myIgResources;
public IgPackValidationSupportDstu3(Map<IIdType, IBaseResource> theIgResources) {
myIgResources = theIgResources;
}
@Override
public ValueSet.ValueSetExpansionComponent expandValueSet(FhirContext theContext, ValueSet.ConceptSetComponent theInclude) {
return null;
}
@Override
public List<IBaseResource> fetchAllConformanceResources(FhirContext theContext) {
return new ArrayList<>(myIgResources.values());
}
@Override
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
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 CodeSystem fetchCodeSystem(FhirContext theContext, String theSystem) {
return fetchResource(theContext, CodeSystem.class, theSystem);
}
@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(CodeSystem.class)) {
if (theClass.isAssignableFrom(next.getValue().getClass())) {
CodeSystem sd = ((CodeSystem) next.getValue());
if (sd.getUrl().equals(theUri)) {
return (T) sd;
}
}
}
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 StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl) {
return fetchResource(theCtx, StructureDefinition.class, theUrl);
}
@Override
public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) {
return false;
}
@Override
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
return null;
}
}

View File

@ -0,0 +1,30 @@
package ca.uhn.fhir.igpack.parser;
import ca.uhn.fhir.context.FhirContext;
import org.hl7.fhir.dstu3.hapi.validation.IValidationSupport;
import org.hl7.fhir.dstu3.model.ValueSet;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import parser.IgPackParserDstu3;
import static org.junit.Assert.*;
public class IgPackParserDstu3Test {
private static final Logger ourLog = LoggerFactory.getLogger(IgPackParserDstu3Test.class);
@Test
public void testParseIg() {
FhirContext ctx = FhirContext.forDstu3();
IgPackParserDstu3 igParser = new IgPackParserDstu3(ctx);
IValidationSupport result = igParser.parseIg(IgPackParserDstu3Test.class.getResourceAsStream("/us-core-stu3-validator.pack"), "US-Core STU3");
assertNotNull(result.fetchResource(ctx, ValueSet.class, "http://hl7.org/fhir/us/core/ValueSet/simple-language"));
assertEquals(50, result.fetchAllConformanceResources(ctx).size());
}
}

View File

@ -0,0 +1,30 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%file:%line] - %msg%n
</pattern>
</encoder>
</appender>
<logger name="org.eclipse" additivity="false" level="info">
<appender-ref ref="STDOUT" />
</logger>
<logger name="org.apache" additivity="false" level="info">
<appender-ref ref="STDOUT" />
</logger>
<logger name="org.thymeleaf" additivity="false" level="warn">
<appender-ref ref="STDOUT" />
</logger>
<!--
<logger name="ca.uhn.fhir.rest.client" additivity="false" level="trace">
<appender-ref ref="STDOUT" />
</logger>
-->
<root level="info">
<appender-ref ref="STDOUT" />
</root>
</configuration>

View File

@ -165,11 +165,6 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
@Autowired
private IResourceIndexedCompositeStringUniqueDao myResourceIndexedCompositeStringUniqueDao;
private <T extends IBaseResource> void autoCreateResource(T theResource) {
IFhirResourceDao<T> dao = (IFhirResourceDao<T>) getDao(theResource.getClass());
dao.create(theResource);
}
protected void clearRequestAsProcessingSubRequest(ServletRequestDetails theRequestDetails) {
if (theRequestDetails != null) {
theRequestDetails.getUserData().remove(PROCESSING_SUB_REQUEST);
@ -243,16 +238,20 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
if (nextParam.getParamName().equals(nextCompositeOf.getName())) {
IQueryParameterType nextParamAsClientParam = nextParam.toQueryParameterType();
String value = nextParamAsClientParam.getValueAsQueryToken(getContext());
value = UrlUtil.escape(value);
nextChoicesList.add(key + "=" + value);
if (isNotBlank(value)) {
value = UrlUtil.escape(value);
nextChoicesList.add(key + "=" + value);
}
}
}
}
if (linksForCompositePart != null) {
for (ResourceLink nextLink : linksForCompositePart) {
String value = nextLink.getTargetResource().getIdDt().toUnqualifiedVersionless().getValue();
value = UrlUtil.escape(value);
nextChoicesList.add(key + "=" + value);
if (isNotBlank(value)) {
value = UrlUtil.escape(value);
nextChoicesList.add(key + "=" + value);
}
}
}
}
@ -260,7 +259,9 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
Set<String> queryStringsToPopulate = extractCompositeStringUniquesValueChains(theEntity.getResourceType(), partsChoices);
for (String nextQueryString : queryStringsToPopulate) {
compositeStringUniques.add(new ResourceIndexedCompositeStringUnique(theEntity, nextQueryString));
if (isNotBlank(nextQueryString)) {
compositeStringUniques.add(new ResourceIndexedCompositeStringUnique(theEntity, nextQueryString));
}
}
}
@ -417,10 +418,12 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
if (getConfig().isAutoCreatePlaceholderReferenceTargets()) {
IBaseResource newResource = missingResourceDef.newInstance();
newResource.setId(resName + "/" + id);
autoCreateResource(newResource);
IFhirResourceDao<IBaseResource> placeholderResourceDao = (IFhirResourceDao<IBaseResource>) getDao(newResource.getClass());
ourLog.info("Automatically creating empty placeholder resource: {}", newResource.getIdElement().getValue());
valueOf = placeholderResourceDao.update(newResource).getEntity().getId();
} else {
throw new InvalidRequestException("Resource " + resName + "/" + id + " not found, specified in path: " + nextPathsUnsplit);
}
throw new InvalidRequestException("Resource " + resName + "/" + id + " not found, specified in path: " + nextPathsUnsplit);
}
ResourceTable target = myEntityManager.find(ResourceTable.class, valueOf);
RuntimeResourceDefinition targetResourceDef = getContext().getResourceDefinition(type);
@ -1562,7 +1565,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
theEntity.setParamsUriPopulated(uriParams.isEmpty() == false);
theEntity.setParamsCoords(coordsParams);
theEntity.setParamsCoordsPopulated(coordsParams.isEmpty() == false);
theEntity.setParamsCompositeStringUnique(compositeStringUniques);
// theEntity.setParamsCompositeStringUnique(compositeStringUniques);
theEntity.setParamsCompositeStringUniquePresent(compositeStringUniques.isEmpty() == false);
theEntity.setResourceLinks(links);
theEntity.setHasLinks(links.isEmpty() == false);
@ -1715,11 +1718,24 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
theEntity.setResourceLinks(links);
// Store composite string uniques
for (ResourceIndexedCompositeStringUnique next : existingCompositeStringUniques) {
myEntityManager.remove(next);
}
for (ResourceIndexedCompositeStringUnique next : compositeStringUniques) {
myEntityManager.persist(next);
if (getConfig().isUniqueIndexesEnabled()) {
for (ResourceIndexedCompositeStringUnique next : existingCompositeStringUniques) {
if (!compositeStringUniques.contains(next)) {
myEntityManager.remove(next);
}
}
for (ResourceIndexedCompositeStringUnique next : compositeStringUniques) {
if (!existingCompositeStringUniques.contains(next)) {
if (myConfig.isUniqueIndexesCheckedBeforeSave()) {
ResourceIndexedCompositeStringUnique existing = myResourceIndexedCompositeStringUniqueDao.findByQueryString(next.getIndexString());
if (existing != null) {
throw new PreconditionFailedException("Can not create resource of type " + theEntity.getResourceType() + " as it would create a duplicate index matching query: " + next.getIndexString() + " (existing index belongs to " + existing.getResource().getIdDt().toUnqualifiedVersionless().getValue() + ")");
}
}
ourLog.debug("Persisting unique index: {}", next);
myEntityManager.persist(next);
}
}
}
theEntity.toString();
@ -1897,6 +1913,21 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
*/
public static Set<String> extractCompositeStringUniquesValueChains(String theResourceType, List<List<String>> thePartsChoices) {
for (List<String> next : thePartsChoices) {
for (Iterator<String> iter = next.iterator(); iter.hasNext(); ) {
if (isBlank(iter.next())) {
iter.remove();
}
}
if (next.isEmpty()) {
return Collections.emptySet();
}
}
if (thePartsChoices.isEmpty()) {
return Collections.emptySet();
}
Collections.sort(thePartsChoices, new Comparator<List<String>>() {
@Override
public int compare(List<String> o1, List<String> o2) {

View File

@ -75,10 +75,64 @@ public class DaoConfig {
private boolean myDeleteStaleSearches = true;
private boolean myEnforceReferentialIntegrityOnDelete = true;
private boolean myUniqueIndexesEnabled = true;
/**
* If set to <code>true</code> (default is <code>true</code>), indexes will be
* created for search parameters marked as {@link ca.uhn.fhir.jpa.util.JpaConstants#EXT_SP_UNIQUE}.
* This is a HAPI FHIR specific extension which can be used to specify that no more than one
* resource can exist which matches a given criteria, using a database constraint to
* enforce this.
*/
public boolean isUniqueIndexesEnabled() {
return myUniqueIndexesEnabled;
}
/**
* If set to <code>true</code> (default is <code>true</code>), indexes will be
* created for search parameters marked as {@link ca.uhn.fhir.jpa.util.JpaConstants#EXT_SP_UNIQUE}.
* This is a HAPI FHIR specific extension which can be used to specify that no more than one
* resource can exist which matches a given criteria, using a database constraint to
* enforce this.
*/
public void setUniqueIndexesEnabled(boolean theUniqueIndexesEnabled) {
myUniqueIndexesEnabled = theUniqueIndexesEnabled;
}
/**
* When using {@link #setUniqueIndexesEnabled(boolean) unique indexes}, if this
* setting is set to <code>true</code> (default is <code>true</code>) the system
* will test for the existence of a particular unique index value prior to saving
* a new one.
* <p>
* This causes friendlier error messages to be generated, but adds an
* extra round-trip to the database for eavh save so it can cause
* a small performance hit.
* </p>
*/
public boolean isUniqueIndexesCheckedBeforeSave() {
return myUniqueIndexesCheckedBeforeSave;
}
/**
* When using {@link #setUniqueIndexesEnabled(boolean) unique indexes}, if this
* setting is set to <code>true</code> (default is <code>true</code>) the system
* will test for the existence of a particular unique index value prior to saving
* a new one.
* <p>
* This causes friendlier error messages to be generated, but adds an
* extra round-trip to the database for eavh save so it can cause
* a small performance hit.
* </p>
*/
public void setUniqueIndexesCheckedBeforeSave(boolean theUniqueIndexesCheckedBeforeSave) {
myUniqueIndexesCheckedBeforeSave = theUniqueIndexesCheckedBeforeSave;
}
private boolean myUniqueIndexesCheckedBeforeSave = true;
private boolean myEnforceReferentialIntegrityOnWrite = true;
private int myEverythingIncludesFetchPageSize = 50;
/**
* update setter javadoc if default changes
*/

View File

@ -1258,33 +1258,35 @@ public class SearchBuilder implements ISearchBuilder {
* Check if there is a unique key associated with the set
* of parameters passed in
*/
if (myParams.getIncludes().isEmpty()) {
if (myParams.getRevIncludes().isEmpty()) {
if (myParams.getEverythingMode() == null) {
if (myParams.isAllParametersHaveNoModifier()) {
Set<String> paramNames = theParams.keySet();
if (paramNames.isEmpty() == false) {
List<JpaRuntimeSearchParam> searchParams = mySearchParamRegistry.getActiveUniqueSearchParams(myResourceName, paramNames);
if (searchParams.size() > 0) {
List<List<String>> params = new ArrayList<>();
for (Entry<String, List<List<? extends IQueryParameterType>>> nextParamNameToValues : theParams.entrySet()) {
String nextParamName = nextParamNameToValues.getKey();
nextParamName = UrlUtil.escape(nextParamName);
for (List<? extends IQueryParameterType> nextAnd : nextParamNameToValues.getValue()) {
ArrayList<String> nextValueList = new ArrayList<>();
params.add(nextValueList);
for (IQueryParameterType nextOr : nextAnd) {
String nextOrValue = nextOr.getValueAsQueryToken(myContext);
nextOrValue = UrlUtil.escape(nextOrValue);
nextValueList.add(nextParamName + "=" + nextOrValue);
if (myCallingDao.getConfig().isUniqueIndexesEnabled()) {
if (myParams.getIncludes().isEmpty()) {
if (myParams.getRevIncludes().isEmpty()) {
if (myParams.getEverythingMode() == null) {
if (myParams.isAllParametersHaveNoModifier()) {
Set<String> paramNames = theParams.keySet();
if (paramNames.isEmpty() == false) {
List<JpaRuntimeSearchParam> searchParams = mySearchParamRegistry.getActiveUniqueSearchParams(myResourceName, paramNames);
if (searchParams.size() > 0) {
List<List<String>> params = new ArrayList<>();
for (Entry<String, List<List<? extends IQueryParameterType>>> nextParamNameToValues : theParams.entrySet()) {
String nextParamName = nextParamNameToValues.getKey();
nextParamName = UrlUtil.escape(nextParamName);
for (List<? extends IQueryParameterType> nextAnd : nextParamNameToValues.getValue()) {
ArrayList<String> nextValueList = new ArrayList<>();
params.add(nextValueList);
for (IQueryParameterType nextOr : nextAnd) {
String nextOrValue = nextOr.getValueAsQueryToken(myContext);
nextOrValue = UrlUtil.escape(nextOrValue);
nextValueList.add(nextParamName + "=" + nextOrValue);
}
}
}
Set<String> uniqueQueryStrings = BaseHapiFhirDao.extractCompositeStringUniquesValueChains(myResourceName, params);
ourLastHandlerMechanismForUnitTest = HandlerTypeEnum.UNIQUE_INDEX;
return new UniqueIndexIterator(uniqueQueryStrings);
}
Set<String> uniqueQueryStrings = BaseHapiFhirDao.extractCompositeStringUniquesValueChains(myResourceName, params);
ourLastHandlerMechanismForUnitTest = HandlerTypeEnum.UNIQUE_INDEX;
return new UniqueIndexIterator(uniqueQueryStrings);
}
}
}

View File

@ -58,7 +58,7 @@ public class FhirResourceDaoSearchParameterDstu3 extends FhirResourceDaoDstu3<Se
String expression = theResource.getExpression();
if (isNotBlank(expression)) {
final String resourceType = expression.substring(0, expression.indexOf('.'));
ourLog.info("Marking all resources of type {} for reindexing due to updated search parameter with path: {}", expression);
ourLog.info("Marking all resources of type {} for reindexing due to updated search parameter with path: {}", resourceType, expression);
TransactionTemplate txTemplate = new TransactionTemplate(myPlatformTransactionManager);
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

View File

@ -662,6 +662,7 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
String nextReplacementIdPart = nextReplacementId.getValueAsString();
if (nextTemporaryId.isUrn() && nextTemporaryIdPart.length() > IdType.URN_PREFIX.length()) {
matchUrl = matchUrl.replace(nextTemporaryIdPart, nextReplacementIdPart);
matchUrl = matchUrl.replace(UrlUtil.escape(nextTemporaryIdPart), nextReplacementIdPart);
}
}
}

View File

@ -1,15 +1,23 @@
package ca.uhn.fhir.jpa.dao.dstu3;
import java.util.Collections;
import java.util.List;
import javax.transaction.Transactional;
import javax.transaction.Transactional.TxType;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.param.UriParam;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import javax.transaction.Transactional;
import javax.transaction.Transactional.TxType;
import java.util.Collections;
import java.util.List;
/*
* #%L
@ -31,18 +39,7 @@ import org.hl7.fhir.instance.model.api.IAnyResource;
* #L%
*/
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.param.UriParam;
@Transactional(value=TxType.REQUIRED)
@Transactional(value = TxType.REQUIRED)
public class JpaValidationSupportDstu3 implements IJpaValidationSupportDstu3 {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JpaValidationSupportDstu3.class);
@ -72,11 +69,22 @@ public class JpaValidationSupportDstu3 implements IJpaValidationSupportDstu3 {
@Override
@Transactional(value=TxType.SUPPORTS)
@Transactional(value = TxType.SUPPORTS)
public ValueSetExpansionComponent expandValueSet(FhirContext theCtx, ConceptSetComponent theInclude) {
return null;
}
@Override
public List<IBaseResource> fetchAllConformanceResources(FhirContext theContext) {
return null;
}
@Override
@Transactional(value = TxType.SUPPORTS)
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
return Collections.emptyList();
}
@Override
public CodeSystem fetchCodeSystem(FhirContext theCtx, String theSystem) {
return fetchResource(theCtx, CodeSystem.class, theSystem);
@ -103,7 +111,7 @@ public class JpaValidationSupportDstu3 implements IJpaValidationSupportDstu3 {
params = new SearchParameterMap();
params.setLoadSynchronousUpTo(1);
params.add(ValueSet.SP_URL, new UriParam(theUri));
search = myValueSetDao.search(params);
search = myValueSetDao.search(params);
}
} else {
SearchParameterMap params = new SearchParameterMap();
@ -145,28 +153,20 @@ public class JpaValidationSupportDstu3 implements IJpaValidationSupportDstu3 {
}
@Override
@Transactional(value=TxType.SUPPORTS)
public StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl) {
return fetchResource(theCtx, StructureDefinition.class, theUrl);
}
@Override
@Transactional(value = TxType.SUPPORTS)
public boolean isCodeSystemSupported(FhirContext theCtx, String theSystem) {
return false;
}
@Override
@Transactional(value=TxType.SUPPORTS)
@Transactional(value = TxType.SUPPORTS)
public CodeValidationResult validateCode(FhirContext theCtx, String theCodeSystem, String theCode, String theDisplay) {
return null;
}
@Override
public StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl) {
return fetchResource(theCtx, StructureDefinition.class, theUrl);
}
@Override
@Transactional(value=TxType.SUPPORTS)
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
return Collections.emptyList();
}
}

View File

@ -60,7 +60,7 @@ public class FhirResourceDaoSearchParameterR4 extends FhirResourceDaoR4<SearchPa
String expression = theResource.getExpression();
if (isNotBlank(expression)) {
final String resourceType = expression.substring(0, expression.indexOf('.'));
ourLog.info("Marking all resources of type {} for reindexing due to updated search parameter with path: {}", expression);
ourLog.info("Marking all resources of type {} for reindexing due to updated search parameter with path: {}", resourceType, expression);
TransactionTemplate txTemplate = new TransactionTemplate(myPlatformTransactionManager);
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

View File

@ -662,6 +662,7 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
String nextReplacementIdPart = nextReplacementId.getValueAsString();
if (nextTemporaryId.isUrn() && nextTemporaryIdPart.length() > IdType.URN_PREFIX.length()) {
matchUrl = matchUrl.replace(nextTemporaryIdPart, nextReplacementIdPart);
matchUrl = matchUrl.replace(UrlUtil.escape(nextTemporaryIdPart), nextReplacementIdPart);
}
}
}

View File

@ -1,15 +1,23 @@
package ca.uhn.fhir.jpa.dao.r4;
import java.util.Collections;
import java.util.List;
import javax.transaction.Transactional;
import javax.transaction.Transactional.TxType;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.param.UriParam;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import javax.transaction.Transactional;
import javax.transaction.Transactional.TxType;
import java.util.Collections;
import java.util.List;
/*
* #%L
@ -31,18 +39,7 @@ import org.hl7.fhir.instance.model.api.IAnyResource;
* #L%
*/
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.param.UriParam;
@Transactional(value=TxType.REQUIRED)
@Transactional(value = TxType.REQUIRED)
public class JpaValidationSupportR4 implements IJpaValidationSupportR4 {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JpaValidationSupportR4.class);
@ -72,11 +69,22 @@ public class JpaValidationSupportR4 implements IJpaValidationSupportR4 {
@Override
@Transactional(value=TxType.SUPPORTS)
@Transactional(value = TxType.SUPPORTS)
public ValueSetExpansionComponent expandValueSet(FhirContext theCtx, ConceptSetComponent theInclude) {
return null;
}
@Override
public List<IBaseResource> fetchAllConformanceResources(FhirContext theContext) {
return null;
}
@Override
@Transactional(value = TxType.SUPPORTS)
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
return Collections.emptyList();
}
@Override
public CodeSystem fetchCodeSystem(FhirContext theCtx, String theSystem) {
return fetchResource(theCtx, CodeSystem.class, theSystem);
@ -103,7 +111,7 @@ public class JpaValidationSupportR4 implements IJpaValidationSupportR4 {
params = new SearchParameterMap();
params.setLoadSynchronousUpTo(1);
params.add(ValueSet.SP_URL, new UriParam(theUri));
search = myValueSetDao.search(params);
search = myValueSetDao.search(params);
}
} else {
SearchParameterMap params = new SearchParameterMap();
@ -145,28 +153,20 @@ public class JpaValidationSupportR4 implements IJpaValidationSupportR4 {
}
@Override
@Transactional(value=TxType.SUPPORTS)
public StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl) {
return fetchResource(theCtx, StructureDefinition.class, theUrl);
}
@Override
@Transactional(value = TxType.SUPPORTS)
public boolean isCodeSystemSupported(FhirContext theCtx, String theSystem) {
return false;
}
@Override
@Transactional(value=TxType.SUPPORTS)
@Transactional(value = TxType.SUPPORTS)
public CodeValidationResult validateCode(FhirContext theCtx, String theCodeSystem, String theCode, String theDisplay) {
return null;
}
@Override
public StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl) {
return fetchResource(theCtx, StructureDefinition.class, theUrl);
}
@Override
@Transactional(value=TxType.SUPPORTS)
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
return Collections.emptyList();
}
}

View File

@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.entity;
* #L%
*/
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.*;
import org.hl7.fhir.r4.model.Resource;
@ -108,6 +109,7 @@ public class ResourceIndexedCompositeStringUnique implements Comparable<Resource
}
public void setResource(ResourceTable theResource) {
Validate.notNull(theResource);
myResource = theResource;
}

View File

@ -35,6 +35,7 @@ import ca.uhn.fhir.rest.param.DateRangeParam;
public class BaseJpaSystemProvider<T, MT> extends BaseJpaProvider {
public static final String MARK_ALL_RESOURCES_FOR_REINDEXING = "$mark-all-resources-for-reindexing";
public static final String PERFORM_REINDEXING_PASS = "$perform-reindexing-pass";
private IFhirSystemDao<T, MT> myDao;

View File

@ -30,11 +30,9 @@ import ca.uhn.fhir.util.ParametersUtil;
public abstract class BaseJpaSystemProviderDstu2Plus<T, MT> extends BaseJpaSystemProvider<T, MT> {
//@formatter:off
@Operation(name=MARK_ALL_RESOURCES_FOR_REINDEXING, idempotent=true, returnParameters= {
@OperationParam(name="status")
})
//@formatter:on
public IBaseResource markAllResourcesForReindexing() {
int count = getDao().markAllResourcesForReindexing();
@ -46,5 +44,18 @@ public abstract class BaseJpaSystemProviderDstu2Plus<T, MT> extends BaseJpaSyste
return retVal;
}
@Operation(name=PERFORM_REINDEXING_PASS, idempotent=true, returnParameters= {
@OperationParam(name="status")
})
public IBaseResource performReindexingPass() {
int count = getDao().performReindexingPass(1000);
IBaseParameters retVal = ParametersUtil.newInstance(getContext());
IPrimitiveType<?> string = ParametersUtil.createString(getContext(), "Indexed " + count + " resources");
ParametersUtil.addParameterToParameters(getContext(), retVal, string, "status");
return retVal;
}
}

View File

@ -46,10 +46,7 @@ import org.springframework.transaction.support.TransactionSynchronizationManager
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.*;
import java.util.concurrent.*;
public abstract class BaseSubscriptionInterceptor extends ServerOperationInterceptorAdapter {
@ -63,13 +60,15 @@ public abstract class BaseSubscriptionInterceptor extends ServerOperationInterce
private static final Integer MAX_SUBSCRIPTION_RESULTS = 1000;
private SubscribableChannel myProcessingChannel;
private SubscribableChannel myDeliveryChannel;
private ExecutorService myExecutor;
private ExecutorService myProcessingExecutor;
private int myExecutorThreadCount;
private SubscriptionActivatingSubscriber mySubscriptionActivatingSubscriber;
private MessageHandler mySubscriptionCheckingSubscriber;
private ConcurrentHashMap<String, IBaseResource> myIdToSubscription = new ConcurrentHashMap<>();
private Logger ourLog = LoggerFactory.getLogger(BaseSubscriptionInterceptor.class);
private BlockingQueue<Runnable> myExecutorQueue;
private ThreadPoolExecutor myDeliveryExecutor;
private LinkedBlockingQueue<Runnable> myProcessingExecutorQueue;
private LinkedBlockingQueue<Runnable> myDeliveryExecutorQueue;
/**
* Constructor
@ -89,8 +88,8 @@ public abstract class BaseSubscriptionInterceptor extends ServerOperationInterce
myDeliveryChannel = theDeliveryChannel;
}
public BlockingQueue<Runnable> getExecutorQueueForUnitTests() {
return myExecutorQueue;
public int getExecutorQueueSizeForUnitTests() {
return myProcessingExecutorQueue.size() + myDeliveryExecutorQueue.size();
}
public int getExecutorThreadCount() {
@ -157,42 +156,73 @@ public abstract class BaseSubscriptionInterceptor extends ServerOperationInterce
@PostConstruct
public void postConstruct() {
myExecutorQueue = new LinkedBlockingQueue<>(1000);
{
myProcessingExecutorQueue = new LinkedBlockingQueue<>(1000);
RejectedExecutionHandler rejectedExecutionHandler = new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable theRunnable, ThreadPoolExecutor theExecutor) {
ourLog.info("Note: Executor queue is full ({} elements), waiting for a slot to become available!", myExecutorQueue.size());
StopWatch sw = new StopWatch();
try {
myExecutorQueue.put(theRunnable);
} catch (InterruptedException theE) {
throw new RejectedExecutionException("Task " + theRunnable.toString() +
" rejected from " + theE.toString());
RejectedExecutionHandler rejectedExecutionHandler = new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable theRunnable, ThreadPoolExecutor theExecutor) {
ourLog.info("Note: Executor queue is full ({} elements), waiting for a slot to become available!", myProcessingExecutorQueue.size());
StopWatch sw = new StopWatch();
try {
myProcessingExecutorQueue.put(theRunnable);
} catch (InterruptedException theE) {
throw new RejectedExecutionException("Task " + theRunnable.toString() +
" rejected from " + theE.toString());
}
ourLog.info("Slot become available after {}ms", sw.getMillis());
}
ourLog.info("Slot become available after {}ms", sw.getMillis());
}
};
ThreadFactory threadFactory = new BasicThreadFactory.Builder()
.namingPattern("subscription-%d")
.daemon(false)
.priority(Thread.NORM_PRIORITY)
.build();
myExecutor = new ThreadPoolExecutor(
1,
getExecutorThreadCount(),
0L,
TimeUnit.MILLISECONDS,
myExecutorQueue,
threadFactory,
rejectedExecutionHandler);
};
ThreadFactory threadFactory = new BasicThreadFactory.Builder()
.namingPattern("subscription-proc-%d")
.daemon(false)
.priority(Thread.NORM_PRIORITY)
.build();
myProcessingExecutor = new ThreadPoolExecutor(
1,
getExecutorThreadCount(),
0L,
TimeUnit.MILLISECONDS,
myProcessingExecutorQueue,
threadFactory,
rejectedExecutionHandler);
}
{
myDeliveryExecutorQueue = new LinkedBlockingQueue<>(1000);
BasicThreadFactory threadFactory = new BasicThreadFactory.Builder()
.namingPattern("subscription-delivery-%d")
.daemon(false)
.priority(Thread.NORM_PRIORITY)
.build();
RejectedExecutionHandler rejectedExecutionHandler2 = new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable theRunnable, ThreadPoolExecutor theExecutor) {
ourLog.info("Note: Executor queue is full ({} elements), waiting for a slot to become available!", myDeliveryExecutorQueue.size());
StopWatch sw = new StopWatch();
try {
myDeliveryExecutorQueue.put(theRunnable);
} catch (InterruptedException theE) {
throw new RejectedExecutionException("Task " + theRunnable.toString() +
" rejected from " + theE.toString());
}
ourLog.info("Slot become available after {}ms", sw.getMillis());
}
};
myDeliveryExecutor = new ThreadPoolExecutor(
1,
getExecutorThreadCount(),
0L,
TimeUnit.MILLISECONDS,
myDeliveryExecutorQueue,
threadFactory,
rejectedExecutionHandler2);
}
if (getProcessingChannel() == null) {
setProcessingChannel(new ExecutorSubscribableChannel(myExecutor));
setProcessingChannel(new ExecutorSubscribableChannel(myProcessingExecutor));
}
if (getDeliveryChannel() == null) {
setDeliveryChannel(new ExecutorSubscribableChannel(myExecutor));
setDeliveryChannel(new ExecutorSubscribableChannel(myDeliveryExecutor));
}
if (mySubscriptionActivatingSubscriber == null) {

View File

@ -21,12 +21,14 @@ package ca.uhn.fhir.jpa.subscription;
*/
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor;
import ca.uhn.fhir.rest.gclient.IClientExecutable;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4.model.Subscription;
@ -78,44 +80,51 @@ public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionSu
if (!(theMessage.getPayload() instanceof ResourceDeliveryMessage)) {
return;
}
try {
ResourceDeliveryMessage msg = (ResourceDeliveryMessage) theMessage.getPayload();
ResourceDeliveryMessage msg = (ResourceDeliveryMessage) theMessage.getPayload();
if (!subscriptionTypeApplies(getContext(), msg.getSubscription())) {
return;
}
IBaseResource subscription = msg.getSubscription();
// Grab the endpoint from the subscription
IPrimitiveType<?> endpoint = getContext().newTerser().getSingleValueOrNull(subscription, BaseSubscriptionInterceptor.SUBSCRIPTION_ENDPOINT, IPrimitiveType.class);
String endpointUrl = endpoint.getValueAsString();
// Grab the payload type (encoding mimetype) from the subscription
IPrimitiveType<?> payload = getContext().newTerser().getSingleValueOrNull(subscription, BaseSubscriptionInterceptor.SUBSCRIPTION_PAYLOAD, IPrimitiveType.class);
String payloadString = payload.getValueAsString();
if (payloadString.contains(";")) {
payloadString = payloadString.substring(0, payloadString.indexOf(';'));
}
payloadString = payloadString.trim();
EncodingEnum payloadType = EncodingEnum.forContentType(payloadString);
payloadType = ObjectUtils.defaultIfNull(payloadType, EncodingEnum.XML);
// Create the client request
getContext().getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
IGenericClient client = getContext().newRestfulGenericClient(endpointUrl);
// Additional headers specified in the subscription
List<IPrimitiveType> headers = getContext().newTerser().getValues(subscription, BaseSubscriptionInterceptor.SUBSCRIPTION_HEADER, IPrimitiveType.class);
for (IPrimitiveType next : headers) {
if (isNotBlank(next.getValueAsString())) {
client.registerInterceptor(new SimpleRequestHeaderInterceptor(next.getValueAsString()));
if (!subscriptionTypeApplies(getContext(), msg.getSubscription())) {
return;
}
IBaseResource subscription = msg.getSubscription();
// Grab the endpoint from the subscription
IPrimitiveType<?> endpoint = getContext().newTerser().getSingleValueOrNull(subscription, BaseSubscriptionInterceptor.SUBSCRIPTION_ENDPOINT, IPrimitiveType.class);
String endpointUrl = endpoint != null ? endpoint.getValueAsString() : null;
// Grab the payload type (encoding mimetype) from the subscription
IPrimitiveType<?> payload = getContext().newTerser().getSingleValueOrNull(subscription, BaseSubscriptionInterceptor.SUBSCRIPTION_PAYLOAD, IPrimitiveType.class);
String payloadString = payload != null ? payload.getValueAsString() : null;
payloadString = StringUtils.defaultString(payloadString, Constants.CT_FHIR_XML_NEW);
if (payloadString.contains(";")) {
payloadString = payloadString.substring(0, payloadString.indexOf(';'));
}
payloadString = payloadString.trim();
EncodingEnum payloadType = EncodingEnum.forContentType(payloadString);
payloadType = ObjectUtils.defaultIfNull(payloadType, EncodingEnum.XML);
// Create the client request
getContext().getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
IGenericClient client = null;
if (isNotBlank(endpointUrl)) {
client = getContext().newRestfulGenericClient(endpointUrl);
// Additional headers specified in the subscription
List<IPrimitiveType> headers = getContext().newTerser().getValues(subscription, BaseSubscriptionInterceptor.SUBSCRIPTION_HEADER, IPrimitiveType.class);
for (IPrimitiveType next : headers) {
if (isNotBlank(next.getValueAsString())) {
client.registerInterceptor(new SimpleRequestHeaderInterceptor(next.getValueAsString()));
}
}
}
deliverPayload(msg, subscription, payloadType, client);
} catch (Exception e) {
ourLog.error("Failure handling subscription payload", e);
throw new MessagingException(theMessage, "Failure handling subscription payload", e);
}
deliverPayload(msg, subscription, payloadType, client);
}
}

View File

@ -254,6 +254,11 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvc implements I
return retVal;
}
@Override
public List<IBaseResource> fetchAllConformanceResources(FhirContext theContext) {
return null;
}
@Override
public List<VersionIndependentConcept> expandValueSet(String theValueSet) {
ValueSet source = new ValueSet();

View File

@ -1,5 +1,43 @@
package ca.uhn.fhir.jpa.term;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.DaoMethodOutcome;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.entity.TermCodeSystem;
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.jpa.util.StopWatch;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.util.CoverageIgnore;
import ca.uhn.fhir.util.UrlUtil;
import org.apache.lucene.search.Query;
import org.hibernate.search.jpa.FullTextEntityManager;
import org.hibernate.search.jpa.FullTextQuery;
import org.hibernate.search.query.dsl.BooleanJunction;
import org.hibernate.search.query.dsl.QueryBuilder;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.hapi.ctx.HapiWorkerContext;
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.CodeSystem.CodeSystemContentMode;
import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r4.model.StructureDefinition;
import org.hl7.fhir.r4.model.ValueSet;
import org.hl7.fhir.r4.model.ValueSet.*;
import org.hl7.fhir.r4.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
@ -22,36 +60,6 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
* limitations under the License.
* #L%
*/
import java.util.*;
import org.apache.lucene.search.Query;
import org.hibernate.search.jpa.FullTextEntityManager;
import org.hibernate.search.jpa.FullTextQuery;
import org.hibernate.search.query.dsl.BooleanJunction;
import org.hibernate.search.query.dsl.QueryBuilder;
import org.hl7.fhir.r4.hapi.ctx.HapiWorkerContext;
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.CodeSystem.CodeSystemContentMode;
import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r4.model.ValueSet.*;
import org.hl7.fhir.r4.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.*;
import ca.uhn.fhir.jpa.entity.*;
import ca.uhn.fhir.jpa.util.StopWatch;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.*;
import ca.uhn.fhir.util.CoverageIgnore;
import ca.uhn.fhir.util.UrlUtil;
public class HapiTerminologySvcR4 extends BaseHapiTerminologySvc implements IValidationSupport, IHapiTerminologySvcR4 {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(HapiTerminologySvcR4.class);
@ -63,6 +71,15 @@ public class HapiTerminologySvcR4 extends BaseHapiTerminologySvc implements IVal
@Autowired
private IValidationSupport myValidationSupport;
private void addAllChildren(String theSystemString, ConceptDefinitionComponent theCode, List<VersionIndependentConcept> theListToPopulate) {
if (isNotBlank(theCode.getCode())) {
theListToPopulate.add(new VersionIndependentConcept(theSystemString, theCode.getCode()));
}
for (ConceptDefinitionComponent nextChild : theCode.getConcept()) {
addAllChildren(theSystemString, nextChild, theListToPopulate);
}
}
private void addCodeIfNotAlreadyAdded(String system, ValueSetExpansionComponent retVal, Set<String> addedCodes, TermConcept nextConcept) {
if (addedCodes.add(nextConcept.getCode())) {
ValueSetExpansionContainsComponent contains = retVal.addContains();
@ -72,36 +89,20 @@ public class HapiTerminologySvcR4 extends BaseHapiTerminologySvc implements IVal
}
}
@Override
protected List<VersionIndependentConcept> findCodesBelowUsingBuiltInSystems(String theSystem, String theCode) {
ArrayList<VersionIndependentConcept> retVal = new ArrayList<VersionIndependentConcept>();
CodeSystem system = myValidationSupport.fetchCodeSystem(myContext, theSystem);
if (system != null) {
findCodesBelow(system, theSystem, theCode, retVal);
}
return retVal;
private void addDisplayFilterExact(QueryBuilder qb, BooleanJunction<?> bool, ConceptSetFilterComponent nextFilter) {
bool.must(qb.phrase().onField("myDisplay").sentence(nextFilter.getValue()).createQuery());
}
private void findCodesBelow(CodeSystem theSystem, String theSystemString, String theCode, List<VersionIndependentConcept> theListToPopulate) {
List<ConceptDefinitionComponent> conceptList = theSystem.getConcept();
findCodesBelow(theSystemString, theCode, theListToPopulate, conceptList);
}
private void findCodesBelow(String theSystemString, String theCode, List<VersionIndependentConcept> theListToPopulate, List<ConceptDefinitionComponent> conceptList) {
for (ConceptDefinitionComponent next : conceptList) {
if (theCode.equals(next.getCode())) {
addAllChildren(theSystemString, next, theListToPopulate);
} else {
findCodesBelow(theSystemString, theCode, theListToPopulate, next.getConcept());
}
}
}
private void findCodesAbove(CodeSystem theSystem, String theSystemString, String theCode, List<VersionIndependentConcept> theListToPopulate) {
List<ConceptDefinitionComponent> conceptList = theSystem.getConcept();
for (ConceptDefinitionComponent next : conceptList) {
addTreeIfItContainsCode(theSystemString, next, theCode, theListToPopulate);
}
private void addDisplayFilterInexact(QueryBuilder qb, BooleanJunction<?> bool, ConceptSetFilterComponent nextFilter) {
Query textQuery = qb
.phrase()
.withSlop(2)
.onField("myDisplay").boostedTo(4.0f)
.andField("myDisplayEdgeNGram").boostedTo(2.0f)
// .andField("myDisplayNGram").boostedTo(1.0f)
// .andField("myDisplayPhonetic").boostedTo(0.5f)
.sentence(nextFilter.getValue().toLowerCase()).createQuery();
bool.must(textQuery);
}
private boolean addTreeIfItContainsCode(String theSystemString, ConceptDefinitionComponent theNext, String theCode, List<VersionIndependentConcept> theListToPopulate) {
@ -118,41 +119,6 @@ public class HapiTerminologySvcR4 extends BaseHapiTerminologySvc implements IVal
return false;
}
private void addAllChildren(String theSystemString, ConceptDefinitionComponent theCode, List<VersionIndependentConcept> theListToPopulate) {
if (isNotBlank(theCode.getCode())) {
theListToPopulate.add(new VersionIndependentConcept(theSystemString, theCode.getCode()));
}
for (ConceptDefinitionComponent nextChild : theCode.getConcept()) {
addAllChildren(theSystemString, nextChild, theListToPopulate);
}
}
@Override
protected List<VersionIndependentConcept> findCodesAboveUsingBuiltInSystems(String theSystem, String theCode) {
ArrayList<VersionIndependentConcept> retVal = new ArrayList<VersionIndependentConcept>();
CodeSystem system = myValidationSupport.fetchCodeSystem(myContext, theSystem);
if (system != null) {
findCodesAbove(system, theSystem, theCode, retVal);
}
return retVal;
}
private void addDisplayFilterExact(QueryBuilder qb, BooleanJunction<?> bool, ConceptSetFilterComponent nextFilter) {
bool.must(qb.phrase().onField("myDisplay").sentence(nextFilter.getValue()).createQuery());
}
private void addDisplayFilterInexact(QueryBuilder qb, BooleanJunction<?> bool, ConceptSetFilterComponent nextFilter) {
Query textQuery = qb
.phrase()
.withSlop(2)
.onField("myDisplay").boostedTo(4.0f)
.andField("myDisplayEdgeNGram").boostedTo(2.0f)
// .andField("myDisplayNGram").boostedTo(1.0f)
// .andField("myDisplayPhonetic").boostedTo(0.5f)
.sentence(nextFilter.getValue().toLowerCase()).createQuery();
bool.must(textQuery);
}
@Override
public ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) {
String system = theInclude.getSystem();
@ -278,6 +244,11 @@ public class HapiTerminologySvcR4 extends BaseHapiTerminologySvc implements IVal
}
@Override
public List<IBaseResource> fetchAllConformanceResources(FhirContext theContext) {
return null;
}
@Override
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
return Collections.emptyList();
@ -300,6 +271,48 @@ public class HapiTerminologySvcR4 extends BaseHapiTerminologySvc implements IVal
return null;
}
private void findCodesAbove(CodeSystem theSystem, String theSystemString, String theCode, List<VersionIndependentConcept> theListToPopulate) {
List<ConceptDefinitionComponent> conceptList = theSystem.getConcept();
for (ConceptDefinitionComponent next : conceptList) {
addTreeIfItContainsCode(theSystemString, next, theCode, theListToPopulate);
}
}
@Override
protected List<VersionIndependentConcept> findCodesAboveUsingBuiltInSystems(String theSystem, String theCode) {
ArrayList<VersionIndependentConcept> retVal = new ArrayList<VersionIndependentConcept>();
CodeSystem system = myValidationSupport.fetchCodeSystem(myContext, theSystem);
if (system != null) {
findCodesAbove(system, theSystem, theCode, retVal);
}
return retVal;
}
private void findCodesBelow(CodeSystem theSystem, String theSystemString, String theCode, List<VersionIndependentConcept> theListToPopulate) {
List<ConceptDefinitionComponent> conceptList = theSystem.getConcept();
findCodesBelow(theSystemString, theCode, theListToPopulate, conceptList);
}
private void findCodesBelow(String theSystemString, String theCode, List<VersionIndependentConcept> theListToPopulate, List<ConceptDefinitionComponent> conceptList) {
for (ConceptDefinitionComponent next : conceptList) {
if (theCode.equals(next.getCode())) {
addAllChildren(theSystemString, next, theListToPopulate);
} else {
findCodesBelow(theSystemString, theCode, theListToPopulate, next.getConcept());
}
}
}
@Override
protected List<VersionIndependentConcept> findCodesBelowUsingBuiltInSystems(String theSystem, String theCode) {
ArrayList<VersionIndependentConcept> retVal = new ArrayList<VersionIndependentConcept>();
CodeSystem system = myValidationSupport.fetchCodeSystem(myContext, theSystem);
if (system != null) {
findCodesBelow(system, theSystem, theCode, retVal);
}
return retVal;
}
@Override
public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) {
return super.supportsSystem(theSystem);

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.jpa.util;
import org.apache.commons.lang3.time.DateUtils;
import java.util.Date;
/*
@ -72,9 +74,15 @@ public class StopWatch {
static public String formatMillis(long val) {
StringBuilder buf = new StringBuilder(20);
append(buf, "", 2, ((val % 3600000) / 60000));
append(buf, ":", 2, ((val % 60000) / 1000));
append(buf, ".", 3, (val % 1000));
if (val >= DateUtils.MILLIS_PER_DAY) {
append(buf, "", 1, ((val / DateUtils.MILLIS_PER_DAY)));
append(buf, "d", 2, ((val % DateUtils.MILLIS_PER_DAY) / DateUtils.MILLIS_PER_HOUR));
} else {
append(buf, "", 2, ((val % DateUtils.MILLIS_PER_DAY) / DateUtils.MILLIS_PER_HOUR));
}
append(buf, ":", 2, ((val % DateUtils.MILLIS_PER_HOUR) / DateUtils.MILLIS_PER_MINUTE));
append(buf, ":", 2, ((val % DateUtils.MILLIS_PER_MINUTE) / DateUtils.MILLIS_PER_SECOND));
append(buf, ".", 3, (val % DateUtils.MILLIS_PER_SECOND));
return buf.toString();
}

View File

@ -57,6 +57,9 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
private static JpaValidationSupportChainDstu3 ourJpaValidationSupportChainDstu3;
private static IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> ourValueSetDao;
@Autowired
@Qualifier("myCoverageDaoDstu3")
protected IFhirResourceDao<Coverage> myCoverageDao;
@Autowired
protected IResourceIndexedCompositeStringUniqueDao myResourceIndexedCompositeStringUniqueDao;
@Autowired

View File

@ -9,6 +9,7 @@ import ca.uhn.fhir.jpa.util.JpaConstants;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.DateParam;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.util.TestUtil;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus;
@ -36,6 +37,185 @@ public class FhirResourceDaoDstu3UniqueSearchParamTest extends BaseJpaDstu3Test
myDaoConfig.setDefaultSearchParamsCanBeOverridden(new DaoConfig().isDefaultSearchParamsCanBeOverridden());
}
@Test
public void testIndexTransactionWithMatchUrl() {
Patient pt2 = new Patient();
pt2.setGender(Enumerations.AdministrativeGender.MALE);
pt2.setBirthDateElement(new DateType("2011-01-02"));
IIdType id2 = myPatientDao.create(pt2).getId().toUnqualifiedVersionless();
Coverage cov = new Coverage();
cov.getBeneficiary().setReference(id2.getValue());
cov.addIdentifier().setSystem("urn:foo:bar").setValue("123");
IIdType id3 = myCoverageDao.create(cov).getId().toUnqualifiedVersionless();
createUniqueIndexCoverageBeneficiary();
mySystemDao.markAllResourcesForReindexing();
mySystemDao.performReindexingPass(1000);
List<ResourceIndexedCompositeStringUnique> uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
assertEquals(uniques.toString(), 1, uniques.size());
assertEquals("Coverage/" + id3.getIdPart(), uniques.get(0).getResource().getIdDt().toUnqualifiedVersionless().getValue());
assertEquals("Coverage?beneficiary=Patient%2F" + id2.getIdPart() + "&identifier=urn%3Afoo%3Abar%7C123", uniques.get(0).getIndexString());
}
@Test
public void testIndexTransactionWithMatchUrl2() {
createUniqueIndexCoverageBeneficiary();
String input = "{\n" +
" \"resourceType\": \"Bundle\",\n" +
" \"type\": \"transaction\",\n" +
" \"entry\": [\n" +
" {\n" +
" \"fullUrl\": \"urn:uuid:d2a46176-8e15-405d-bbda-baea1a9dc7f3\",\n" +
" \"resource\": {\n" +
" \"resourceType\": \"Patient\",\n" +
" \"identifier\": [\n" +
" {\n" +
" \"use\": \"official\",\n" +
" \"type\": {\n" +
" \"coding\": [\n" +
" {\n" +
" \"system\": \"http://hl7.org/fhir/v2/0203\",\n" +
" \"code\": \"MR\"\n" +
" }\n" +
" ]\n" +
" },\n" +
" \"system\": \"FOOORG:FOOSITE:patientid:MR:R\",\n" +
" \"value\": \"007811959\"\n" +
" }\n" +
" ]\n" +
" },\n" +
" \"request\": {\n" +
" \"method\": \"PUT\",\n" +
" \"url\": \"/Patient?identifier=FOOORG%3AFOOSITE%3Apatientid%3AMR%3AR%7C007811959%2CFOOORG%3AFOOSITE%3Apatientid%3AMR%3AB%7C000929990%2CFOOORG%3AFOOSITE%3Apatientid%3API%3APH%7C00589363%2Chttp%3A%2F%2Fhl7.org%2Ffhir%2Fsid%2Fus-ssn%7C657-01-8133\"\n" +
" }\n" +
" },\n" +
" {\n" +
" \"fullUrl\": \"urn:uuid:b58ff639-11d1-4dac-942f-abf4f9a625d7\",\n" +
" \"resource\": {\n" +
" \"resourceType\": \"Coverage\",\n" +
" \"identifier\": [\n" +
" {\n" +
" \"system\": \"FOOORG:FOOSITE:coverage:planId\",\n" +
" \"value\": \"0403-010101\"\n" +
" }\n" +
" ],\n" +
" \"beneficiary\": {\n" +
" \"reference\": \"urn:uuid:d2a46176-8e15-405d-bbda-baea1a9dc7f3\"\n" +
" }\n" +
" },\n" +
" \"request\": {\n" +
" \"method\": \"PUT\",\n" +
" \"url\": \"/Coverage?beneficiary=urn%3Auuid%3Ad2a46176-8e15-405d-bbda-baea1a9dc7f3&identifier=FOOORG%3AFOOSITE%3Acoverage%3AplanId%7C0403-010101\"\n" +
" }\n" +
" },\n" +
" {\n" +
" \"fullUrl\": \"urn:uuid:13f5da1a-6601-4c1a-82c9-41527be23fa0\",\n" +
" \"resource\": {\n" +
" \"resourceType\": \"Coverage\",\n" +
" \"contained\": [\n" +
" {\n" +
" \"resourceType\": \"RelatedPerson\",\n" +
" \"id\": \"1\",\n" +
" \"name\": [\n" +
" {\n" +
" \"family\": \"SMITH\",\n" +
" \"given\": [\n" +
" \"FAKER\"\n" +
" ]\n" +
" }\n" +
" ]\n" +
" },\n" +
" {\n" +
" \"resourceType\": \"Organization\",\n" +
" \"id\": \"2\",\n" +
" \"name\": \"MEDICAID\"\n" +
" }\n" +
" ],\n" +
" \"identifier\": [\n" +
" {\n" +
" \"system\": \"FOOORG:FOOSITE:coverage:planId\",\n" +
" \"value\": \"0404-010101\"\n" +
" }\n" +
" ],\n" +
" \"policyHolder\": {\n" +
" \"reference\": \"#1\"\n" +
" },\n" +
" \"beneficiary\": {\n" +
" \"reference\": \"urn:uuid:d2a46176-8e15-405d-bbda-baea1a9dc7f3\"\n" +
" },\n" +
" \"payor\": [\n" +
" {\n" +
" \"reference\": \"#2\"\n" +
" }\n" +
" ]\n" +
" },\n" +
" \"request\": {\n" +
" \"method\": \"PUT\",\n" +
" \"url\": \"/Coverage?beneficiary=urn%3Auuid%3Ad2a46176-8e15-405d-bbda-baea1a9dc7f3&identifier=FOOORG%3AFOOSITE%3Acoverage%3AplanId%7C0404-010101\"\n" +
" }\n" +
" }\n" +
" ]\n" +
"}";
Bundle inputBundle = myFhirCtx.newJsonParser().parseResource(Bundle.class, input);
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inputBundle));
mySystemDao.transaction(mySrd, inputBundle);
inputBundle = myFhirCtx.newJsonParser().parseResource(Bundle.class, input);
mySystemDao.transaction(mySrd, inputBundle);
}
private void createUniqueIndexCoverageBeneficiary() {
SearchParameter sp = new SearchParameter();
sp.setId("SearchParameter/coverage-beneficiary");
sp.setCode("beneficiary");
sp.setExpression("Coverage.beneficiary");
sp.setType(Enumerations.SearchParamType.REFERENCE);
sp.setStatus(PublicationStatus.ACTIVE);
sp.addBase("Coverage");
mySearchParameterDao.update(sp);
sp = new SearchParameter();
sp.setId("SearchParameter/coverage-identifier");
sp.setCode("identifier");
sp.setExpression("Coverage.identifier");
sp.setType(Enumerations.SearchParamType.TOKEN);
sp.setStatus(PublicationStatus.ACTIVE);
sp.addBase("Coverage");
mySearchParameterDao.update(sp);
sp = new SearchParameter();
sp.setId("SearchParameter/coverage-beneficiary-identifier");
sp.setCode("coverage-beneficiary-identifier");
sp.setExpression("Coverage.beneficiary");
sp.setType(Enumerations.SearchParamType.COMPOSITE);
sp.setStatus(PublicationStatus.ACTIVE);
sp.addBase("Coverage");
sp.addComponent()
.setExpression("Coverage")
.setDefinition(new Reference("/SearchParameter/coverage-beneficiary"));
sp.addComponent()
.setExpression("Coverage")
.setDefinition(new Reference("/SearchParameter/coverage-identifier"));
sp.addExtension()
.setUrl(JpaConstants.EXT_SP_UNIQUE)
.setValue(new BooleanType(true));
mySearchParameterDao.update(sp);
mySearchParamRegsitry.forceRefresh();
}
@Before
public void before() {
myDaoConfig.setDefaultSearchParamsCanBeOverridden(true);
@ -143,7 +323,7 @@ public class FhirResourceDaoDstu3UniqueSearchParamTest extends BaseJpaDstu3Test
try {
myPatientDao.create(pt1).getId().toUnqualifiedVersionless();
fail();
} catch (JpaSystemException e) {
} catch (PreconditionFailedException e) {
// good
}

View File

@ -146,6 +146,9 @@ public abstract class BaseJpaR4Test extends BaseJpaTest {
@Qualifier("myPatientDaoR4")
protected IFhirResourceDaoPatient<Patient> myPatientDao;
@Autowired
@Qualifier("myCoverageDaoR4")
protected IFhirResourceDao<Coverage> myCoverageDao;
@Autowired
@Qualifier("myPractitionerDaoR4")
protected IFhirResourceDao<Practitioner> myPractitionerDao;
@Autowired

View File

@ -83,12 +83,32 @@ public class FhirResourceDaoCreatePlaceholdersR4Test extends BaseJpaR4Test {
Observation o = new Observation();
o.setStatus(ObservationStatus.FINAL);
o.getSubject().setReference("Patient/FOO");
try {
myObservationDao.create(o, mySrd);
fail();
} catch (InvalidRequestException e) {
assertEquals("Resource Patient/FOO not found, specified in path: Observation.subject", e.getMessage());
}
myObservationDao.create(o, mySrd);
}
@Test
public void testCreateWithMultiplePlaceholders() {
myDaoConfig.setAutoCreatePlaceholderReferenceTargets(true);
Task task = new Task();
task.addNote().setText("A note");
task.addPartOf().setReference("Task/AAA");
task.addPartOf().setReference("Task/AAA");
task.addPartOf().setReference("Task/AAA");
IIdType id = myTaskDao.create(task).getId().toUnqualifiedVersionless();
task = myTaskDao.read(id);
assertEquals(3, task.getPartOf().size());
assertEquals("Task/AAA", task.getPartOf().get(0).getReference());
assertEquals("Task/AAA", task.getPartOf().get(1).getReference());
assertEquals("Task/AAA", task.getPartOf().get(2).getReference());
SearchParameterMap params = new SearchParameterMap();
params.add(Task.SP_PART_OF, new ReferenceParam("Task/AAA"));
List<String> found = toUnqualifiedVersionlessIdValues(myTaskDao.search(params));
assertThat(found, contains(id.getValue()));
}
@Test
@ -123,12 +143,7 @@ public class FhirResourceDaoCreatePlaceholdersR4Test extends BaseJpaR4Test {
o.setId(id);
o.setStatus(ObservationStatus.FINAL);
o.getSubject().setReference("Patient/FOO");
try {
myObservationDao.update(o, mySrd);
fail();
} catch (InvalidRequestException e) {
assertEquals("Resource Patient/FOO not found, specified in path: Observation.subject", e.getMessage());
}
myObservationDao.update(o, mySrd);
}
@AfterClass

View File

@ -22,6 +22,7 @@ import org.springframework.orm.jpa.JpaSystemException;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.empty;
@ -35,6 +36,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
@After
public void after() {
myDaoConfig.setDefaultSearchParamsCanBeOverridden(new DaoConfig().isDefaultSearchParamsCanBeOverridden());
myDaoConfig.setUniqueIndexesCheckedBeforeSave(new DaoConfig().isUniqueIndexesCheckedBeforeSave());
}
@Before
@ -118,6 +120,58 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
mySearchParamRegsitry.forceRefresh();
}
private void createUniqueObservationSubjectDateCode() {
SearchParameter sp = new SearchParameter();
sp.setId("SearchParameter/obs-subject");
sp.setType(Enumerations.SearchParamType.REFERENCE);
sp.setCode("subject");
sp.setExpression("Observation.subject");
sp.setStatus(PublicationStatus.ACTIVE);
sp.addBase("Observation");
sp.addTarget("Patient");
mySearchParameterDao.update(sp);
sp = new SearchParameter();
sp.setId("SearchParameter/obs-effective");
sp.setType(Enumerations.SearchParamType.DATE);
sp.setCode("date");
sp.setExpression("Observation.effective");
sp.setStatus(PublicationStatus.ACTIVE);
sp.addBase("Observation");
mySearchParameterDao.update(sp);
sp = new SearchParameter();
sp.setId("SearchParameter/obs-code");
sp.setType(Enumerations.SearchParamType.TOKEN);
sp.setCode("code");
sp.setExpression("Observation.code");
sp.setStatus(PublicationStatus.ACTIVE);
sp.addBase("Observation");
mySearchParameterDao.update(sp);
sp = new SearchParameter();
sp.setId("SearchParameter/observation-subject-date-code");
sp.setType(Enumerations.SearchParamType.COMPOSITE);
sp.setStatus(PublicationStatus.ACTIVE);
sp.addBase("Observation");
sp.setExpression("Observation.code");
sp.addComponent()
.setExpression("Observation")
.setDefinition(new Reference("SearchParameter/obs-subject"));
sp.addComponent()
.setExpression("Observation")
.setDefinition(new Reference("SearchParameter/obs-effective"));
sp.addComponent()
.setExpression("Observation")
.setDefinition(new Reference("SearchParameter/obs-code"));
sp.addExtension()
.setUrl(JpaConstants.EXT_SP_UNIQUE)
.setValue(new BooleanType(true));
mySearchParameterDao.update(sp);
mySearchParamRegsitry.forceRefresh();
}
@Test
public void testDetectUniqueSearchParams() {
createUniqueBirthdateAndGenderSps();
@ -131,9 +185,61 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
assertEquals("gender", params.get(0).getCompositeOf().get(1).getName());
}
@Test
public void testDuplicateUniqueValuesAreReIndexed() {
Patient pt1 = new Patient();
pt1.setActive(true);
IIdType id1 = myPatientDao.create(pt1).getId().toUnqualifiedVersionless();
/*
* Both of the following resources will match the unique index we'll
* create afterward. So we can create them both, but then when we create
* the unique index that matches them both that's a problem...
*/
Observation obs = new Observation();
obs.getCode().addCoding().setSystem("foo").setCode("bar");
obs.setSubject(new Reference(pt1.getIdElement().toUnqualifiedVersionless().getValue()));
obs.setEffective(new DateTimeType("2011-01-01"));
IIdType id2 = myObservationDao.create(obs).getId().toUnqualifiedVersionless();
obs = new Observation();
obs.getCode().addCoding().setSystem("foo").setCode("bar");
obs.setSubject(new Reference(pt1.getIdElement().toUnqualifiedVersionless().getValue()));
obs.setEffective(new DateTimeType("2011-01-01"));
IIdType id3 = myObservationDao.create(obs).getId().toUnqualifiedVersionless();
ourLog.info("ID1: {} - ID2: {} - ID3: {}", id1, id2, id3);
createUniqueObservationSubjectDateCode();
mySystemDao.markAllResourcesForReindexing();
mySystemDao.performReindexingPass(1000);
mySystemDao.performReindexingPass(1000);
List<ResourceIndexedCompositeStringUnique> uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
assertEquals(uniques.toString(), 1, uniques.size());
assertEquals("Observation/" + id2.getIdPart(), uniques.get(0).getResource().getIdDt().toUnqualifiedVersionless().getValue());
assertEquals("Observation?code=foo%7Cbar&date=2011-01-01&subject=Patient%2F" + id1.getIdPart(), uniques.get(0).getIndexString());
myResourceIndexedCompositeStringUniqueDao.deleteAll();
mySystemDao.markAllResourcesForReindexing();
mySystemDao.performReindexingPass(1000);
mySystemDao.performReindexingPass(1000);
uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
assertEquals(uniques.toString(), 1, uniques.size());
assertEquals("Observation/" + id2.getIdPart(), uniques.get(0).getResource().getIdDt().toUnqualifiedVersionless().getValue());
assertEquals("Observation?code=foo%7Cbar&date=2011-01-01&subject=Patient%2F" + id1.getIdPart(), uniques.get(0).getIndexString());
}
@Test
public void testDuplicateUniqueValuesAreRejected() {
public void testDuplicateUniqueValuesAreRejectedWithChecking_TestingDisabled() {
myDaoConfig.setUniqueIndexesCheckedBeforeSave(false);
createUniqueBirthdateAndGenderSps();
Patient pt1 = new Patient();
@ -147,26 +253,10 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
} catch (JpaSystemException e) {
// good
}
Patient pt2 = new Patient();
pt2.setGender(Enumerations.AdministrativeGender.MALE);
IIdType id2 = myPatientDao.create(pt2).getId().toUnqualifiedVersionless();
pt2 = new Patient();
pt2.setId(id2);
pt2.setGender(Enumerations.AdministrativeGender.MALE);
pt2.setBirthDateElement(new DateType("2011-01-01"));
try {
myPatientDao.update(pt2);
fail();
} catch (JpaSystemException e) {
// good
}
}
@Test
public void testUniqueValuesAreIndexed_DateAndToken() {
public void testDuplicateUniqueValuesAreRejectedWithChecking_TestingEnabled() {
createUniqueBirthdateAndGenderSps();
Patient pt1 = new Patient();
@ -174,10 +264,12 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
pt1.setBirthDateElement(new DateType("2011-01-01"));
IIdType id1 = myPatientDao.create(pt1).getId().toUnqualifiedVersionless();
List<ResourceIndexedCompositeStringUnique> uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
assertEquals(1, uniques.size());
assertEquals("Patient/" + id1.getIdPart(), uniques.get(0).getResource().getIdDt().toUnqualifiedVersionless().getValue());
assertEquals("Patient?birthdate=2011-01-01&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale", uniques.get(0).getIndexString());
try {
myPatientDao.create(pt1).getId().toUnqualifiedVersionless();
fail();
} catch (PreconditionFailedException e) {
assertEquals("Can not create resource of type Patient as it would create a duplicate index matching query: Patient?birthdate=2011-01-01&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale (existing index belongs to Patient/" + id1.getIdPart() + ")", e.getMessage());
}
}
@Test
@ -204,7 +296,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
assertEquals(SearchBuilder.HandlerTypeEnum.UNIQUE_INDEX, SearchBuilder.getLastHandlerMechanismForUnitTest());
}
@Test
@Test
public void testSearchUsingUniqueComposite() {
createUniqueBirthdateAndGenderSps();
@ -255,6 +347,227 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
}
private void createUniqueIndexCoverageBeneficiary() {
SearchParameter sp = new SearchParameter();
sp.setId("SearchParameter/coverage-beneficiary");
sp.setCode("beneficiary");
sp.setExpression("Coverage.beneficiary");
sp.setType(Enumerations.SearchParamType.REFERENCE);
sp.setStatus(PublicationStatus.ACTIVE);
sp.addBase("Coverage");
mySearchParameterDao.update(sp);
sp = new SearchParameter();
sp.setId("SearchParameter/coverage-identifier");
sp.setCode("identifier");
sp.setExpression("Coverage.identifier");
sp.setType(Enumerations.SearchParamType.TOKEN);
sp.setStatus(PublicationStatus.ACTIVE);
sp.addBase("Coverage");
mySearchParameterDao.update(sp);
sp = new SearchParameter();
sp.setId("SearchParameter/coverage-beneficiary-identifier");
sp.setCode("coverage-beneficiary-identifier");
sp.setExpression("Coverage.beneficiary");
sp.setType(Enumerations.SearchParamType.COMPOSITE);
sp.setStatus(PublicationStatus.ACTIVE);
sp.addBase("Coverage");
sp.addComponent()
.setExpression("Coverage")
.setDefinition(new Reference("/SearchParameter/coverage-beneficiary"));
sp.addComponent()
.setExpression("Coverage")
.setDefinition(new Reference("/SearchParameter/coverage-identifier"));
sp.addExtension()
.setUrl(JpaConstants.EXT_SP_UNIQUE)
.setValue(new BooleanType(true));
mySearchParameterDao.update(sp);
mySearchParamRegsitry.forceRefresh();
}
@Test
public void testIndexTransactionWithMatchUrl() {
Patient pt2 = new Patient();
pt2.setGender(Enumerations.AdministrativeGender.MALE);
pt2.setBirthDateElement(new DateType("2011-01-02"));
IIdType id2 = myPatientDao.create(pt2).getId().toUnqualifiedVersionless();
Coverage cov = new Coverage();
cov.getBeneficiary().setReference(id2.getValue());
cov.addIdentifier().setSystem("urn:foo:bar").setValue("123");
IIdType id3 = myCoverageDao.create(cov).getId().toUnqualifiedVersionless();
createUniqueIndexCoverageBeneficiary();
mySystemDao.markAllResourcesForReindexing();
mySystemDao.performReindexingPass(1000);
List<ResourceIndexedCompositeStringUnique> uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
assertEquals(uniques.toString(), 1, uniques.size());
assertEquals("Coverage/" + id3.getIdPart(), uniques.get(0).getResource().getIdDt().toUnqualifiedVersionless().getValue());
assertEquals("Coverage?beneficiary=Patient%2F" + id2.getIdPart() + "&identifier=urn%3Afoo%3Abar%7C123", uniques.get(0).getIndexString());
}
@Test
public void testIndexTransactionWithMatchUrl2() {
createUniqueIndexCoverageBeneficiary();
String input = "{\n" +
" \"resourceType\": \"Bundle\",\n" +
" \"type\": \"transaction\",\n" +
" \"entry\": [\n" +
" {\n" +
" \"fullUrl\": \"urn:uuid:d2a46176-8e15-405d-bbda-baea1a9dc7f3\",\n" +
" \"resource\": {\n" +
" \"resourceType\": \"Patient\",\n" +
" \"identifier\": [\n" +
" {\n" +
" \"use\": \"official\",\n" +
" \"type\": {\n" +
" \"coding\": [\n" +
" {\n" +
" \"system\": \"http://hl7.org/fhir/v2/0203\",\n" +
" \"code\": \"MR\"\n" +
" }\n" +
" ]\n" +
" },\n" +
" \"system\": \"FOOORG:FOOSITE:patientid:MR:R\",\n" +
" \"value\": \"007811959\"\n" +
" }\n" +
" ]\n" +
" },\n" +
" \"request\": {\n" +
" \"method\": \"PUT\",\n" +
" \"url\": \"/Patient?identifier=FOOORG%3AFOOSITE%3Apatientid%3AMR%3AR%7C007811959%2CFOOORG%3AFOOSITE%3Apatientid%3AMR%3AB%7C000929990%2CFOOORG%3AFOOSITE%3Apatientid%3API%3APH%7C00589363%2Chttp%3A%2F%2Fhl7.org%2Ffhir%2Fsid%2Fus-ssn%7C657-01-8133\"\n" +
" }\n" +
" },\n" +
" {\n" +
" \"fullUrl\": \"urn:uuid:b58ff639-11d1-4dac-942f-abf4f9a625d7\",\n" +
" \"resource\": {\n" +
" \"resourceType\": \"Coverage\",\n" +
" \"identifier\": [\n" +
" {\n" +
" \"system\": \"FOOORG:FOOSITE:coverage:planId\",\n" +
" \"value\": \"0403-010101\"\n" +
" }\n" +
" ],\n" +
" \"beneficiary\": {\n" +
" \"reference\": \"urn:uuid:d2a46176-8e15-405d-bbda-baea1a9dc7f3\"\n" +
" }\n" +
" },\n" +
" \"request\": {\n" +
" \"method\": \"PUT\",\n" +
" \"url\": \"/Coverage?beneficiary=urn%3Auuid%3Ad2a46176-8e15-405d-bbda-baea1a9dc7f3&identifier=FOOORG%3AFOOSITE%3Acoverage%3AplanId%7C0403-010101\"\n" +
" }\n" +
" },\n" +
" {\n" +
" \"fullUrl\": \"urn:uuid:13f5da1a-6601-4c1a-82c9-41527be23fa0\",\n" +
" \"resource\": {\n" +
" \"resourceType\": \"Coverage\",\n" +
" \"contained\": [\n" +
" {\n" +
" \"resourceType\": \"RelatedPerson\",\n" +
" \"id\": \"1\",\n" +
" \"name\": [\n" +
" {\n" +
" \"family\": \"SMITH\",\n" +
" \"given\": [\n" +
" \"FAKER\"\n" +
" ]\n" +
" }\n" +
" ]\n" +
" },\n" +
" {\n" +
" \"resourceType\": \"Organization\",\n" +
" \"id\": \"2\",\n" +
" \"name\": \"MEDICAID\"\n" +
" }\n" +
" ],\n" +
" \"identifier\": [\n" +
" {\n" +
" \"system\": \"FOOORG:FOOSITE:coverage:planId\",\n" +
" \"value\": \"0404-010101\"\n" +
" }\n" +
" ],\n" +
" \"policyHolder\": {\n" +
" \"reference\": \"#1\"\n" +
" },\n" +
" \"beneficiary\": {\n" +
" \"reference\": \"urn:uuid:d2a46176-8e15-405d-bbda-baea1a9dc7f3\"\n" +
" },\n" +
" \"payor\": [\n" +
" {\n" +
" \"reference\": \"#2\"\n" +
" }\n" +
" ]\n" +
" },\n" +
" \"request\": {\n" +
" \"method\": \"PUT\",\n" +
" \"url\": \"/Coverage?beneficiary=urn%3Auuid%3Ad2a46176-8e15-405d-bbda-baea1a9dc7f3&identifier=FOOORG%3AFOOSITE%3Acoverage%3AplanId%7C0404-010101\"\n" +
" }\n" +
" }\n" +
" ]\n" +
"}";
Bundle inputBundle = myFhirCtx.newJsonParser().parseResource(Bundle.class, input);
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inputBundle));
mySystemDao.transaction(mySrd, inputBundle);
inputBundle = myFhirCtx.newJsonParser().parseResource(Bundle.class, input);
mySystemDao.transaction(mySrd, inputBundle);
}
@Test
public void testUniqueValuesAreIndexed_DateAndToken() {
createUniqueBirthdateAndGenderSps();
Patient pt1 = new Patient();
pt1.setGender(Enumerations.AdministrativeGender.MALE);
pt1.setBirthDateElement(new DateType("2011-01-01"));
IIdType id1 = myPatientDao.create(pt1).getId().toUnqualifiedVersionless();
List<ResourceIndexedCompositeStringUnique> uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
assertEquals(uniques.toString(), 1, uniques.size());
assertEquals("Patient/" + id1.getIdPart(), uniques.get(0).getResource().getIdDt().toUnqualifiedVersionless().getValue());
assertEquals("Patient?birthdate=2011-01-01&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale", uniques.get(0).getIndexString());
}
@Test
public void testUniqueValuesAreIndexed_RefAndDateAndToken() {
createUniqueObservationSubjectDateCode();
List<ResourceIndexedCompositeStringUnique> uniques;
uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
assertEquals(uniques.toString(), 0, uniques.size());
Patient pt1 = new Patient();
pt1.setActive(true);
IIdType id1 = myPatientDao.create(pt1).getId().toUnqualifiedVersionless();
Observation obs = new Observation();
obs.getCode().addCoding().setSystem("foo").setCode("bar");
obs.setSubject(new Reference(pt1.getIdElement().toUnqualifiedVersionless().getValue()));
obs.setEffective(new DateTimeType("2011-01-01"));
IIdType id2 = myObservationDao.create(obs).getId().toUnqualifiedVersionless();
Patient pt2 = new Patient();
pt2.setActive(false);
IIdType id3 = myPatientDao.create(pt1).getId().toUnqualifiedVersionless();
ourLog.info("ID1: {} - ID2: {} - ID3: {}", id1, id2, id3);
uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
assertEquals(uniques.toString(), 1, uniques.size());
assertEquals("Observation/" + id2.getIdPart(), uniques.get(0).getResource().getIdDt().toUnqualifiedVersionless().getValue());
assertEquals("Observation?code=foo%7Cbar&date=2011-01-01&subject=Patient%2F" + id1.getIdPart(), uniques.get(0).getIndexString());
}
@Test
public void testUniqueValuesAreIndexed_StringAndReference() {
createUniqueNameAndManagingOrganizationSps();
@ -287,6 +600,219 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
assertEquals("Patient?name=GIVEN2&organization=Organization%2FORG", uniques.get(2).getIndexString());
}
@Test
public void testUniqueValuesAreIndexed_StringAndReference_UsingConditional() {
createUniqueNameAndManagingOrganizationSps();
List<ResourceIndexedCompositeStringUnique> uniques;
Organization org = new Organization();
org.setId("Organization/ORG");
org.setName("ORG");
myOrganizationDao.update(org);
Patient pt1 = new Patient();
pt1.addName().setFamily("FAMILY1");
pt1.setManagingOrganization(new Reference("Organization/ORG"));
IIdType id1 = myPatientDao.update(pt1, "Patient?name=FAMILY1&organization.name=ORG").getId().toUnqualifiedVersionless();
uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
assertEquals(1, uniques.size());
assertEquals("Patient/" + id1.getIdPart(), uniques.get(0).getResource().getIdDt().toUnqualifiedVersionless().getValue());
assertEquals("Patient?name=FAMILY1&organization=Organization%2FORG", uniques.get(0).getIndexString());
// Again
pt1 = new Patient();
pt1.addName().setFamily("FAMILY1");
pt1.setManagingOrganization(new Reference("Organization/ORG"));
id1 = myPatientDao.update(pt1, "Patient?name=FAMILY1&organization.name=ORG").getId().toUnqualifiedVersionless();
uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
assertEquals(1, uniques.size());
assertEquals("Patient/" + id1.getIdPart(), uniques.get(0).getResource().getIdDt().toUnqualifiedVersionless().getValue());
assertEquals("Patient?name=FAMILY1&organization=Organization%2FORG", uniques.get(0).getIndexString());
}
@Test
public void testUniqueValuesAreIndexed_StringAndReference_UsingConditionalInTransaction() {
createUniqueNameAndManagingOrganizationSps();
List<ResourceIndexedCompositeStringUnique> uniques;
Organization org = new Organization();
org.setId("Organization/ORG");
org.setName("ORG");
myOrganizationDao.update(org);
Bundle bundle = new Bundle();
bundle.setType(Bundle.BundleType.TRANSACTION);
String orgId = "urn:uuid:" + UUID.randomUUID().toString();
org = new Organization();
org.setName("ORG");
bundle
.addEntry()
.setResource(org)
.setFullUrl(orgId)
.getRequest()
.setMethod(Bundle.HTTPVerb.PUT)
.setUrl("/Organization?name=ORG");
Patient pt1 = new Patient();
pt1.addName().setFamily("FAMILY1");
pt1.setManagingOrganization(new Reference(orgId));
bundle
.addEntry()
.setResource(pt1)
.getRequest()
.setMethod(Bundle.HTTPVerb.PUT)
.setUrl("/Patient?name=FAMILY1&organization=" + orgId.replace(":", "%3A"));
Bundle resp = mySystemDao.transaction(mySrd, bundle);
IIdType id1 = new IdType(resp.getEntry().get(1).getResponse().getLocation());
uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
assertEquals(1, uniques.size());
assertEquals("Patient/" + id1.getIdPart(), uniques.get(0).getResource().getIdDt().toUnqualifiedVersionless().getValue());
assertEquals("Patient?name=FAMILY1&organization=Organization%2FORG", uniques.get(0).getIndexString());
// Again
bundle = new Bundle();
bundle.setType(Bundle.BundleType.TRANSACTION);
orgId = IdType.newRandomUuid().getValue();
org = new Organization();
org.setName("ORG");
bundle
.addEntry()
.setResource(org)
.setFullUrl(orgId)
.getRequest()
.setMethod(Bundle.HTTPVerb.PUT)
.setUrl("/Organization?name=ORG");
pt1 = new Patient();
pt1.addName().setFamily("FAMILY1");
pt1.setManagingOrganization(new Reference(orgId));
bundle
.addEntry()
.setResource(pt1)
.getRequest()
.setMethod(Bundle.HTTPVerb.PUT)
.setUrl("/Patient?name=FAMILY1&organization=" + orgId);
resp = mySystemDao.transaction(mySrd, bundle);
id1 = new IdType(resp.getEntry().get(1).getResponse().getLocation());
uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
assertEquals(1, uniques.size());
assertEquals("Patient/" + id1.getIdPart(), uniques.get(0).getResource().getIdDt().toUnqualifiedVersionless().getValue());
assertEquals("Patient?name=FAMILY1&organization=Organization%2FORG", uniques.get(0).getIndexString());
}
@Test
public void testUniqueValuesAreNotIndexedIfNotAllParamsAreFound_DateAndToken() {
createUniqueBirthdateAndGenderSps();
Patient pt;
List<ResourceIndexedCompositeStringUnique> uniques;
pt = new Patient();
pt.setGender(Enumerations.AdministrativeGender.MALE);
myPatientDao.create(pt).getId().toUnqualifiedVersionless();
uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
assertEquals(uniques.toString(), 0, uniques.size());
pt = new Patient();
myPatientDao.create(pt).getId().toUnqualifiedVersionless();
uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
assertEquals(uniques.toString(), 0, uniques.size());
pt = new Patient();
pt.setBirthDateElement(new DateType());
pt.setGender(Enumerations.AdministrativeGender.MALE);
myPatientDao.create(pt).getId().toUnqualifiedVersionless();
uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
assertEquals(uniques.toString(), 0, uniques.size());
}
@Test
public void testUniqueValuesAreNotIndexedIfNotAllParamsAreFound_StringAndReference() {
createUniqueNameAndManagingOrganizationSps();
Organization org = new Organization();
org.setId("Organization/ORG");
org.setName("ORG");
myOrganizationDao.update(org);
List<ResourceIndexedCompositeStringUnique> uniques;
Patient pt;
pt = new Patient();
pt.setManagingOrganization(new Reference("Organization/ORG"));
myPatientDao.create(pt).getId().toUnqualifiedVersionless();
uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
assertEquals(uniques.toString(), 0, uniques.size());
pt = new Patient();
pt.addName()
.setFamily("FAMILY1")
.addGiven("GIVEN1")
.addGiven("GIVEN2")
.addGiven("GIVEN2"); // GIVEN2 happens twice
myPatientDao.create(pt).getId().toUnqualifiedVersionless();
uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
assertEquals(uniques.toString(), 0, uniques.size());
pt = new Patient();
pt.setActive(true);
myPatientDao.create(pt).getId().toUnqualifiedVersionless();
uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
assertEquals(uniques.toString(), 0, uniques.size());
}
@Test
public void testUniqueValuesAreReIndexed() {
createUniqueObservationSubjectDateCode();
Patient pt1 = new Patient();
pt1.setActive(true);
IIdType id1 = myPatientDao.create(pt1).getId().toUnqualifiedVersionless();
Observation obs = new Observation();
obs.getCode().addCoding().setSystem("foo").setCode("bar");
obs.setSubject(new Reference(pt1.getIdElement().toUnqualifiedVersionless().getValue()));
obs.setEffective(new DateTimeType("2011-01-01"));
IIdType id2 = myObservationDao.create(obs).getId().toUnqualifiedVersionless();
Patient pt2 = new Patient();
pt2.setActive(false);
myPatientDao.create(pt1).getId().toUnqualifiedVersionless();
mySystemDao.markAllResourcesForReindexing();
mySystemDao.performReindexingPass(1000);
List<ResourceIndexedCompositeStringUnique> uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
assertEquals(uniques.toString(), 1, uniques.size());
assertEquals("Observation/" + id2.getIdPart(), uniques.get(0).getResource().getIdDt().toUnqualifiedVersionless().getValue());
assertEquals("Observation?code=foo%7Cbar&date=2011-01-01&subject=Patient%2F" + id1.getIdPart(), uniques.get(0).getIndexString());
myResourceIndexedCompositeStringUniqueDao.deleteAll();
mySystemDao.markAllResourcesForReindexing();
mySystemDao.performReindexingPass(1000);
uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
assertEquals(uniques.toString(), 1, uniques.size());
assertEquals("Observation/" + id2.getIdPart(), uniques.get(0).getResource().getIdDt().toUnqualifiedVersionless().getValue());
assertEquals("Observation?code=foo%7Cbar&date=2011-01-01&subject=Patient%2F" + id1.getIdPart(), uniques.get(0).getIndexString());
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();

View File

@ -362,6 +362,17 @@ public class SystemProviderR4Test extends BaseJpaR4Test {
} finally {
IOUtils.closeQuietly(http);;
}
get = new HttpGet(ourServerBase + "/$perform-reindexing-pass");
http = ourHttpClient.execute(get);
try {
String output = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(output);
assertEquals(200, http.getStatusLine().getStatusCode());
} finally {
IOUtils.closeQuietly(http);;
}
}
@Transactional(propagation = Propagation.NEVER)

View File

@ -281,11 +281,11 @@ public class RestHookTestDstu2Test extends BaseResourceProviderDstu2Test {
}
public static void waitForQueueToDrain(BaseSubscriptionInterceptor theRestHookSubscriptionInterceptor) throws InterruptedException {
ourLog.info("QUEUE HAS {} ITEMS", theRestHookSubscriptionInterceptor.getExecutorQueueForUnitTests().size());
while (theRestHookSubscriptionInterceptor.getExecutorQueueForUnitTests().size() > 0) {
ourLog.info("QUEUE HAS {} ITEMS", theRestHookSubscriptionInterceptor.getExecutorQueueSizeForUnitTests());
while (theRestHookSubscriptionInterceptor.getExecutorQueueSizeForUnitTests() > 0) {
Thread.sleep(50);
}
ourLog.info("QUEUE HAS {} ITEMS", theRestHookSubscriptionInterceptor.getExecutorQueueForUnitTests().size());
ourLog.info("QUEUE HAS {} ITEMS", theRestHookSubscriptionInterceptor.getExecutorQueueSizeForUnitTests());
}
private void waitForQueueToDrain() throws InterruptedException {

View File

@ -84,11 +84,11 @@ public class RestHookTestWithInterceptorRegisteredToDaoConfigR4Test extends Base
}
private void waitForQueueToDrain() throws InterruptedException {
ourLog.info("QUEUE HAS {} ITEMS", getRestHookSubscriptionInterceptor().getExecutorQueueForUnitTests().size());
while (getRestHookSubscriptionInterceptor().getExecutorQueueForUnitTests().size() > 0) {
ourLog.info("QUEUE HAS {} ITEMS", getRestHookSubscriptionInterceptor().getExecutorQueueSizeForUnitTests());
while (getRestHookSubscriptionInterceptor().getExecutorQueueSizeForUnitTests() > 0) {
Thread.sleep(250);
}
ourLog.info("QUEUE HAS {} ITEMS", getRestHookSubscriptionInterceptor().getExecutorQueueForUnitTests().size());
ourLog.info("QUEUE HAS {} ITEMS", getRestHookSubscriptionInterceptor().getExecutorQueueSizeForUnitTests());
}
private Observation sendObservation(String code, String system) throws InterruptedException {

View File

@ -3,10 +3,12 @@ package ca.uhn.fhir.jpa.util;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.lessThan;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import java.util.Date;
import org.apache.commons.lang3.time.DateUtils;
import org.junit.Test;
public class StopWatchTest {
@ -46,4 +48,14 @@ public class StopWatchTest {
assertThat(string, startsWith("00:00"));
}
@Test
public void testFormatMillis() throws Exception {
assertEquals("00:00:01.000", StopWatch.formatMillis(DateUtils.MILLIS_PER_SECOND));
assertEquals("00:01:00.000", StopWatch.formatMillis(DateUtils.MILLIS_PER_MINUTE));
assertEquals("01:00:00.000", StopWatch.formatMillis(DateUtils.MILLIS_PER_HOUR));
assertEquals("1d00:00:00.000", StopWatch.formatMillis(DateUtils.MILLIS_PER_DAY));
assertEquals("2d00:00:00.000", StopWatch.formatMillis(DateUtils.MILLIS_PER_DAY*2));
assertEquals("2d00:00:00.001", StopWatch.formatMillis((DateUtils.MILLIS_PER_DAY*2)+1));
}
}

View File

@ -44,12 +44,34 @@ delete from hfj_forced_id where resource_pid = 16940;
delete from hfj_resource where res_id = 16940;
# Drop all tables
drop table hfj_history_tag cascade constraints;
drop table hfj_res_ver cascade constraints;
drop table hfj_forced_id cascade constraints;
drop table hfj_res_link cascade constraints;
drop table hfj_res_link cascade constraints;
drop table hfj_spidx_coords cascade constraints;
drop table hfj_spidx_date cascade constraints;
drop table hfj_spidx_number cascade constraints;
drop table hfj_spidx_quantity cascade constraints;
drop table hfj_spidx_string cascade constraints;
drop table hfj_spidx_token cascade constraints;
drop table hfj_spidx_uri cascade constraints;
drop table hfj_res_tag cascade constraints;
drop table hfj_search_result cascade constraints;
drop table hfj_res_param_present cascade constraints;
drop table hfj_resource cascade constraints;
drop table hfj_idx_cmp_string_uniq cascade constraints;
drop table hfj_search cascade constraints;
drop table hfj_search_include cascade constraints;
drop table hfj_search_parm cascade constraints;
drop table hfj_subscription cascade constraints;
drop table hfj_subscription_flag_res cascade constraints;
drop table hfj_tag_def cascade constraints;
drop table trm_codesystem cascade constraints;
drop table trm_codesystem_var cascade constraints;
drop table trm_concept cascade constraints;
drop table trm_concept_pc_link cascade constraints;
drop table trm_concept_property cascade constraints;
delete from hfj_res_link where src_resource_id in ( 156618, 1092 , 1114 , 1139 , 1140 , 1141, 1142 , 1143 , 1144 , 1146 , 1147, 1148, 1062912, 1062916, 1062918, 1062919, 1062922, 1062929, 1062930, 1062934, 1062939, 1062940, 1062944, 1062949, 1062955, 1062957, 1062958, 1062959, 1062966, 1062969, 1062975, 1062976, 1062979, 1062981, 1062985, 1062987, 1062992, 1063002, 1063005, 1063007, 1063013, 1063016, 1063018, 1063020, 1063022, 1063062, 1063068, 1063075, 1063078, 1063080, 1063083, 1063084, 1063091, 1063095, 1063096, 1063098, 1107579, 1107591, 1107598, 1107761, 1107705, 1107748, 1109361, 1109388, 1109378, 1109399, 1109400, 1109401, 1109403, 1109404, 1109406, 1109409, 1109411, 1109414, 1109417, 1109418, 1109421, 1109427, 1109428, 1109429, 1109431, 1109432, 1109433, 1109891, 1109893, 1109947, 1179553, 1182781, 1182788, 1182791, 1182792, 1182795, 1182798, 1182801, 1182806, 1182811, 1182815) or target_resource_id in ( 156618, 1092 , 1114 , 1139 , 1140 , 1141, 1142 , 1143 , 1144 , 1146 , 1147, 1148, 1062912, 1062916, 1062918, 1062919, 1062922, 1062929, 1062930, 1062934, 1062939, 1062940, 1062944, 1062949, 1062955, 1062957, 1062958, 1062959, 1062966, 1062969, 1062975, 1062976, 1062979, 1062981, 1062985, 1062987, 1062992, 1063002, 1063005, 1063007, 1063013, 1063016, 1063018, 1063020, 1063022, 1063062, 1063068, 1063075, 1063078, 1063080, 1063083, 1063084, 1063091, 1063095, 1063096, 1063098, 1107579, 1107591, 1107598, 1107761, 1107705, 1107748, 1109361, 1109388, 1109378, 1109399, 1109400, 1109401, 1109403, 1109404, 1109406, 1109409, 1109411, 1109414, 1109417, 1109418, 1109421, 1109427, 1109428, 1109429, 1109431, 1109432, 1109433, 1109891, 1109893, 1109947, 1179553, 1182781, 1182788, 1182791, 1182792, 1182795, 1182798, 1182801, 1182806, 1182811, 1182815);
delete from hfj_spidx_date where res_id in ( 156618, 1092 , 1114 , 1139 , 1140 , 1141, 1142 , 1143 , 1144 , 1146 , 1147, 1148, 1062912, 1062916, 1062918, 1062919, 1062922, 1062929, 1062930, 1062934, 1062939, 1062940, 1062944, 1062949, 1062955, 1062957, 1062958, 1062959, 1062966, 1062969, 1062975, 1062976, 1062979, 1062981, 1062985, 1062987, 1062992, 1063002, 1063005, 1063007, 1063013, 1063016, 1063018, 1063020, 1063022, 1063062, 1063068, 1063075, 1063078, 1063080, 1063083, 1063084, 1063091, 1063095, 1063096, 1063098, 1107579, 1107591, 1107598, 1107761, 1107705, 1107748, 1109361, 1109388, 1109378, 1109399, 1109400, 1109401, 1109403, 1109404, 1109406, 1109409, 1109411, 1109414, 1109417, 1109418, 1109421, 1109427, 1109428, 1109429, 1109431, 1109432, 1109433, 1109891, 1109893, 1109947, 1179553, 1182781, 1182788, 1182791, 1182792, 1182795, 1182798, 1182801, 1182806, 1182811, 1182815);
delete from hfj_spidx_string where res_id in ( 156618, 1092 , 1114 , 1139 , 1140 , 1141, 1142 , 1143 , 1144 , 1146 , 1147, 1148, 1062912, 1062916, 1062918, 1062919, 1062922, 1062929, 1062930, 1062934, 1062939, 1062940, 1062944, 1062949, 1062955, 1062957, 1062958, 1062959, 1062966, 1062969, 1062975, 1062976, 1062979, 1062981, 1062985, 1062987, 1062992, 1063002, 1063005, 1063007, 1063013, 1063016, 1063018, 1063020, 1063022, 1063062, 1063068, 1063075, 1063078, 1063080, 1063083, 1063084, 1063091, 1063095, 1063096, 1063098, 1107579, 1107591, 1107598, 1107761, 1107705, 1107748, 1109361, 1109388, 1109378, 1109399, 1109400, 1109401, 1109403, 1109404, 1109406, 1109409, 1109411, 1109414, 1109417, 1109418, 1109421, 1109427, 1109428, 1109429, 1109431, 1109432, 1109433, 1109891, 1109893, 1109947, 1179553, 1182781, 1182788, 1182791, 1182792, 1182795, 1182798, 1182801, 1182806, 1182811, 1182815);
delete from hfj_spidx_token where res_id in ( 156618, 1092 , 1114 , 1139 , 1140 , 1141, 1142 , 1143 , 1144 , 1146 , 1147, 1148, 1062912, 1062916, 1062918, 1062919, 1062922, 1062929, 1062930, 1062934, 1062939, 1062940, 1062944, 1062949, 1062955, 1062957, 1062958, 1062959, 1062966, 1062969, 1062975, 1062976, 1062979, 1062981, 1062985, 1062987, 1062992, 1063002, 1063005, 1063007, 1063013, 1063016, 1063018, 1063020, 1063022, 1063062, 1063068, 1063075, 1063078, 1063080, 1063083, 1063084, 1063091, 1063095, 1063096, 1063098, 1107579, 1107591, 1107598, 1107761, 1107705, 1107748, 1109361, 1109388, 1109378, 1109399, 1109400, 1109401, 1109403, 1109404, 1109406, 1109409, 1109411, 1109414, 1109417, 1109418, 1109421, 1109427, 1109428, 1109429, 1109431, 1109432, 1109433, 1109891, 1109893, 1109947, 1179553, 1182781, 1182788, 1182791, 1182792, 1182795, 1182798, 1182801, 1182806, 1182811, 1182815);
delete from hfj_search_result where resource_pid in ( 156618, 1092 , 1114 , 1139 , 1140 , 1141, 1142 , 1143 , 1144 , 1146 , 1147, 1148, 1062912, 1062916, 1062918, 1062919, 1062922, 1062929, 1062930, 1062934, 1062939, 1062940, 1062944, 1062949, 1062955, 1062957, 1062958, 1062959, 1062966, 1062969, 1062975, 1062976, 1062979, 1062981, 1062985, 1062987, 1062992, 1063002, 1063005, 1063007, 1063013, 1063016, 1063018, 1063020, 1063022, 1063062, 1063068, 1063075, 1063078, 1063080, 1063083, 1063084, 1063091, 1063095, 1063096, 1063098, 1107579, 1107591, 1107598, 1107761, 1107705, 1107748, 1109361, 1109388, 1109378, 1109399, 1109400, 1109401, 1109403, 1109404, 1109406, 1109409, 1109411, 1109414, 1109417, 1109418, 1109421, 1109427, 1109428, 1109429, 1109431, 1109432, 1109433, 1109891, 1109893, 1109947, 1179553, 1182781, 1182788, 1182791, 1182792, 1182795, 1182798, 1182801, 1182806, 1182811, 1182815);
delete from hfj_resource where res_id in ( 156618, 1092 , 1114 , 1139 , 1140 , 1141, 1142 , 1143 , 1144 , 1146 , 1147, 1148, 1062912, 1062916, 1062918, 1062919, 1062922, 1062929, 1062930, 1062934, 1062939, 1062940, 1062944, 1062949, 1062955, 1062957, 1062958, 1062959, 1062966, 1062969, 1062975, 1062976, 1062979, 1062981, 1062985, 1062987, 1062992, 1063002, 1063005, 1063007, 1063013, 1063016, 1063018, 1063020, 1063022, 1063062, 1063068, 1063075, 1063078, 1063080, 1063083, 1063084, 1063091, 1063095, 1063096, 1063098, 1107579, 1107591, 1107598, 1107761, 1107705, 1107748, 1109361, 1109388, 1109378, 1109399, 1109400, 1109401, 1109403, 1109404, 1109406, 1109409, 1109411, 1109414, 1109417, 1109418, 1109421, 1109427, 1109428, 1109429, 1109431, 1109432, 1109433, 1109891, 1109893, 1109947, 1179553, 1182781, 1182788, 1182791, 1182792, 1182795, 1182798, 1182801, 1182806, 1182811, 1182815);

View File

@ -1,32 +1,22 @@
package org.hl7.fhir.dstu2016may.hapi.validation;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import ca.uhn.fhir.context.FhirContext;
import org.apache.commons.io.Charsets;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.dstu2016may.model.Bundle;
import org.hl7.fhir.dstu2016may.model.*;
import org.hl7.fhir.dstu2016may.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.dstu2016may.model.CodeSystem;
import org.hl7.fhir.dstu2016may.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.dstu2016may.model.DomainResource;
import org.hl7.fhir.dstu2016may.model.OperationOutcome.IssueSeverity;
import org.hl7.fhir.dstu2016may.model.StructureDefinition;
import org.hl7.fhir.dstu2016may.model.ValueSet;
import org.hl7.fhir.dstu2016may.model.ValueSet.ConceptReferenceComponent;
import org.hl7.fhir.dstu2016may.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.dstu2016may.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class DefaultProfileValidationSupport implements IValidationSupport {
@ -55,6 +45,15 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
return retVal;
}
@Override
public List<IBaseResource> fetchAllConformanceResources(FhirContext theContext) {
ArrayList<IBaseResource> retVal = new ArrayList<>();
retVal.addAll(myCodeSystems.values());
retVal.addAll(myStructureDefinitions.values());
retVal.addAll(myValueSets.values());
return retVal;
}
@Override
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
return new ArrayList<StructureDefinition>(provideStructureDefinitionMap(theContext).values());
@ -219,25 +218,6 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
return structureDefinitions;
}
@Override
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
CodeSystem cs = fetchCodeSystem(theContext, theCodeSystem);
if (cs != null) {
boolean caseSensitive = true;
if (cs.hasCaseSensitive()) {
caseSensitive = cs.getCaseSensitive();
}
CodeValidationResult retVal = testIfConceptIsInList(theCode, cs.getConcept(), caseSensitive);
if (retVal != null) {
return retVal;
}
}
return new CodeValidationResult(IssueSeverity.WARNING, "Unknown code: " + theCodeSystem + " / " + theCode);
}
private CodeValidationResult testIfConceptIsInList(String theCode, List<ConceptDefinitionComponent> conceptList, boolean theCaseSensitive) {
String code = theCode;
if (theCaseSensitive == false) {
@ -269,4 +249,23 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
return retVal;
}
@Override
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
CodeSystem cs = fetchCodeSystem(theContext, theCodeSystem);
if (cs != null) {
boolean caseSensitive = true;
if (cs.hasCaseSensitive()) {
caseSensitive = cs.getCaseSensitive();
}
CodeValidationResult retVal = testIfConceptIsInList(theCode, cs.getConcept(), caseSensitive);
if (retVal != null) {
return retVal;
}
}
return new CodeValidationResult(IssueSeverity.WARNING, "Unknown code: " + theCodeSystem + " / " + theCode);
}
}

View File

@ -1,10 +1,6 @@
package org.hl7.fhir.dstu2016may.hapi.validation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import ca.uhn.fhir.context.FhirContext;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.dstu2016may.model.CodeSystem;
import org.hl7.fhir.dstu2016may.model.StructureDefinition;
@ -13,7 +9,10 @@ import org.hl7.fhir.dstu2016may.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.dstu2016may.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* This class is an implementation of {@link IValidationSupport} which may be pre-populated
@ -29,11 +28,36 @@ public class PrePopulatedValidationSupport implements IValidationSupport {
* Constructor
*/
public PrePopulatedValidationSupport() {
myStructureDefinitions = new HashMap<String,StructureDefinition>();
myValueSets = new HashMap<String,ValueSet>();
myCodeSystems = new HashMap<String,CodeSystem>();
myStructureDefinitions = new HashMap<String, StructureDefinition>();
myValueSets = new HashMap<String, ValueSet>();
myCodeSystems = new HashMap<String, CodeSystem>();
}
/**
* Constructor
*
* @param theStructureDefinitions The StructureDefinitions to be returned by this module. Keys are the logical URL for the resource, and
* values are the resource itself.
* @param theValueSets The ValueSets to be returned by this module. Keys are the logical URL for the resource, and values are
* the resource itself.
* @param theCodeSystems The CodeSystems to be returned by this module. Keys are the logical URL for the resource, and values are
* the resource itself.
*/
public PrePopulatedValidationSupport(Map<String, StructureDefinition> theStructureDefinitions, Map<String, ValueSet> theValueSets, Map<String, CodeSystem> theCodeSystems) {
myStructureDefinitions = theStructureDefinitions;
myValueSets = theValueSets;
myCodeSystems = theCodeSystems;
}
/**
* Add a new CodeSystem resource which will be available to the validator. Note that
* {@link CodeSystem#getUrl() the URL field) in this resource must contain a value as this
* value will be used as the logical URL.
*/
public void addCodeSystem(CodeSystem theCodeSystem) {
Validate.notBlank(theCodeSystem.getUrl(), "theCodeSystem.getUrl() must not return a value");
myCodeSystems.put(theCodeSystem.getUrl(), theCodeSystem);
}
/**
* Add a new StructureDefinition resource which will be available to the validator. Note that
@ -55,40 +79,20 @@ public class PrePopulatedValidationSupport implements IValidationSupport {
myValueSets.put(theValueSet.getUrl(), theValueSet);
}
/**
* Add a new CodeSystem resource which will be available to the validator. Note that
* {@link CodeSystem#getUrl() the URL field) in this resource must contain a value as this
* value will be used as the logical URL.
*/
public void addCodeSystem(CodeSystem theCodeSystem) {
Validate.notBlank(theCodeSystem.getUrl(), "theCodeSystem.getUrl() must not return a value");
myCodeSystems.put(theCodeSystem.getUrl(), theCodeSystem);
}
/**
* Constructor
*
* @param theStructureDefinitions
* The StructureDefinitions to be returned by this module. Keys are the logical URL for the resource, and
* values are the resource itself.
* @param theValueSets
* The ValueSets to be returned by this module. Keys are the logical URL for the resource, and values are
* the resource itself.
* @param theCodeSystems
* The CodeSystems to be returned by this module. Keys are the logical URL for the resource, and values are
* the resource itself.
*/
public PrePopulatedValidationSupport(Map<String, StructureDefinition> theStructureDefinitions, Map<String, ValueSet> theValueSets, Map<String, CodeSystem> theCodeSystems) {
myStructureDefinitions = theStructureDefinitions;
myValueSets = theValueSets;
myCodeSystems = theCodeSystems;
}
@Override
public ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) {
return null;
}
@Override
public List<IBaseResource> fetchAllConformanceResources(FhirContext theContext) {
ArrayList<IBaseResource> retVal = new ArrayList<>();
retVal.addAll(myCodeSystems.values());
retVal.addAll(myStructureDefinitions.values());
retVal.addAll(myValueSets.values());
return retVal;
}
@Override
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
return new ArrayList<StructureDefinition>(myStructureDefinitions.values());

View File

@ -1,19 +1,18 @@
package org.hl7.fhir.dstu2016may.hapi.validation;
import static org.apache.commons.lang3.StringUtils.isBlank;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import ca.uhn.fhir.context.FhirContext;
import org.hl7.fhir.dstu2016may.model.CodeSystem;
import org.hl7.fhir.dstu2016may.model.StructureDefinition;
import org.hl7.fhir.dstu2016may.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.dstu2016may.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static org.apache.commons.lang3.StringUtils.isBlank;
public class ValidationSupportChain implements IValidationSupport {
@ -52,6 +51,32 @@ public class ValidationSupportChain implements IValidationSupport {
return myChain.get(0).expandValueSet(theCtx, theInclude);
}
@Override
public List<IBaseResource> fetchAllConformanceResources(FhirContext theContext) {
List<IBaseResource> retVal = new ArrayList<>();
for (IValidationSupport next : myChain) {
List<IBaseResource> candidates = next.fetchAllConformanceResources(theContext);
if (candidates != null) {
retVal.addAll(candidates);
}
}
return retVal;
}
@Override
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
ArrayList<StructureDefinition> retVal = new ArrayList<StructureDefinition>();
Set<String> urls = new HashSet<String>();
for (IValidationSupport nextSupport : myChain) {
for (StructureDefinition next : nextSupport.fetchAllStructureDefinitions(theContext)) {
if (isBlank(next.getUrl()) || urls.add(next.getUrl())) {
retVal.add(next);
}
}
}
return retVal;
}
@Override
public CodeSystem fetchCodeSystem(FhirContext theCtx, String theSystem) {
for (IValidationSupport next : myChain) {
@ -105,18 +130,4 @@ public class ValidationSupportChain implements IValidationSupport {
return myChain.get(0).validateCode(theCtx, theCodeSystem, theCode, theDisplay);
}
@Override
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
ArrayList<StructureDefinition> retVal = new ArrayList<StructureDefinition>();
Set<String> urls= new HashSet<String>();
for (IValidationSupport nextSupport : myChain) {
for (StructureDefinition next : nextSupport.fetchAllStructureDefinitions(theContext)) {
if (isBlank(next.getUrl()) || urls.add(next.getUrl())) {
retVal.add(next);
}
}
}
return retVal;
}
}

View File

@ -1,11 +1,6 @@
package org.hl7.fhir.dstu3.hapi.validation;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;
import ca.uhn.fhir.context.FhirContext;
import org.apache.commons.io.Charsets;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
@ -19,7 +14,11 @@ import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import ca.uhn.fhir.context.FhirContext;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class DefaultProfileValidationSupport implements IValidationSupport {
@ -52,6 +51,15 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
return retVal;
}
@Override
public List<IBaseResource> fetchAllConformanceResources(FhirContext theContext) {
ArrayList<IBaseResource> retVal = new ArrayList<>();
retVal.addAll(myCodeSystems.values());
retVal.addAll(myStructureDefinitions.values());
retVal.addAll(myValueSets.values());
return retVal;
}
@Override
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
return new ArrayList<StructureDefinition>(provideStructureDefinitionMap(theContext).values());
@ -195,25 +203,6 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
return structureDefinitions;
}
@Override
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
CodeSystem cs = fetchCodeSystem(theContext, theCodeSystem);
if (cs != null) {
boolean caseSensitive = true;
if (cs.hasCaseSensitive()) {
caseSensitive = cs.getCaseSensitive();
}
CodeValidationResult retVal = testIfConceptIsInList(theCode, cs.getConcept(), caseSensitive);
if (retVal != null) {
return retVal;
}
}
return new CodeValidationResult(IssueSeverity.WARNING, "Unknown code: " + theCodeSystem + " / " + theCode);
}
private CodeValidationResult testIfConceptIsInList(String theCode, List<ConceptDefinitionComponent> conceptList, boolean theCaseSensitive) {
String code = theCode;
if (theCaseSensitive == false) {
@ -245,4 +234,23 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
return retVal;
}
@Override
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
CodeSystem cs = fetchCodeSystem(theContext, theCodeSystem);
if (cs != null) {
boolean caseSensitive = true;
if (cs.hasCaseSensitive()) {
caseSensitive = cs.getCaseSensitive();
}
CodeValidationResult retVal = testIfConceptIsInList(theCode, cs.getConcept(), caseSensitive);
if (retVal != null) {
return retVal;
}
}
return new CodeValidationResult(IssueSeverity.WARNING, "Unknown code: " + theCodeSystem + " / " + theCode);
}
}

View File

@ -1,19 +1,21 @@
package org.hl7.fhir.dstu3.hapi.validation;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import ca.uhn.fhir.context.FhirContext;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.dstu3.model.CodeSystem;
import org.hl7.fhir.dstu3.model.MetadataResource;
import org.hl7.fhir.dstu3.model.StructureDefinition;
import org.hl7.fhir.dstu3.model.ValueSet;
import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.instance.model.api.IBaseResource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
/**
* This class is an implementation of {@link IValidationSupport} which may be pre-populated
@ -29,23 +31,20 @@ public class PrePopulatedValidationSupport implements IValidationSupport {
* Constructor
*/
public PrePopulatedValidationSupport() {
myStructureDefinitions = new HashMap<String, StructureDefinition>();
myValueSets = new HashMap<String, ValueSet>();
myCodeSystems = new HashMap<String, CodeSystem>();
myStructureDefinitions = new HashMap<>();
myValueSets = new HashMap<>();
myCodeSystems = new HashMap<>();
}
/**
* Constructor
*
* @param theStructureDefinitions
* The StructureDefinitions to be returned by this module. Keys are the logical URL for the resource, and
* values are the resource itself.
* @param theValueSets
* The ValueSets to be returned by this module. Keys are the logical URL for the resource, and values are
* the resource itself.
* @param theCodeSystems
* The CodeSystems to be returned by this module. Keys are the logical URL for the resource, and values are
* the resource itself.
* @param theStructureDefinitions The StructureDefinitions to be returned by this module. Keys are the logical URL for the resource, and
* values are the resource itself.
* @param theValueSets The ValueSets to be returned by this module. Keys are the logical URL for the resource, and values are
* the resource itself.
* @param theCodeSystems The CodeSystems to be returned by this module. Keys are the logical URL for the resource, and values are
* the resource itself.
*/
public PrePopulatedValidationSupport(Map<String, StructureDefinition> theStructureDefinitions, Map<String, ValueSet> theValueSets, Map<String, CodeSystem> theCodeSystems) {
myStructureDefinitions = theStructureDefinitions;
@ -131,6 +130,15 @@ public class PrePopulatedValidationSupport implements IValidationSupport {
return null;
}
@Override
public List<IBaseResource> fetchAllConformanceResources(FhirContext theContext) {
ArrayList<IBaseResource> retVal = new ArrayList<>();
retVal.addAll(myCodeSystems.values());
retVal.addAll(myStructureDefinitions.values());
retVal.addAll(myValueSets.values());
return retVal;
}
@Override
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
return new ArrayList<StructureDefinition>(myStructureDefinitions.values());

View File

@ -1,131 +1,143 @@
package org.hl7.fhir.dstu3.hapi.validation;
import static org.apache.commons.lang3.StringUtils.isBlank;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import ca.uhn.fhir.context.FhirContext;
import org.hl7.fhir.dstu3.model.CodeSystem;
import org.hl7.fhir.dstu3.model.StructureDefinition;
import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static org.apache.commons.lang3.StringUtils.isBlank;
public class ValidationSupportChain implements IValidationSupport {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValidationSupportChain.class);
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValidationSupportChain.class);
private List<IValidationSupport> myChain;
private List<IValidationSupport> myChain;
/**
* Constructor
*/
public ValidationSupportChain() {
myChain = new ArrayList<IValidationSupport>();
}
/**
* Constructor
*/
public ValidationSupportChain() {
myChain = new ArrayList<IValidationSupport>();
}
/**
* Constructor
*/
public ValidationSupportChain(IValidationSupport... theValidationSupportModules) {
this();
for (IValidationSupport next : theValidationSupportModules) {
if (next != null) {
myChain.add(next);
}
}
}
/**
* Constructor
*/
public ValidationSupportChain(IValidationSupport... theValidationSupportModules) {
this();
for (IValidationSupport next : theValidationSupportModules) {
if (next != null) {
myChain.add(next);
}
}
}
public void addValidationSupport(IValidationSupport theValidationSupport) {
myChain.add(theValidationSupport);
}
public void addValidationSupport(IValidationSupport theValidationSupport) {
myChain.add(theValidationSupport);
}
@Override
public ValueSetExpansionComponent expandValueSet(FhirContext theCtx, ConceptSetComponent theInclude) {
for (IValidationSupport next : myChain) {
if (next.isCodeSystemSupported(theCtx, theInclude.getSystem())) {
return next.expandValueSet(theCtx, theInclude);
}
}
return myChain.get(0).expandValueSet(theCtx, theInclude);
}
@Override
public ValueSetExpansionComponent expandValueSet(FhirContext theCtx, ConceptSetComponent theInclude) {
for (IValidationSupport next : myChain) {
if (next.isCodeSystemSupported(theCtx, theInclude.getSystem())) {
return next.expandValueSet(theCtx, theInclude);
}
}
return myChain.get(0).expandValueSet(theCtx, theInclude);
}
@Override
public CodeSystem fetchCodeSystem(FhirContext theCtx, String theSystem) {
for (IValidationSupport next : myChain) {
CodeSystem retVal = next.fetchCodeSystem(theCtx, theSystem);
if (retVal != null) {
return retVal;
}
}
return null;
}
@Override
public List<IBaseResource> fetchAllConformanceResources(FhirContext theContext) {
List<IBaseResource> retVal = new ArrayList<>();
for (IValidationSupport next : myChain) {
List<IBaseResource> candidates = next.fetchAllConformanceResources(theContext);
if (candidates != null) {
retVal.addAll(candidates);
}
}
return retVal;
}
@Override
public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
for (IValidationSupport next : myChain) {
T retVal = next.fetchResource(theContext, theClass, theUri);
if (retVal != null) {
return retVal;
}
}
return null;
}
@Override
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
ArrayList<StructureDefinition> retVal = new ArrayList<StructureDefinition>();
Set<String> urls = new HashSet<String>();
for (IValidationSupport nextSupport : myChain) {
for (StructureDefinition next : nextSupport.fetchAllStructureDefinitions(theContext)) {
if (isBlank(next.getUrl()) || urls.add(next.getUrl())) {
retVal.add(next);
}
}
}
return retVal;
}
@Override
public StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl) {
for (IValidationSupport next : myChain) {
StructureDefinition retVal = next.fetchStructureDefinition(theCtx, theUrl);
if (retVal != null) {
return retVal;
}
}
return null;
}
@Override
public CodeSystem fetchCodeSystem(FhirContext theCtx, String theSystem) {
for (IValidationSupport next : myChain) {
CodeSystem retVal = next.fetchCodeSystem(theCtx, theSystem);
if (retVal != null) {
return retVal;
}
}
return null;
}
@Override
public boolean isCodeSystemSupported(FhirContext theCtx, String theSystem) {
for (IValidationSupport next : myChain) {
if (next.isCodeSystemSupported(theCtx, theSystem)) {
return true;
}
}
return false;
}
@Override
public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
for (IValidationSupport next : myChain) {
T retVal = next.fetchResource(theContext, theClass, theUri);
if (retVal != null) {
return retVal;
}
}
return null;
}
@Override
public CodeValidationResult validateCode(FhirContext theCtx, String theCodeSystem, String theCode, String theDisplay) {
@Override
public StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl) {
for (IValidationSupport next : myChain) {
StructureDefinition retVal = next.fetchStructureDefinition(theCtx, theUrl);
if (retVal != null) {
return retVal;
}
}
return null;
}
ourLog.info("Validating code {} in chain with {} items", theCode, myChain.size());
@Override
public boolean isCodeSystemSupported(FhirContext theCtx, String theSystem) {
for (IValidationSupport next : myChain) {
if (next.isCodeSystemSupported(theCtx, theSystem)) {
return true;
}
}
return false;
}
for (IValidationSupport next : myChain) {
if (next.isCodeSystemSupported(theCtx, theCodeSystem)) {
CodeValidationResult result = next.validateCode(theCtx, theCodeSystem, theCode, theDisplay);
ourLog.info("Chain item {} returned outcome {}", next, result.isOk());
return result;
} else {
ourLog.info("Chain item {} does not support code system {}", next, theCodeSystem);
}
}
return myChain.get(0).validateCode(theCtx, theCodeSystem, theCode, theDisplay);
}
@Override
public CodeValidationResult validateCode(FhirContext theCtx, String theCodeSystem, String theCode, String theDisplay) {
ourLog.info("Validating code {} in chain with {} items", theCode, myChain.size());
for (IValidationSupport next : myChain) {
if (next.isCodeSystemSupported(theCtx, theCodeSystem)) {
CodeValidationResult result = next.validateCode(theCtx, theCodeSystem, theCode, theDisplay);
ourLog.info("Chain item {} returned outcome {}", next, result.isOk());
return result;
} else {
ourLog.info("Chain item {} does not support code system {}", next, theCodeSystem);
}
}
return myChain.get(0).validateCode(theCtx, theCodeSystem, theCode, theDisplay);
}
@Override
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
ArrayList<StructureDefinition> retVal = new ArrayList<StructureDefinition>();
Set<String> urls= new HashSet<String>();
for (IValidationSupport nextSupport : myChain) {
for (StructureDefinition next : nextSupport.fetchAllStructureDefinitions(theContext)) {
if (isBlank(next.getUrl()) || urls.add(next.getUrl())) {
retVal.add(next);
}
}
}
return retVal;
}
}

View File

@ -1,11 +1,6 @@
package org.hl7.fhir.r4.hapi.ctx;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;
import ca.uhn.fhir.context.FhirContext;
import org.apache.commons.io.Charsets;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
@ -14,10 +9,16 @@ import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.r4.model.CodeSystem.CodeSystemContentMode;
import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r4.model.ValueSet.*;
import org.hl7.fhir.r4.model.ValueSet.ConceptReferenceComponent;
import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import ca.uhn.fhir.context.FhirContext;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class DefaultProfileValidationSupport implements IValidationSupport {
@ -51,10 +52,20 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
}
@Override
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
return new ArrayList<StructureDefinition>(provideStructureDefinitionMap(theContext).values());
public List<IBaseResource> fetchAllConformanceResources(FhirContext theContext) {
ArrayList<IBaseResource> retVal = new ArrayList<>();
retVal.addAll(myCodeSystems.values());
retVal.addAll(myStructureDefinitions.values());
retVal.addAll(myValueSets.values());
return retVal;
}
@Override
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
return new ArrayList<>(provideStructureDefinitionMap(theContext).values());
}
@Override
public CodeSystem fetchCodeSystem(FhirContext theContext, String theSystem) {
return (CodeSystem) fetchCodeSystemOrValueSet(theContext, theSystem, true);
@ -65,8 +76,8 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
Map<String, CodeSystem> codeSystems = myCodeSystems;
Map<String, ValueSet> valueSets = myValueSets;
if (codeSystems == null || valueSets == null) {
codeSystems = new HashMap<String, CodeSystem>();
valueSets = new HashMap<String, ValueSet>();
codeSystems = new HashMap<>();
valueSets = new HashMap<>();
loadCodeSystems(theContext, codeSystems, valueSets, "/org/hl7/fhir/r4/model/valueset/valuesets.xml");
loadCodeSystems(theContext, codeSystems, valueSets, "/org/hl7/fhir/r4/model/valueset/v2-tables.xml");
@ -193,25 +204,6 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
return structureDefinitions;
}
@Override
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
CodeSystem cs = fetchCodeSystem(theContext, theCodeSystem);
if (cs != null) {
boolean caseSensitive = true;
if (cs.hasCaseSensitive()) {
caseSensitive = cs.getCaseSensitive();
}
CodeValidationResult retVal = testIfConceptIsInList(theCode, cs.getConcept(), caseSensitive);
if (retVal != null) {
return retVal;
}
}
return new CodeValidationResult(IssueSeverity.WARNING, "Unknown code: " + theCodeSystem + " / " + theCode);
}
private CodeValidationResult testIfConceptIsInList(String theCode, List<ConceptDefinitionComponent> conceptList, boolean theCaseSensitive) {
String code = theCode;
if (theCaseSensitive == false) {
@ -243,4 +235,23 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
return retVal;
}
@Override
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
CodeSystem cs = fetchCodeSystem(theContext, theCodeSystem);
if (cs != null) {
boolean caseSensitive = true;
if (cs.hasCaseSensitive()) {
caseSensitive = cs.getCaseSensitive();
}
CodeValidationResult retVal = testIfConceptIsInList(theCode, cs.getConcept(), caseSensitive);
if (retVal != null) {
return retVal;
}
}
return new CodeValidationResult(IssueSeverity.WARNING, "Unknown code: " + theCodeSystem + " / " + theCode);
}
}

View File

@ -1,16 +1,21 @@
package org.hl7.fhir.r4.hapi.ctx;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.util.*;
import ca.uhn.fhir.context.FhirContext;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.MetadataResource;
import org.hl7.fhir.r4.model.StructureDefinition;
import org.hl7.fhir.r4.model.ValueSet;
import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent;
import ca.uhn.fhir.context.FhirContext;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
/**
* This class is an implementation of {@link IValidationSupport} which may be pre-populated
@ -26,23 +31,21 @@ public class PrePopulatedValidationSupport implements IValidationSupport {
* Constructor
*/
public PrePopulatedValidationSupport() {
myStructureDefinitions = new HashMap<String, StructureDefinition>();
myValueSets = new HashMap<String, ValueSet>();
myCodeSystems = new HashMap<String, CodeSystem>();
myStructureDefinitions = new HashMap<>();
myValueSets = new HashMap<>();
myCodeSystems = new HashMap<>();
}
/**
* Constructor
*
* @param theStructureDefinitions
* The StructureDefinitions to be returned by this module. Keys are the logical URL for the resource, and
* values are the resource itself.
* @param theValueSets
* The ValueSets to be returned by this module. Keys are the logical URL for the resource, and values are
* the resource itself.
* @param theCodeSystems
* The CodeSystems to be returned by this module. Keys are the logical URL for the resource, and values are
* the resource itself.
* @param theStructureDefinitions The StructureDefinitions to be returned by this module. Keys are the logical URL for the resource, and
* values are the resource itself.
* @param theValueSets The ValueSets to be returned by this module. Keys are the logical URL for the resource, and values are
* the resource itself.
* @param theCodeSystems The CodeSystems to be returned by this module. Keys are the logical URL for the resource, and values are
* the resource itself.
*/
public PrePopulatedValidationSupport(Map<String, StructureDefinition> theStructureDefinitions, Map<String, ValueSet> theValueSets, Map<String, CodeSystem> theCodeSystems) {
myStructureDefinitions = theStructureDefinitions;
@ -128,6 +131,15 @@ public class PrePopulatedValidationSupport implements IValidationSupport {
return null;
}
@Override
public List<IBaseResource> fetchAllConformanceResources(FhirContext theContext) {
ArrayList<IBaseResource> retVal = new ArrayList<>();
retVal.addAll(myCodeSystems.values());
retVal.addAll(myStructureDefinitions.values());
retVal.addAll(myValueSets.values());
return retVal;
}
@Override
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
return new ArrayList<StructureDefinition>(myStructureDefinitions.values());

View File

@ -1,16 +1,18 @@
package org.hl7.fhir.r4.hapi.ctx;
import static org.apache.commons.lang3.StringUtils.isBlank;
import java.util.*;
import ca.uhn.fhir.context.FhirContext;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.StructureDefinition;
import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent;
import ca.uhn.fhir.context.FhirContext;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static org.apache.commons.lang3.StringUtils.isBlank;
public class ValidationSupportChain implements IValidationSupport {
@ -51,7 +53,19 @@ public class ValidationSupportChain implements IValidationSupport {
return myChain.get(0).expandValueSet(theCtx, theInclude);
}
@Override
@Override
public List<IBaseResource> fetchAllConformanceResources(FhirContext theContext) {
List<IBaseResource> retVal = new ArrayList<>();
for (IValidationSupport next : myChain) {
List<IBaseResource> candidates = next.fetchAllConformanceResources(theContext);
if (candidates != null) {
retVal.addAll(candidates);
}
}
return retVal;
}
@Override
public CodeSystem fetchCodeSystem(FhirContext theCtx, String theSystem) {
for (IValidationSupport next : myChain) {
CodeSystem retVal = next.fetchCodeSystem(theCtx, theSystem);

View File

@ -85,7 +85,7 @@ public class LoggingInterceptorTest {
System.out.flush();
return
formattedMessage.contains("Client request: GET http://localhost:" + ourPort + "/Patient/1 HTTP/1.1") ||
formattedMessage.contains("Client response: HTTP 200 OK (Location: http://localhost:" + ourPort + "/Patient/1/_history/1)");
formattedMessage.contains("Client response: HTTP 200 OK (Patient/1/_history/1)");
}
}));
}

View File

@ -1828,6 +1828,7 @@
<module>example-projects/hapi-fhir-base-example-embedded-ws</module>
<module>example-projects/hapi-fhir-standalone-overlay-example</module>
<module>hapi-fhir-jacoco</module>
<module>hapi-fhir-igpacks</module>
<!--<module>hapi-fhir-osgi-core</module>-->
</modules>
</profile>
@ -1875,6 +1876,4 @@
</profile>
</profiles>
<modules>
</modules>
</project>