ValueSet cleanup (#4227)

* POST release deferred cleanup

* Fixes

* One more test fix

* Test fixes

* Test fixes

* Test cleanup

* Test fixes

* Fixes

* ValueSet cleanup

* Test fix

* Test fixes

* Fixes

* Test fixes

* Fixed

* Test fixes

* Test fixes

* Test fixes

* Test fixes

* Test fixc

* Build fix

* Fix merge artifact

* Build fix

* Work on tests

* Test fixes

* Work

* Fixes

* Changelog fix

* Add changelog

* Test fix

* Test fixes

* Fixes

* Test fixes

* Test fixes

* Test fixes

* Test fix

* Tests

* Bumps

* Fixes

* Add errorprone

* Drop bz2 bins

* POM fix

* Build fix

* Update

* Test fix

Co-authored-by: James Agnew <james@jamess-mbp.lan>
This commit is contained in:
James Agnew 2022-11-08 22:18:36 -05:00 committed by GitHub
parent d3367cfede
commit b3ebbe7933
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
323 changed files with 4209 additions and 4912 deletions

3
.gitignore vendored
View File

@ -19,6 +19,9 @@ ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString/
ca.uhn.fhir.jpa.entity.ResourceTable/
ca.uhn.fhir.jpa.entity.TermConcept/
# Ignore Java Heap Dumps
java_pid*.*
# Vagrant stuff.
.vagrant
/vagrant/build

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>6.3.0-PRE3-SNAPSHOT</version>
<version>6.3.0-PRE4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
@ -126,12 +126,11 @@
<execution>
<phase>process-sources</phase>
<goals>
<goal>checkstyle</goal>
<goal>check</goal>
</goals>
<configuration>
<failsOnError>true</failsOnError>
<enableRulesSummary>true</enableRulesSummary>
<enableSeveritySummary>true</enableSeveritySummary>
<logViolationsToConsole>true</logViolationsToConsole>
<consoleOutput>true</consoleOutput>
<configLocation>${maven.multiModuleProjectDirectory}/src/checkstyle/checkstyle.xml</configLocation>
</configuration>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.3.0-PRE3-SNAPSHOT</version>
<version>6.3.0-PRE4-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.3.0-PRE3-SNAPSHOT</version>
<version>6.3.0-PRE4-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -394,8 +394,9 @@ public class FhirContext {
return myNarrativeGenerator;
}
public void setNarrativeGenerator(final INarrativeGenerator theNarrativeGenerator) {
public FhirContext setNarrativeGenerator(final INarrativeGenerator theNarrativeGenerator) {
myNarrativeGenerator = theNarrativeGenerator;
return this;
}
/**

View File

@ -325,9 +325,9 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
private void loadStructureDefinitions(Map<String, IBaseResource> theCodeSystems, String theClasspath) {
ourLog.info("Loading structure definitions from classpath: {}", theClasspath);
try (InputStream valuesetText = DefaultProfileValidationSupport.class.getResourceAsStream(theClasspath)) {
if (valuesetText != null) {
try (InputStreamReader reader = new InputStreamReader(valuesetText, Constants.CHARSET_UTF8)) {
try (InputStream valueSetText = DefaultProfileValidationSupport.class.getResourceAsStream(theClasspath)) {
if (valueSetText != null) {
try (InputStreamReader reader = new InputStreamReader(valueSetText, Constants.CHARSET_UTF8)) {
List<IBaseResource> resources = parseBundle(reader);
for (IBaseResource next : resources) {

View File

@ -181,7 +181,11 @@ public abstract class BaseInterceptorService<POINTCUT extends IPointcut> impleme
private void unregisterInterceptorsIf(Predicate<Object> theShouldUnregisterFunction, ListMultimap<POINTCUT, BaseInvoker> theGlobalInvokers) {
synchronized (myRegistryMutex) {
theGlobalInvokers.entries().removeIf(t -> theShouldUnregisterFunction.test(t.getValue().getInterceptor()));
for (Map.Entry<POINTCUT, BaseInvoker> nextInvoker : new ArrayList<>(theGlobalInvokers.entries())) {
if (theShouldUnregisterFunction.test(nextInvoker.getValue().getInterceptor())) {
unregisterInterceptor(nextInvoker.getValue().getInterceptor());
}
}
}
}

View File

@ -1,8 +1,8 @@
package ca.uhn.fhir.jpa.provider;
package ca.uhn.fhir.util;
/*
/*-
* #%L
* HAPI FHIR JPA Server
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2022 Smile CDR, Inc.
* %%
@ -20,17 +20,15 @@ package ca.uhn.fhir.jpa.provider;
* #L%
*/
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.model.api.IResource;
public class JpaResourceProviderDstu2<T extends IResource> extends BaseJpaResourceProvider<T> {
public JpaResourceProviderDstu2() {
// nothing
/**
* Exception superclass for an exception representing an unrecoverable failure
*/
public abstract class BaseUnrecoverableRuntimeException extends RuntimeException {
public BaseUnrecoverableRuntimeException(String theMessage) {
super(theMessage);
}
public JpaResourceProviderDstu2(IFhirResourceDao<T> theDao) {
super(theDao);
public BaseUnrecoverableRuntimeException(String theMessage, Throwable theCause) {
super(theMessage, theCause);
}
}

View File

@ -61,4 +61,12 @@ public class DatatypeUtil {
return b.toString();
}
/**
* Returns {@link IPrimitiveType#getValueAsString()} if <code>thePrimitiveType</code> is
* not null, else returns null.
*/
public static String toStringValue(IPrimitiveType<?> thePrimitiveType) {
return thePrimitiveType != null ? thePrimitiveType.getValueAsString() : null;
}
}

View File

@ -1,65 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.3.0-PRE3-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>
<artifactId>hapi-fhir-batch</artifactId>
<packaging>jar</packaging>
<name>HAPI FHIR JPA Server - Batch Task Processor</name>
<description>Default implementation of batch job submitter along with constants used by the different hapi-fhir batch
jobs.
</description>
<dependencies>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
</dependency>
<!--test dependencies -->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
<version>${project.version}</version>
</dependency>
<!-- test -->
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-test-utilities</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
<configuration>
<skipDeploy>true</skipDeploy>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

View File

@ -3,14 +3,14 @@
<modelVersion>4.0.0</modelVersion>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-bom</artifactId>
<version>6.3.0-PRE3-SNAPSHOT</version>
<version>6.3.0-PRE4-SNAPSHOT</version>
<packaging>pom</packaging>
<name>HAPI FHIR BOM</name>
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.3.0-PRE3-SNAPSHOT</version>
<version>6.3.0-PRE4-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>6.3.0-PRE3-SNAPSHOT</version>
<version>6.3.0-PRE4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
@ -18,8 +18,6 @@
<dependency>
<groupId>com.puppycrawl.tools</groupId>
<artifactId>checkstyle</artifactId>
<!-- FIXME move version up -->
<version>8.43</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.3.0-PRE3-SNAPSHOT</version>
<version>6.3.0-PRE4-SNAPSHOT</version>
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
</parent>
@ -78,10 +78,6 @@
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
<dependency>
<groupId>commons-cli</groupId>
@ -177,11 +173,15 @@
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-api</artifactId>
<artifactId>websocket-jetty-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-client</artifactId>
<artifactId>websocket-core-client</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-jetty-client</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>

View File

@ -25,9 +25,14 @@ import ca.uhn.fhir.model.primitive.IdDt;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.eclipse.jetty.websocket.api.Frame;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.*;
import org.eclipse.jetty.websocket.api.extensions.Frame;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketFrame;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
import org.eclipse.jetty.websocket.client.WebSocketClient;

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-cli</artifactId>
<version>6.3.0-PRE3-SNAPSHOT</version>
<version>6.3.0-PRE4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.3.0-PRE3-SNAPSHOT</version>
<version>6.3.0-PRE4-SNAPSHOT</version>
<relativePath>../../hapi-deployable-pom</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>6.3.0-PRE3-SNAPSHOT</version>
<version>6.3.0-PRE4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.3.0-PRE3-SNAPSHOT</version>
<version>6.3.0-PRE4-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -1818,7 +1818,7 @@ public class GenericOkHttpClientDstu2Test {
@Override
public void handle(String theArg0, Request theRequest, HttpServletRequest theServletRequest, HttpServletResponse theResp) throws IOException, ServletException {
theRequest.setHandled(true);
ourRequestUri = "http:" + theRequest.getHttpURI().toString();
ourRequestUri = theRequest.getHttpURI().toString();
ourRequestUriAll.add(ourRequestUri);
ourRequestMethod = theRequest.getMethod();
ourRequestContentType = theServletRequest.getContentType();

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.3.0-PRE3-SNAPSHOT</version>
<version>6.3.0-PRE4-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.3.0-PRE3-SNAPSHOT</version>
<version>6.3.0-PRE4-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -79,7 +79,10 @@ public class VersionCanonicalizer {
public VersionCanonicalizer(FhirVersionEnum theTargetVersion) {
switch (theTargetVersion) {
case DSTU2:
myStrategy = new Dstu2Strategy();
myStrategy = new Dstu2Strategy(false);
break;
case DSTU2_HL7ORG:
myStrategy = new Dstu2Strategy(true);
break;
case DSTU3:
myStrategy = new Dstu3Strategy();
@ -179,6 +182,11 @@ public class VersionCanonicalizer {
private final FhirContext myDstu2Hl7OrgContext = FhirContext.forDstu2Hl7OrgCached();
private final FhirContext myDstu2Context = FhirContext.forDstu2Cached();
private final boolean myHl7OrgStructures;
public Dstu2Strategy(boolean theHl7OrgStructures) {
myHl7OrgStructures = theHl7OrgStructures;
}
@Override
public CapabilityStatement capabilityStatementToCanonical(ca.uhn.fhir.model.dstu2.resource.BaseResource theCapabilityStatement) {
@ -216,7 +224,8 @@ public class VersionCanonicalizer {
@Override
public ValueSet valueSetToCanonical(IBaseResource theValueSet) {
org.hl7.fhir.dstu2.model.Resource reencoded = reencodeToHl7Org(theValueSet);
return (ValueSet) VersionConvertorFactory_10_40.convertResource(reencoded, ADVISOR_10_40);
ValueSet valueSet = (ValueSet) VersionConvertorFactory_10_40.convertResource(reencoded, ADVISOR_10_40);
return valueSet;
}
@Override
@ -267,10 +276,16 @@ public class VersionCanonicalizer {
}
private Resource reencodeToHl7Org(IBaseResource theInput) {
if (myHl7OrgStructures) {
return (Resource) theInput;
}
return (Resource) myDstu2Hl7OrgContext.newJsonParser().parseResource(myDstu2Context.newJsonParser().encodeResourceToString(theInput));
}
private IBaseResource reencodeFromHl7Org(Resource theInput) {
if (myHl7OrgStructures) {
return theInput;
}
return myDstu2Context.newJsonParser().parseResource(myDstu2Hl7OrgContext.newJsonParser().encodeResourceToString(theInput));
}

View File

@ -4,6 +4,7 @@ import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.model.dstu2.composite.CodingDt;
import org.hl7.fhir.instance.model.api.IBaseCoding;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.ValueSet;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
@ -19,5 +20,4 @@ class VersionCanonicalizerTest {
assertEquals("dstuSystem", convertedCoding.getSystem());
}
}

View File

@ -9,37 +9,6 @@
</encoder>
</appender>
<logger name="org.springframework.web.socket.handler.ExceptionWebSocketHandlerDecorator" additivity="false" level="info">
<appender-ref ref="STDOUT" />
</logger>
<logger name="ca.uhn.fhir.jpa.dao.FhirResourceDaoSubscriptionDstu2" additivity="false" level="info">
<appender-ref ref="STDOUT" />
</logger>
<logger name="org.eclipse.jetty.websocket" additivity="false" level="info">
<appender-ref ref="STDOUT" />
</logger>
<logger name="org.eclipse" additivity="false" level="error">
</logger>
<logger name="ca.uhn.fhir.rest.client" additivity="false" level="info">
<appender-ref ref="STDOUT" />
</logger>
<logger name="ca.uhn.fhir.jpa.dao" additivity="false" level="info">
<appender-ref ref="STDOUT" />
</logger>
<!-- Set to 'trace' to enable SQL logging -->
<logger name="org.hibernate.SQL" additivity="false" level="info">
<appender-ref ref="STDOUT" />
</logger>
<!-- Set to 'trace' to enable SQL Value logging -->
<logger name="org.hibernate.type" additivity="false" level="info">
<appender-ref ref="STDOUT" />
</logger>
<root level="info">
<appender-ref ref="STDOUT" />

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>6.3.0-PRE3-SNAPSHOT</version>
<version>6.3.0-PRE4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,6 @@
<formats>
<format>zip</format>
<format>tar.bz2</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>

View File

@ -5,7 +5,6 @@
<formats>
<format>zip</format>
<format>tar.bz2</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>

View File

@ -5,7 +5,6 @@
<formats>
<format>zip</format>
<format>tar.bz2</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.3.0-PRE3-SNAPSHOT</version>
<version>6.3.0-PRE4-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -0,0 +1,8 @@
---
type: change
issue: 4221
title: "The JPA server implementation of the $process-message operation has been extracted into
a standalone provider called `ProcessMessageProvider` which can be registered individually
on servers that have provided an implementation of the backing operation. Because this operation
can not be implemented in a generic was in HAPI FHIR (it assumes business-specific processing
logic) it does not make sense to include it by default."

View File

@ -0,0 +1,5 @@
---
type: add
issue: 4227
title: "The internal LinkedChannelQueue implementation used for Batch2 processing and Subscription delivery
now uses automated retries if a message processing failure occurs in order to improve resiliency."

View File

@ -0,0 +1,5 @@
---
type: add
issue: 4227
title: "When indexing CodeSystem content in the terminology service, an unintended busy-wait cycle sometimes caused the
CPU to be thrashed while waiting for a batch job to complete. This has been resolved."

View File

@ -0,0 +1,6 @@
---
type: add
issue: 4227
title: "In some cases in the JPA server, CodeSystem updating failed and left the server with a hung
reindex job that never completes. This has been corrected. Thanks to GitHub user @tyfoni-systematic for
the bug report!"

View File

@ -0,0 +1,7 @@
---
type: add
issue: 4227
title: "When validating terminology as a part of resource validation or by directly calling the $validate-code
operations, an incorrect caching routine sometimes caused incorrect cached results to be returned in cases
where display names are being validated and multiple display names were validated for the same
system+code combination. This has been corrected."

View File

@ -0,0 +1,33 @@
---
- item:
type: "add"
title: "The version of a few dependencies have been bumped to the latest versions
(dependent HAPI modules listed in brackets):
<ul>
<li>SLF4j (All): 1.7.33 -> 2.0.3</li>
<li>Logback (All): 1.2.10 -> 1.4.4</li>
<li>Jetty (CLI): 9.4.48.v20220622 -> 10.0.12</li>
<li>Spring Boot: 2.7.4 -> 2.7.5</li>
</ul>
In addition the following dependencies have been replaced with newer equivalents:
<ul>
<li>
javax.annotation:javax.annotation-api:1.3.2 ->
jakarta.annotation:jakarta.annotation-api:1.3.3 &ndash;
This change brings HAPI FHIR up to the new permanent Maven dependency name for
this library. Note that this new version does not change the actual package
structure of the included classes from <code>javax.</code> to <code>jakarta.</code>,
it is only a change in the Maven packaging.
</li>
<li>
javax.transaction:javax.transaction-api:1.3.2 ->
jakarta.transaction:jakarta.transaction-api:1.3.5 &ndash;
This change brings HAPI FHIR up to the new permanent Maven dependency name for
this library. Note that this new version does not change the actual package
structure of the included classes from <code>javax.</code> to <code>jakarta.</code>,
it is only a change in the Maven packaging.
</li>
</ul>
"

View File

@ -11,7 +11,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.3.0-PRE3-SNAPSHOT</version>
<version>6.3.0-PRE4-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.3.0-PRE3-SNAPSHOT</version>
<version>6.3.0-PRE4-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>
@ -87,12 +87,6 @@
<artifactId>jboss-jaxrs-api_2.1_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
</dependency>
<!-- Unit test dependencies -->
<dependency>
<groupId>com.fasterxml.woodstox</groupId>

View File

@ -2032,7 +2032,7 @@ public class GenericJaxRsClientDstu2Test {
@Override
public void handle(String theArg0, Request theRequest, HttpServletRequest theServletRequest, HttpServletResponse theResp) throws IOException {
theRequest.setHandled(true);
ourRequestUri = "http:" + theRequest.getHttpURI().toString();
ourRequestUri = theRequest.getHttpURI().toString();
ourRequestUriAll.add(ourRequestUri);
ourRequestMethod = theRequest.getMethod();
ourRequestContentType = theServletRequest.getContentType();

View File

@ -2121,7 +2121,7 @@ public class GenericJaxRsClientDstu3Test {
@Override
public void handle(String theArg0, Request theRequest, HttpServletRequest theServletRequest, HttpServletResponse theResp) throws IOException {
theRequest.setHandled(true);
ourRequestUri = "http:" + theRequest.getHttpURI().toString();
ourRequestUri = theRequest.getHttpURI().toString();
ourRequestUriAll.add(ourRequestUri);
ourRequestMethod = theRequest.getMethod();
ourRequestContentType = theServletRequest.getContentType();

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.3.0-PRE3-SNAPSHOT</version>
<version>6.3.0-PRE4-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -21,10 +21,6 @@
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
</dependency>
<!-- for date logging -->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
@ -32,6 +28,11 @@
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
</dependency>
<!-- Hibernate -->
<dependency>
<groupId>org.hibernate</groupId>

View File

@ -229,13 +229,18 @@ public class CircularQueueCaptureQueriesListener extends BaseCaptureQueriesListe
.map(CircularQueueCaptureQueriesListener::formatQueryAsSql)
.collect(Collectors.toList());
List<String> newList = new ArrayList<>();
if (theIndexes != null && theIndexes.length > 0) {
List<String> newList = new ArrayList<>();
for (int i = 0; i < theIndexes.length; i++) {
newList.add(queries.get(theIndexes[i]));
int index = theIndexes[i];
newList.add("[" + index + "] " + queries.get(index));
}
} else {
for (int i = 0; i < queries.size(); i++) {
newList.add("[" + i + "] " + queries.get(i));
}
queries = newList;
}
queries = newList;
String queriesAsString = String.join("\n", queries);
ourLog.info("Select Queries:\n{}", queriesAsString);
@ -382,14 +387,14 @@ public class CircularQueueCaptureQueriesListener extends BaseCaptureQueriesListe
public int countInsertQueries() {
return getInsertQueries()
.stream()
.map(t->t.getSize())
.map(t -> t.getSize())
.reduce(0, Integer::sum);
}
public int countUpdateQueries() {
return getUpdateQueries()
.stream()
.map(t->t.getSize())
.map(t -> t.getSize())
.reduce(0, Integer::sum);
}
@ -404,6 +409,11 @@ public class CircularQueueCaptureQueriesListener extends BaseCaptureQueriesListe
return getSelectQueriesForCurrentThread().size();
}
// TODO: JA2 The count "forCurrentThread" methods work differently than the non
// current thread ones - The other ones aggregate multiple instances of the same
// query - In other words if the same query is issued twice with different parameters
// that counts for 2 on the other method but 1 for this one. Need to harmonize this,
// and should do it on this method since the higher number is more accurate.
public int countInsertQueriesForCurrentThread() {
return getInsertQueriesForCurrentThread().size();
}

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.3.0-PRE3-SNAPSHOT</version>
<version>6.3.0-PRE4-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>
@ -57,6 +57,11 @@
<artifactId>hapi-fhir-server-mdm</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-storage</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jpaserver-subscription</artifactId>
@ -132,11 +137,6 @@
<artifactId>hapi-fhir-validation-resources-r5</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-batch</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-storage-batch2</artifactId>
@ -158,6 +158,10 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>net.ttddyy</groupId>
<artifactId>datasource-proxy</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
@ -247,11 +251,6 @@
<!--
Dependencies that need to be added since JDK9
-->
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
@ -354,6 +353,10 @@
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
@ -375,6 +378,12 @@
<groupId>org.apache.jena</groupId>
<artifactId>jena-arq</artifactId>
<scope>compile</scope>
<exclusions>
<exclusion>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.jena</groupId>

View File

@ -239,6 +239,7 @@ public class JpaJobPersistenceImpl implements IJobPersistence {
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void markWorkChunkAsFailed(String theChunkId, String theErrorMessage) {
ourLog.info("Marking chunk {} as failed with message: {}", theChunkId, theErrorMessage);
String errorMessage;
if (theErrorMessage.length() > Batch2WorkChunkEntity.ERROR_MSG_MAX_LENGTH) {
ourLog.warn("Truncating error message that is too long to store in database: {}", theErrorMessage);
@ -353,6 +354,7 @@ public class JpaJobPersistenceImpl implements IJobPersistence {
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void deleteInstanceAndChunks(String theInstanceId) {
ourLog.info("Deleting instance and chunks: {}", theInstanceId);
myWorkChunkRepository.deleteAllForInstance(theInstanceId);
myJobInstanceRepository.deleteById(theInstanceId);
}
@ -360,6 +362,7 @@ public class JpaJobPersistenceImpl implements IJobPersistence {
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void deleteChunks(String theInstanceId) {
ourLog.info("Deleting all chunks for instance ID: {}", theInstanceId);
myWorkChunkRepository.deleteAllForInstance(theInstanceId);
}

View File

@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.config;
import ca.uhn.fhir.batch2.jobs.expunge.DeleteExpungeJobSubmitterImpl;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.interceptor.api.IInterceptorService;
@ -61,9 +62,12 @@ import ca.uhn.fhir.jpa.partition.PartitionLookupSvcImpl;
import ca.uhn.fhir.jpa.partition.PartitionManagementProvider;
import ca.uhn.fhir.jpa.partition.RequestPartitionHelperSvc;
import ca.uhn.fhir.jpa.provider.DiffProvider;
import ca.uhn.fhir.jpa.provider.ValueSetOperationProvider;
import ca.uhn.fhir.jpa.provider.ProcessMessageProvider;
import ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider;
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
import ca.uhn.fhir.jpa.provider.ValueSetOperationProvider;
import ca.uhn.fhir.jpa.provider.ValueSetOperationProviderDstu2;
import ca.uhn.fhir.jpa.provider.r4.IConsentExtensionProvider;
import ca.uhn.fhir.jpa.provider.r4.MemberMatcherR4Helper;
import ca.uhn.fhir.jpa.sched.AutowiringSpringBeanJobFactory;
@ -250,7 +254,10 @@ public class JpaConfig {
@Bean
@Lazy
public ValueSetOperationProvider valueSetOperationProvider() {
public ValueSetOperationProvider valueSetOperationProvider(FhirContext theFhirContext) {
if (theFhirContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU2)) {
return new ValueSetOperationProviderDstu2();
}
return new ValueSetOperationProvider();
}
@ -417,6 +424,12 @@ public class JpaConfig {
return new TerminologyUploaderProvider();
}
@Bean
@Lazy
public ProcessMessageProvider processMessageProvider() {
return new ProcessMessageProvider();
}
@Bean
public ISchedulerService schedulerService() {
return new HapiSchedulerServiceImpl().setDefaultGroup(HAPI_DEFAULT_SCHEDULER_GROUP);

View File

@ -21,20 +21,20 @@ package ca.uhn.fhir.jpa.config.dstu3;
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.ParserOptions;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import static ca.uhn.fhir.jpa.config.r4.FhirContextR4Config.configureFhirContext;
public class FhirContextDstu3Config {
@Primary
@Bean(name = "primaryFhirContext")
public FhirContext fhirContextDstu3() {
FhirContext retVal = FhirContext.forDstu3();
// Don't strip versions in some places
ParserOptions parserOptions = retVal.getParserOptions();
parserOptions.setDontStripVersionsFromReferencesAtPaths("AuditEvent.entity.reference");
configureFhirContext(retVal);
return retVal;
}
}

View File

@ -21,23 +21,38 @@ package ca.uhn.fhir.jpa.config.r4;
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.ParserOptions;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
public class FhirContextR4Config {
public static final String DEFAULT_PRESERVE_VERSION_REFS = "AuditEvent.entity.what";
public static final String DEFAULT_PRESERVE_VERSION_REFS_DSTU2 = "AuditEvent.object.reference";
public static final String DEFAULT_PRESERVE_VERSION_REFS_DSTU3 = "AuditEvent.entity.reference";
public static final String DEFAULT_PRESERVE_VERSION_REFS_R4_AND_LATER = "AuditEvent.entity.what";
@Bean(name = "primaryFhirContext")
@Primary
public FhirContext fhirContextR4() {
FhirContext retVal = FhirContext.forR4();
// Don't strip versions in some places
ParserOptions parserOptions = retVal.getParserOptions();
parserOptions.setDontStripVersionsFromReferencesAtPaths(DEFAULT_PRESERVE_VERSION_REFS);
configureFhirContext(retVal);
return retVal;
}
public static FhirContext configureFhirContext(FhirContext theFhirContext) {
// Don't strip versions in some places
ParserOptions parserOptions = theFhirContext.getParserOptions();
if (theFhirContext.getVersion().getVersion().isOlderThan(FhirVersionEnum.DSTU3)) {
parserOptions.setDontStripVersionsFromReferencesAtPaths(DEFAULT_PRESERVE_VERSION_REFS_DSTU2);
} else if (theFhirContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU3)) {
parserOptions.setDontStripVersionsFromReferencesAtPaths(DEFAULT_PRESERVE_VERSION_REFS_DSTU3);
} else {
parserOptions.setDontStripVersionsFromReferencesAtPaths(DEFAULT_PRESERVE_VERSION_REFS_R4_AND_LATER);
}
return theFhirContext;
}
}

View File

@ -21,23 +21,21 @@ package ca.uhn.fhir.jpa.config.r4b;
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.ParserOptions;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
public class FhirContextR4BConfig {
import static ca.uhn.fhir.jpa.config.r4.FhirContextR4Config.configureFhirContext;
public static final String DEFAULT_PRESERVE_VERSION_REFS = "AuditEvent.entity.what";
public class FhirContextR4BConfig {
@Bean(name = "primaryFhirContext")
@Primary
public FhirContext fhirContextR4B() {
FhirContext retVal = FhirContext.forR4B();
// Don't strip versions in some places
ParserOptions parserOptions = retVal.getParserOptions();
parserOptions.setDontStripVersionsFromReferencesAtPaths(DEFAULT_PRESERVE_VERSION_REFS);
configureFhirContext(retVal);
return retVal;
}
}

View File

@ -21,20 +21,20 @@ package ca.uhn.fhir.jpa.config.r5;
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.ParserOptions;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import static ca.uhn.fhir.jpa.config.r4.FhirContextR4Config.configureFhirContext;
public class FhirContextR5Config {
@Bean(name = "primaryFhirContext")
@Primary
public FhirContext fhirContextR5() {
FhirContext retVal = FhirContext.forR5();
// Don't strip versions in some places
ParserOptions parserOptions = retVal.getParserOptions();
parserOptions.setDontStripVersionsFromReferencesAtPaths("AuditEvent.entity.what");
configureFhirContext(retVal);
return retVal;
}
}

View File

@ -1,34 +0,0 @@
package ca.uhn.fhir.jpa.dao;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2022 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoMessageHeader;
import ca.uhn.fhir.model.dstu2.resource.MessageHeader;
import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException;
import org.hl7.fhir.instance.model.api.IBaseBundle;
public class FhirResourceDaoMessageHeaderDstu2 extends BaseHapiFhirResourceDao<MessageHeader> implements IFhirResourceDaoMessageHeader<MessageHeader> {
public static IBaseBundle throwProcessMessageNotImplemented() {
throw new NotImplementedOperationException(Msg.code(945) + "This operation is not yet implemented on this server");
}
}

View File

@ -1,321 +0,0 @@
package ca.uhn.fhir.jpa.dao;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2022 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.IValidationSupport.CodeValidationResult;
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoValueSet;
import ca.uhn.fhir.jpa.model.entity.BaseHasResource;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt;
import ca.uhn.fhir.model.dstu2.composite.CodingDt;
import ca.uhn.fhir.model.dstu2.resource.ValueSet;
import ca.uhn.fhir.model.dstu2.resource.ValueSet.CodeSystemConcept;
import ca.uhn.fhir.model.dstu2.resource.ValueSet.ComposeInclude;
import ca.uhn.fhir.model.dstu2.resource.ValueSet.ComposeIncludeConcept;
import ca.uhn.fhir.model.dstu2.resource.ValueSet.ExpansionContains;
import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.param.UriParam;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import org.hl7.fhir.common.hapi.validation.support.CachingValidationSupport;
import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.Nonnull;
import javax.annotation.PostConstruct;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import static ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoValueSetDstu3.vsValidateCodeOptions;
import static ca.uhn.fhir.jpa.util.LogicUtil.multiXor;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class FhirResourceDaoValueSetDstu2 extends BaseHapiFhirResourceDao<ValueSet>
implements IFhirResourceDaoValueSet<ValueSet, CodingDt, CodeableConceptDt>, IFhirResourceDaoCodeSystem<ValueSet, CodingDt, CodeableConceptDt> {
private static FhirContext ourRiCtx;
private DefaultProfileValidationSupport myDefaultProfileValidationSupport;
@Autowired
private IValidationSupport myJpaValidationSupport;
@Autowired
private FhirContext myFhirContext;
private CachingValidationSupport myValidationSupport;
private void addCompose(String theFilter, ValueSet theValueSetToPopulate, ValueSet theSourceValueSet, CodeSystemConcept theConcept) {
if (isBlank(theFilter)) {
addCompose(theValueSetToPopulate, theSourceValueSet.getCodeSystem().getSystem(), theConcept.getCode(), theConcept.getDisplay());
} else {
String filter = theFilter.toLowerCase();
if (theConcept.getDisplay().toLowerCase().contains(filter) || theConcept.getCode().toLowerCase().contains(filter)) {
addCompose(theValueSetToPopulate, theSourceValueSet.getCodeSystem().getSystem(), theConcept.getCode(), theConcept.getDisplay());
}
}
for (CodeSystemConcept nextChild : theConcept.getConcept()) {
addCompose(theFilter, theValueSetToPopulate, theSourceValueSet, nextChild);
}
}
private void addCompose(ValueSet retVal, String theSystem, String theCode, String theDisplay) {
if (isBlank(theCode)) {
return;
}
ExpansionContains contains = retVal.getExpansion().addContains();
contains.setSystem(theSystem);
contains.setCode(theCode);
contains.setDisplay(theDisplay);
}
@Override
public ValueSet expand(IIdType theId, ValueSetExpansionOptions theOptions, RequestDetails theRequest) {
ValueSet source = loadValueSetForExpansion(theId, theRequest);
return expand(source, theOptions);
}
@Override
public ValueSet expand(ValueSet source, ValueSetExpansionOptions theOptions) {
ValueSet retVal = new ValueSet();
retVal.setDate(DateTimeDt.withCurrentTime());
String filter = null;
if (theOptions != null) {
filter = theOptions.getFilter();
}
/*
* Add composed concepts
*/
for (ComposeInclude nextInclude : source.getCompose().getInclude()) {
for (ComposeIncludeConcept next : nextInclude.getConcept()) {
if (isBlank(filter)) {
addCompose(retVal, nextInclude.getSystem(), next.getCode(), next.getDisplay());
} else {
filter = filter.toLowerCase();
if (next.getDisplay().toLowerCase().contains(filter) || next.getCode().toLowerCase().contains(filter)) {
addCompose(retVal, nextInclude.getSystem(), next.getCode(), next.getDisplay());
}
}
}
}
/*
* Add defined concepts
*/
for (CodeSystemConcept next : source.getCodeSystem().getConcept()) {
addCompose(filter, retVal, source, next);
}
return retVal;
}
@Override
public ValueSet expandByIdentifier(String theUri, ValueSetExpansionOptions theOptions) {
if (isBlank(theUri)) {
throw new InvalidRequestException(Msg.code(946) + "URI must not be blank or missing");
}
ValueSet source;
ValueSet defaultValueSet = myDefaultProfileValidationSupport.fetchResource(ValueSet.class, theUri);
if (defaultValueSet != null) {
source = getContext().newJsonParser().parseResource(ValueSet.class, getRiCtx().newJsonParser().encodeResourceToString(defaultValueSet));
} else {
SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronousUpTo(1);
params.add(ValueSet.SP_URL, new UriParam(theUri));
IBundleProvider ids = search(params);
if (ids.size() == 0) {
throw new InvalidRequestException(Msg.code(947) + "Unknown ValueSet URI: " + theUri);
}
source = (ValueSet) ids.getResources(0, 1).get(0);
}
return expand(source, theOptions);
}
@Override
public List<IIdType> findCodeSystemIdsContainingSystemAndCode(String theCode, String theSystem, RequestDetails theRequest) {
if (theSystem != null && theSystem.startsWith("http://hl7.org/fhir/")) {
return Collections.singletonList(new IdDt(theSystem));
}
List<IIdType> valueSetIds;
List<ResourcePersistentId> ids = searchForIds(new SearchParameterMap(ValueSet.SP_CODE, new TokenParam(theSystem, theCode)), theRequest);
valueSetIds = new ArrayList<>();
for (ResourcePersistentId next : ids) {
IIdType id = myIdHelperService.translatePidIdToForcedId(myFhirContext, "ValueSet", next);
valueSetIds.add(id);
}
return valueSetIds;
}
private ValueSet loadValueSetForExpansion(IIdType theId, RequestDetails theRequest) {
if (theId.getValue().startsWith("http://hl7.org/fhir/")) {
org.hl7.fhir.dstu2.model.ValueSet valueSet = myValidationSupport.fetchResource(org.hl7.fhir.dstu2.model.ValueSet.class, theId.getValue());
if (valueSet != null) {
return getContext().newJsonParser().parseResource(ValueSet.class, getRiCtx().newJsonParser().encodeResourceToString(valueSet));
}
}
BaseHasResource sourceEntity = readEntity(theId, theRequest);
if (sourceEntity == null) {
throw new ResourceNotFoundException(Msg.code(2002) + theId);
}
ValueSet source = (ValueSet) toResource(sourceEntity, false);
return source;
}
private FhirContext getRiCtx() {
if (ourRiCtx == null) {
ourRiCtx = FhirContext.forDstu2Hl7Org();
}
return ourRiCtx;
}
private IValidationSupport.LookupCodeResult lookup(List<ExpansionContains> theContains, String theSystem, String theCode) {
for (ExpansionContains nextCode : theContains) {
String system = nextCode.getSystem();
String code = nextCode.getCode();
if (theSystem.equals(system) && theCode.equals(code)) {
IValidationSupport.LookupCodeResult retVal = new IValidationSupport.LookupCodeResult();
retVal.setSearchedForCode(code);
retVal.setSearchedForSystem(system);
retVal.setFound(true);
if (nextCode.getAbstract() != null) {
retVal.setCodeIsAbstract(nextCode.getAbstract());
}
retVal.setCodeDisplay(nextCode.getDisplay());
retVal.setCodeSystemVersion(nextCode.getVersion());
retVal.setCodeSystemDisplayName("Unknown"); // TODO: implement
return retVal;
}
}
return null;
}
@Nonnull
@Override
@Transactional
public IValidationSupport.LookupCodeResult lookupCode(IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, CodingDt theCoding, RequestDetails theRequest) {
return lookupCode(theCode, theSystem, theCoding, null, theRequest);
}
@Nonnull
@Override
@Transactional
public IValidationSupport.LookupCodeResult lookupCode(IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, CodingDt theCoding, IPrimitiveType<String> theDisplayLanguage, RequestDetails theRequest) {
boolean haveCoding = theCoding != null && isNotBlank(theCoding.getSystem()) && isNotBlank(theCoding.getCode());
boolean haveCode = theCode != null && theCode.isEmpty() == false;
boolean haveSystem = theSystem != null && theSystem.isEmpty() == false;
if (!haveCoding && !(haveSystem && haveCode)) {
throw new InvalidRequestException(Msg.code(949) + "No code, coding, or codeableConcept provided to validate");
}
if (!multiXor(haveCoding, (haveSystem && haveCode)) || (haveSystem != haveCode)) {
throw new InvalidRequestException(Msg.code(950) + "$lookup can only validate (system AND code) OR (coding.system AND coding.code)");
}
String code;
String system;
if (haveCoding) {
code = theCoding.getCode();
system = theCoding.getSystem();
} else {
code = theCode.getValue();
system = theSystem.getValue();
}
List<IIdType> valueSetIds = findCodeSystemIdsContainingSystemAndCode(code, system, theRequest);
for (IIdType nextId : valueSetIds) {
ValueSet expansion = expand(nextId, null, theRequest);
List<ExpansionContains> contains = expansion.getExpansion().getContains();
IValidationSupport.LookupCodeResult result = lookup(contains, system, code);
if (result != null) {
return result;
}
}
IValidationSupport.LookupCodeResult retVal = new IValidationSupport.LookupCodeResult();
retVal.setFound(false);
retVal.setSearchedForCode(code);
retVal.setSearchedForSystem(system);
return retVal;
}
@Override
public SubsumesResult subsumes(IPrimitiveType<String> theCodeA, IPrimitiveType<String> theCodeB, IPrimitiveType<String> theSystem, CodingDt theCodingA, CodingDt theCodingB, RequestDetails theRequestDetails) {
return myTerminologySvc.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB);
}
@Override
@PostConstruct
public void postConstruct() {
super.postConstruct();
myDefaultProfileValidationSupport = new DefaultProfileValidationSupport(myFhirContext);
myValidationSupport = new CachingValidationSupport(new ValidationSupportChain(myDefaultProfileValidationSupport, myJpaValidationSupport));
}
@Override
public void purgeCaches() {
// nothing
}
@Override
public IValidationSupport.CodeValidationResult validateCode(IPrimitiveType<String> theValueSetIdentifier, IIdType theId, IPrimitiveType<String> theCode,
IPrimitiveType<String> theSystem, IPrimitiveType<String> theDisplay, CodingDt theCoding, CodeableConceptDt theCodeableConcept, RequestDetails theRequest) {
return myTerminologySvc.validateCode(vsValidateCodeOptions(), theId, toStringOrNull(theValueSetIdentifier), toStringOrNull(theSystem), toStringOrNull(theCode), toStringOrNull(theDisplay), theCoding, theCodeableConcept);
}
@Override
public CodeValidationResult validateCode(IIdType theCodeSystemId, IPrimitiveType<String> theCodeSystemUrl, IPrimitiveType<String> theVersion, IPrimitiveType<String> theCode,
IPrimitiveType<String> theDisplay, CodingDt theCoding, CodeableConceptDt theCodeableConcept, RequestDetails theRequestDetails) {
throw new UnsupportedOperationException(Msg.code(951));
}
public static String toStringOrNull(IPrimitiveType<String> thePrimitive) {
return thePrimitive != null ? thePrimitive.getValue() : null;
}
}

View File

@ -63,7 +63,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle, MetaDt> {
@Override
public IBaseBundle processMessage(RequestDetails theRequestDetails, IBaseBundle theMessage) {
return FhirResourceDaoMessageHeaderDstu2.throwProcessMessageNotImplemented();
return JpaResourceDao.throwProcessMessageNotImplemented();
}
}

View File

@ -20,7 +20,6 @@ package ca.uhn.fhir.jpa.dao;
* #L%
*/
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.support.IValidationSupport;
@ -36,7 +35,6 @@ import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.param.UriParam;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.apache.commons.lang3.Validate;
@ -52,12 +50,11 @@ import org.hl7.fhir.r4.model.ValueSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Nullable;
import javax.annotation.PostConstruct;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
@ -89,8 +86,11 @@ public class JpaPersistedResourceValidationSupport implements IValidationSupport
private Class<? extends IBaseResource> myCodeSystemType;
private Class<? extends IBaseResource> myStructureDefinitionType;
private Class<? extends IBaseResource> myValueSetType;
private Class<? extends IBaseResource> myQuestionnaireType;
private Class<? extends IBaseResource> myImplementationGuideType;
// TODO: JA2 We shouldn't need to cache here, but we probably still should since the
// TermReadSvcImpl calls these methods as a part of its "isCodeSystemSupported" calls.
// We should modify CachingValidationSupport to cache the results of "isXXXSupported"
// at which point we could do away with this cache
private Cache<String, IBaseResource> myLoadCache = Caffeine.newBuilder().maximumSize(1000).expireAfterWrite(1, TimeUnit.MINUTES).build();
/**
@ -109,7 +109,7 @@ public class JpaPersistedResourceValidationSupport implements IValidationSupport
public IBaseResource fetchCodeSystem(String theSystem) {
if (TermReadSvcUtil.isLoincUnversionedCodeSystem(theSystem)) {
Optional<IBaseResource> currentCSOpt = getCodeSystemCurrentVersion(new UriType(theSystem));
if (! currentCSOpt.isPresent()) {
if (!currentCSOpt.isPresent()) {
ourLog.info("Couldn't find current version of CodeSystem: " + theSystem);
}
return currentCSOpt.orElse(null);
@ -123,7 +123,7 @@ public class JpaPersistedResourceValidationSupport implements IValidationSupport
* version is always pointed by the ForcedId for the no-versioned CS
*/
private Optional<IBaseResource> getCodeSystemCurrentVersion(UriType theUrl) {
if (! theUrl.getValueAsString().contains(LOINC_LOW)) return Optional.empty();
if (!theUrl.getValueAsString().contains(LOINC_LOW)) return Optional.empty();
return myTermReadSvc.readCodeSystemByForcedId(LOINC_LOW);
}
@ -145,7 +145,7 @@ public class JpaPersistedResourceValidationSupport implements IValidationSupport
*/
private Optional<IBaseResource> getValueSetCurrentVersion(UriType theUrl) {
Optional<String> vsIdOpt = TermReadSvcUtil.getValueSetId(theUrl.getValueAsString());
if (! vsIdOpt.isPresent()) return Optional.empty();
if (!vsIdOpt.isPresent()) return Optional.empty();
IFhirResourceDao<? extends IBaseResource> valueSetResourceDao = myDaoRegistry.getResourceDao(myValueSetType);
IBaseResource valueSet = valueSetResourceDao.read(new IdDt("ValueSet", vsIdOpt.get()));
@ -188,17 +188,8 @@ public class JpaPersistedResourceValidationSupport implements IValidationSupport
private <T extends IBaseResource> IBaseResource doFetchResource(@Nullable Class<T> theClass, String theUri) {
if (theClass == null) {
Supplier<IBaseResource>[] fetchers = new Supplier[]{
() -> doFetchResource(ValueSet.class, theUri),
() -> doFetchResource(CodeSystem.class, theUri),
() -> doFetchResource(StructureDefinition.class, theUri)
};
return Arrays
.stream(fetchers)
.map(t -> t.get())
.filter(t -> t != myNoMatch)
.findFirst()
.orElse(myNoMatch);
Supplier<IBaseResource>[] fetchers = new Supplier[]{() -> doFetchResource(ValueSet.class, theUri), () -> doFetchResource(CodeSystem.class, theUri), () -> doFetchResource(StructureDefinition.class, theUri)};
return Arrays.stream(fetchers).map(t -> t.get()).filter(t -> t != myNoMatch).findFirst().orElse(myNoMatch);
}
IdType id = new IdType(theUri);
@ -234,6 +225,20 @@ public class JpaPersistedResourceValidationSupport implements IValidationSupport
}
params.setSort(new SortSpec("_lastUpdated").setOrder(SortOrderEnum.DESC));
search = myDaoRegistry.getResourceDao(resourceName).search(params);
if (search.isEmpty() && myFhirContext.getVersion().getVersion().isOlderThan(FhirVersionEnum.DSTU3)) {
params = new SearchParameterMap();
params.setLoadSynchronousUpTo(1);
if (versionSeparator != -1) {
params.add(ValueSet.SP_VERSION, new TokenParam(theUri.substring(versionSeparator + 1)));
params.add("system", new UriParam(theUri.substring(0, versionSeparator)));
} else {
params.add("system", new UriParam(theUri));
}
params.setSort(new SortSpec("_lastUpdated").setOrder(SortOrderEnum.DESC));
search = myDaoRegistry.getResourceDao(resourceName).search(params);
}
}
break;
case "StructureDefinition": {
@ -312,8 +317,6 @@ public class JpaPersistedResourceValidationSupport implements IValidationSupport
public void start() {
myStructureDefinitionType = myFhirContext.getResourceDefinition("StructureDefinition").getImplementingClass();
myValueSetType = myFhirContext.getResourceDefinition("ValueSet").getImplementingClass();
myQuestionnaireType = myFhirContext.getResourceDefinition("Questionnaire").getImplementingClass();
myImplementationGuideType = myFhirContext.getResourceDefinition("ImplementationGuide").getImplementingClass();
if (myFhirContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU2)) {
myCodeSystemType = myFhirContext.getResourceDefinition("CodeSystem").getImplementingClass();

View File

@ -20,6 +20,9 @@ package ca.uhn.fhir.jpa.dao;
* #L%
*/
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseResource;
/**
@ -28,6 +31,15 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
*/
public class JpaResourceDao<T extends IBaseResource> extends BaseHapiFhirResourceDao<T> {
// nothing yet
/**
* Constructor
*/
public JpaResourceDao() {
super();
}
public static IBaseBundle throwProcessMessageNotImplemented() {
throw new NotImplementedOperationException(Msg.code(945) + "This operation is not yet implemented on this server");
}
}

View File

@ -0,0 +1,290 @@
package ca.uhn.fhir.jpa.dao;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2022 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.support.ConceptValidationOptions;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.IValidationSupport.CodeValidationResult;
import ca.uhn.fhir.context.support.ValidationSupportContext;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem;
import ca.uhn.fhir.jpa.api.svc.IIdHelperService;
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc;
import ca.uhn.fhir.jpa.util.LogicUtil;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.util.FhirTerser;
import ca.uhn.hapi.converters.canonical.VersionCanonicalizer;
import org.hl7.fhir.common.hapi.validation.support.CommonCodeSystemsTerminologyService;
import org.hl7.fhir.instance.model.api.IBaseCoding;
import org.hl7.fhir.instance.model.api.IBaseDatatype;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4.model.CodeableConcept;
import org.hl7.fhir.r4.model.Coding;
import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.Nonnull;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import static ca.uhn.fhir.util.DatatypeUtil.toStringValue;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class JpaResourceDaoCodeSystem<T extends IBaseResource> extends BaseHapiFhirResourceDao<T> implements IFhirResourceDaoCodeSystem<T> {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JpaResourceDaoCodeSystem.class);
@Autowired
protected ITermCodeSystemStorageSvc myTerminologyCodeSystemStorageSvc;
@Autowired
protected IIdHelperService myIdHelperService;
@Autowired
protected ITermDeferredStorageSvc myTermDeferredStorageSvc;
@Autowired
private IValidationSupport myValidationSupport;
@Autowired
private FhirContext myFhirContext;
private FhirTerser myTerser;
@Autowired
private VersionCanonicalizer myVersionCanonicalizer;
@Override
@PostConstruct
public void start() {
super.start();
myTerser = myFhirContext.newTerser();
}
@Override
public List<IIdType> findCodeSystemIdsContainingSystemAndCode(String theCode, String theSystem, RequestDetails theRequest) {
List<IIdType> valueSetIds;
List<ResourcePersistentId> ids = searchForIds(new SearchParameterMap(org.hl7.fhir.r4.model.CodeSystem.SP_CODE, new TokenParam(theSystem, theCode)), theRequest);
valueSetIds = new ArrayList<>();
for (ResourcePersistentId next : ids) {
IIdType id = myIdHelperService.translatePidIdToForcedId(myFhirContext, "CodeSystem", next);
valueSetIds.add(id);
}
return valueSetIds;
}
@Nonnull
@Override
public IValidationSupport.LookupCodeResult lookupCode(IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, IBaseCoding theCoding, RequestDetails theRequestDetails) {
return lookupCode(theCode, theSystem, theCoding, null, theRequestDetails);
}
@Nonnull
@Override
public IValidationSupport.LookupCodeResult lookupCode(IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, IBaseCoding theCoding, IPrimitiveType<String> theDisplayLanguage, RequestDetails theRequestDetails) {
return doLookupCode(myFhirContext, myTerser, myValidationSupport, theCode, theSystem, theCoding, theDisplayLanguage);
}
@Override
public SubsumesResult subsumes(IPrimitiveType<String> theCodeA, IPrimitiveType<String> theCodeB, IPrimitiveType<String> theSystem, IBaseCoding theCodingA, IBaseCoding theCodingB, RequestDetails theRequestDetails) {
return myTerminologySvc.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB);
}
@Override
protected void preDelete(T theResourceToDelete, ResourceTable theEntityToDelete, RequestDetails theRequestDetails) {
super.preDelete(theResourceToDelete, theEntityToDelete, theRequestDetails);
myTermDeferredStorageSvc.deleteCodeSystemForResource(theEntityToDelete);
}
@Override
public ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing, boolean theUpdateVersion, TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theTransactionDetails, theForceUpdate, theCreateNewHistoryEntry);
if (!retVal.isUnchangedInCurrentOperation()) {
org.hl7.fhir.r4.model.CodeSystem cs = myVersionCanonicalizer.codeSystemToCanonical(theResource);
addPidToResource(theEntity, cs);
myTerminologyCodeSystemStorageSvc.storeNewCodeSystemVersionIfNeeded(cs, (ResourceTable) theEntity, theRequest);
}
return retVal;
}
@Nonnull
@Override
public CodeValidationResult validateCode(IIdType theCodeSystemId, IPrimitiveType<String> theCodeSystemUrl, IPrimitiveType<String> theVersion, IPrimitiveType<String> theCode, IPrimitiveType<String> theDisplay, IBaseCoding theCoding, IBaseDatatype theCodeableConcept, RequestDetails theRequestDetails) {
CodeableConcept codeableConcept = myVersionCanonicalizer.codeableConceptToCanonical(theCodeableConcept);
boolean haveCodeableConcept = codeableConcept != null && codeableConcept.getCoding().size() > 0;
Coding coding = myVersionCanonicalizer.codingToCanonical(theCoding);
boolean haveCoding = coding != null && !coding.isEmpty();
String code = toStringValue(theCode);
boolean haveCode = isNotBlank(code);
if (!haveCodeableConcept && !haveCoding && !haveCode) {
throw new InvalidRequestException(Msg.code(906) + "No code, coding, or codeableConcept provided to validate.");
}
if (!LogicUtil.multiXor(haveCodeableConcept, haveCoding, haveCode)) {
throw new InvalidRequestException(Msg.code(907) + "$validate-code can only validate (code) OR (coding) OR (codeableConcept)");
}
String codeSystemUrl;
if (theCodeSystemId != null) {
IBaseResource codeSystem = read(theCodeSystemId, theRequestDetails);
codeSystemUrl = CommonCodeSystemsTerminologyService.getCodeSystemUrl(codeSystem);
} else if (isNotBlank(toStringValue(theCodeSystemUrl))) {
codeSystemUrl = toStringValue(theCodeSystemUrl);
} else {
throw new InvalidRequestException(Msg.code(908) + "Either CodeSystem ID or CodeSystem identifier must be provided. Unable to validate.");
}
if (haveCodeableConcept) {
CodeValidationResult anyValidation = null;
for (int i = 0; i < codeableConcept.getCoding().size(); i++) {
Coding nextCoding = codeableConcept.getCoding().get(i);
if (nextCoding.hasSystem()) {
if (!codeSystemUrl.equalsIgnoreCase(nextCoding.getSystem())) {
throw new InvalidRequestException(Msg.code(909) + "Coding.system '" + nextCoding.getSystem() + "' does not equal with CodeSystem.url '" + codeSystemUrl + "'. Unable to validate.");
}
codeSystemUrl = nextCoding.getSystem();
}
code = nextCoding.getCode();
String display = nextCoding.getDisplay();
CodeValidationResult nextValidation = codeSystemValidateCode(codeSystemUrl, toStringValue(theVersion), code, display);
anyValidation = nextValidation;
if (nextValidation.isOk()) {
return nextValidation;
}
}
return anyValidation;
} else if (haveCoding) {
if (coding.hasSystem()) {
if (!codeSystemUrl.equalsIgnoreCase(coding.getSystem())) {
throw new InvalidRequestException(Msg.code(910) + "Coding.system '" + coding.getSystem() + "' does not equal with CodeSystem.url '" + codeSystemUrl + "'. Unable to validate.");
}
codeSystemUrl = coding.getSystem();
}
code = coding.getCode();
String display = coding.getDisplay();
return codeSystemValidateCode(codeSystemUrl, toStringValue(theVersion), code, display);
} else {
String display = toStringValue(theDisplay);
return codeSystemValidateCode(codeSystemUrl, toStringValue(theVersion), code, display);
}
}
private CodeValidationResult codeSystemValidateCode(String theCodeSystemUrl, String theVersion, String theCode, String theDisplay) {
ValidationSupportContext context = new ValidationSupportContext(myValidationSupport);
ConceptValidationOptions options = new ConceptValidationOptions();
options.setValidateDisplay(isNotBlank(theDisplay));
String codeSystemUrl = createVersionedSystemIfVersionIsPresent(theCodeSystemUrl, theVersion);
CodeValidationResult retVal = myValidationSupport.validateCode(context, options, codeSystemUrl, theCode, theDisplay, null);
if (retVal == null) {
retVal = new CodeValidationResult();
retVal.setMessage("Terminology service was unable to provide validation for " + codeSystemUrl + "#" + theCode);
}
return retVal;
}
public static IValidationSupport.LookupCodeResult doLookupCode(FhirContext theFhirContext, FhirTerser theFhirTerser, IValidationSupport theValidationSupport, IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, IBaseCoding theCoding, IPrimitiveType<String> theDisplayLanguage) {
boolean haveCoding = theCoding != null && isNotBlank(extractCodingSystem(theCoding)) && isNotBlank(extractCodingCode(theCoding));
boolean haveCode = theCode != null && theCode.isEmpty() == false;
boolean haveSystem = theSystem != null && theSystem.isEmpty() == false;
boolean haveDisplayLanguage = theDisplayLanguage != null && theDisplayLanguage.isEmpty() == false;
if (!haveCoding && !(haveSystem && haveCode)) {
throw new InvalidRequestException(Msg.code(1126) + "No code, coding, or codeableConcept provided to validate");
}
if (!LogicUtil.multiXor(haveCoding, (haveSystem && haveCode)) || (haveSystem != haveCode)) {
throw new InvalidRequestException(Msg.code(1127) + "$lookup can only validate (system AND code) OR (coding.system AND coding.code)");
}
String code;
String system;
if (haveCoding) {
code = extractCodingCode(theCoding);
system = extractCodingSystem(theCoding);
String version = extractCodingVersion(theFhirContext, theFhirTerser, theCoding);
if (isNotBlank(version)) {
system = system + "|" + version;
}
} else {
code = theCode.getValue();
system = theSystem.getValue();
}
String displayLanguage = null;
if (haveDisplayLanguage) {
displayLanguage = theDisplayLanguage.getValue();
}
ourLog.info("Looking up {} / {}", system, code);
if (theValidationSupport.isCodeSystemSupported(new ValidationSupportContext(theValidationSupport), system)) {
ourLog.info("Code system {} is supported", system);
IValidationSupport.LookupCodeResult retVal = theValidationSupport.lookupCode(new ValidationSupportContext(theValidationSupport), system, code, displayLanguage);
if (retVal != null) {
return retVal;
}
}
// We didn't find it..
return IValidationSupport.LookupCodeResult.notFound(system, code);
}
private static String extractCodingSystem(IBaseCoding theCoding) {
return theCoding.getSystem();
}
private static String extractCodingCode(IBaseCoding theCoding) {
return theCoding.getCode();
}
private static String extractCodingVersion(FhirContext theFhirContext, FhirTerser theFhirTerser, IBaseCoding theCoding) {
if (theFhirContext.getVersion().getVersion().isOlderThan(FhirVersionEnum.DSTU3)) {
return null;
}
return theFhirTerser.getSinglePrimitiveValueOrNull(theCoding, "version");
}
public static String createVersionedSystemIfVersionIsPresent(String theCodeSystemUrl, String theVersion) {
String codeSystemUrl = theCodeSystemUrl;
if (isNotBlank(theVersion)) {
codeSystemUrl = codeSystemUrl + "|" + theVersion;
}
return codeSystemUrl;
}
}

View File

@ -0,0 +1,252 @@
package ca.uhn.fhir.jpa.dao;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2022 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.support.ConceptValidationOptions;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.ValidationSupportContext;
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoValueSet;
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.search.autocomplete.ValueSetAutocompleteOptions;
import ca.uhn.fhir.jpa.util.LogicUtil;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.util.ParametersUtil;
import ca.uhn.hapi.converters.canonical.VersionCanonicalizer;
import org.hl7.fhir.common.hapi.validation.support.CommonCodeSystemsTerminologyService;
import org.hl7.fhir.instance.model.api.IBaseCoding;
import org.hl7.fhir.instance.model.api.IBaseDatatype;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4.model.CodeableConcept;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.ValueSet;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Date;
import static ca.uhn.fhir.jpa.dao.JpaResourceDaoCodeSystem.createVersionedSystemIfVersionIsPresent;
import static ca.uhn.fhir.jpa.provider.ValueSetOperationProvider.createValueSetExpansionOptions;
import static ca.uhn.fhir.util.DatatypeUtil.toStringValue;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class JpaResourceDaoValueSet<T extends IBaseResource> extends BaseHapiFhirResourceDao<T> implements IFhirResourceDaoValueSet<T> {
@Autowired
private IValidationSupport myValidationSupport;
@Autowired
private VersionCanonicalizer myVersionCanonicalizer;
@Autowired(required = false)
private IFulltextSearchSvc myFulltextSearch;
@Override
public T expand(IIdType theId, ValueSetExpansionOptions theOptions, RequestDetails theRequestDetails) {
T source = read(theId, theRequestDetails);
return expand(source, theOptions);
}
@SuppressWarnings("unchecked")
@Override
public T expandByIdentifier(String theUri, ValueSetExpansionOptions theOptions) {
IValidationSupport.ValueSetExpansionOutcome expansionOutcome = myValidationSupport.expandValueSet(new ValidationSupportContext(myValidationSupport), theOptions, theUri);
return extractValueSetOrThrowException(expansionOutcome);
}
@Override
public T expand(T theSource, ValueSetExpansionOptions theOptions) {
IValidationSupport.ValueSetExpansionOutcome expansionOutcome = myValidationSupport.expandValueSet(new ValidationSupportContext(myValidationSupport), theOptions, theSource);
return extractValueSetOrThrowException(expansionOutcome);
}
@Override
public T expand(IIdType theId, T theValueSet, IPrimitiveType<String> theUrl, IPrimitiveType<String> theValueSetVersion, IPrimitiveType<String> theFilter, IPrimitiveType<String> theContext, IPrimitiveType<String> theContextDirection, IPrimitiveType<Integer> theOffset, IPrimitiveType<Integer> theCount, IPrimitiveType<String> theDisplayLanguage, IPrimitiveType<Boolean> theIncludeHierarchy, RequestDetails theRequestDetails) {
boolean haveId = theId != null && theId.hasIdPart();
boolean haveIdentifier = theUrl != null && isNotBlank(theUrl.getValue());
boolean haveValueSet = theValueSet != null && !theValueSet.isEmpty();
boolean haveValueSetVersion = theValueSetVersion != null && !theValueSetVersion.isEmpty();
boolean haveContextDirection = theContextDirection != null && !theContextDirection.isEmpty();
boolean haveContext = theContext != null && !theContext.isEmpty();
boolean isAutocompleteExtension = haveContext && haveContextDirection && "existing".equals(theContextDirection.getValue());
if (isAutocompleteExtension) {
// this is a funky extension for NIH. Do our own thing and return.
ValueSetAutocompleteOptions options = ValueSetAutocompleteOptions.validateAndParseOptions(myDaoConfig, theContext, theFilter, theCount, theId, theUrl, theValueSet);
if (myFulltextSearch == null || myFulltextSearch.isDisabled()) {
throw new InvalidRequestException(Msg.code(2083) + " Autocomplete is not supported on this server, as the fulltext search service is not configured.");
} else {
return (T) myFulltextSearch.tokenAutocompleteValueSetSearch(options);
}
}
if (!haveId && !haveIdentifier && !haveValueSet) {
if (myFhirContext.getVersion().getVersion() == FhirVersionEnum.DSTU2) {
// "url" parameter is called "identifier" in DSTU2
throw new InvalidRequestException(Msg.code(1130) + "$expand operation at the type level (no ID specified) requires an identifier or a valueSet as a part of the request");
}
throw new InvalidRequestException(Msg.code(1133) + "$expand operation at the type level (no ID specified) requires a url or a valueSet as a part of the request.");
}
if (!LogicUtil.multiXor(haveId, haveIdentifier, haveValueSet)) {
if (myFhirContext.getVersion().getVersion() == FhirVersionEnum.DSTU2) {
// "url" parameter is called "identifier" in DSTU2
throw new InvalidRequestException(Msg.code(1131) + "$expand must EITHER be invoked at the type level, or have an identifier specified, or have a ValueSet specified. Can not combine these options.");
}
throw new InvalidRequestException(Msg.code(1134) + "$expand must EITHER be invoked at the instance level, or have a url specified, or have a ValueSet specified. Can not combine these options.");
}
ValueSetExpansionOptions options = createValueSetExpansionOptions(myDaoConfig, theOffset, theCount, theIncludeHierarchy, theFilter, theDisplayLanguage);
IValidationSupport.ValueSetExpansionOutcome outcome;
if (haveId) {
IBaseResource valueSet = read(theId, theRequestDetails);
outcome = myValidationSupport.expandValueSet(new ValidationSupportContext(myValidationSupport), options, valueSet);
} else if (haveIdentifier) {
String url;
if (haveValueSetVersion) {
url = theUrl.getValue() + "|" + theValueSetVersion.getValue();
} else {
url = theUrl.getValue();
}
outcome = myValidationSupport.expandValueSet(new ValidationSupportContext(myValidationSupport), options, url);
} else {
outcome = myValidationSupport.expandValueSet(new ValidationSupportContext(myValidationSupport), options, theValueSet);
}
return extractValueSetOrThrowException(outcome);
}
@SuppressWarnings("unchecked")
private T extractValueSetOrThrowException(IValidationSupport.ValueSetExpansionOutcome outcome) {
if (outcome == null) {
throw new InternalErrorException(Msg.code(2028) + "No validation support module was able to expand the given valueset");
}
if (outcome.getError() != null) {
throw new PreconditionFailedException(Msg.code(2029) + outcome.getError());
}
return (T) outcome.getValueSet();
}
@Override
public IValidationSupport.CodeValidationResult validateCode(IPrimitiveType<String> theValueSetIdentifier, IIdType theValueSetId, IPrimitiveType<String> theCode,
IPrimitiveType<String> theSystem, IPrimitiveType<String> theDisplay, IBaseCoding theCoding,
IBaseDatatype theCodeableConcept, RequestDetails theRequestDetails) {
CodeableConcept codeableConcept = myVersionCanonicalizer.codeableConceptToCanonical(theCodeableConcept);
boolean haveCodeableConcept = codeableConcept != null && codeableConcept.getCoding().size() > 0;
Coding canonicalCodingToValidate = myVersionCanonicalizer.codingToCanonical((IBaseCoding) theCoding);
boolean haveCoding = canonicalCodingToValidate != null && !canonicalCodingToValidate.isEmpty();
boolean haveCode = theCode != null && !theCode.isEmpty();
if (!haveCodeableConcept && !haveCoding && !haveCode) {
throw new InvalidRequestException(Msg.code(899) + "No code, coding, or codeableConcept provided to validate");
}
if (!LogicUtil.multiXor(haveCodeableConcept, haveCoding, haveCode)) {
throw new InvalidRequestException(Msg.code(900) + "$validate-code can only validate (system AND code) OR (coding) OR (codeableConcept)");
}
String valueSetIdentifier;
if (theValueSetId != null) {
IBaseResource valueSet = read(theValueSetId, theRequestDetails);
StringBuilder valueSetIdentifierBuilder = new StringBuilder(CommonCodeSystemsTerminologyService.getValueSetUrl(valueSet));
String valueSetVersion = CommonCodeSystemsTerminologyService.getValueSetVersion(valueSet);
if (valueSetVersion != null) {
valueSetIdentifierBuilder.append("|").append(valueSetVersion);
}
valueSetIdentifier = valueSetIdentifierBuilder.toString();
} else if (isNotBlank(toStringValue(theValueSetIdentifier))) {
valueSetIdentifier = toStringValue(theValueSetIdentifier);
} else {
throw new InvalidRequestException(Msg.code(901) + "Either ValueSet ID or ValueSet identifier or system and code must be provided. Unable to validate.");
}
if (haveCodeableConcept) {
IValidationSupport.CodeValidationResult anyValidation = null;
for (int i = 0; i < codeableConcept.getCoding().size(); i++) {
Coding nextCoding = codeableConcept.getCoding().get(i);
String system = createVersionedSystemIfVersionIsPresent(nextCoding.getSystem(), nextCoding.getVersion());
String code = nextCoding.getCode();
String display = nextCoding.getDisplay();
IValidationSupport.CodeValidationResult nextValidation = validateCode(system, code, display, valueSetIdentifier);
anyValidation = nextValidation;
if (nextValidation.isOk()) {
return nextValidation;
}
}
return anyValidation;
} else if (haveCoding) {
String system = createVersionedSystemIfVersionIsPresent(canonicalCodingToValidate.getSystem(), canonicalCodingToValidate.getVersion());
String code = canonicalCodingToValidate.getCode();
String display = canonicalCodingToValidate.getDisplay();
return validateCode(system, code, display, valueSetIdentifier);
} else {
String system = toStringValue(theSystem);
String code = toStringValue(theCode);
String display = toStringValue(theDisplay);
return validateCode(system, code, display, valueSetIdentifier);
}
}
private IValidationSupport.CodeValidationResult validateCode(String theSystem, String theCode, String theDisplay, String theValueSetIdentifier) {
ValidationSupportContext context = new ValidationSupportContext(myValidationSupport);
ConceptValidationOptions options = new ConceptValidationOptions();
options.setValidateDisplay(isNotBlank(theDisplay));
IValidationSupport.CodeValidationResult result = myValidationSupport.validateCode(context, options, theSystem, theCode, theDisplay, theValueSetIdentifier);
if (result == null) {
result = new IValidationSupport.CodeValidationResult();
result.setMessage("Validator is unable to provide validation for " + theCode + "#" + theSystem + " - Unknown or unusable ValueSet[" + theValueSetIdentifier + "]");
}
return result;
}
@Override
public ResourceTable updateEntity(RequestDetails theRequestDetails, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theTransactionDetails, theForceUpdate, theCreateNewHistoryEntry);
if (getConfig().isPreExpandValueSets() && !retVal.isUnchangedInCurrentOperation()) {
if (retVal.getDeleted() == null) {
ValueSet valueSet = myVersionCanonicalizer.valueSetToCanonical(theResource);
myTerminologySvc.storeTermValueSet(retVal, valueSet);
} else {
myTerminologySvc.deleteValueSetAndChildren(retVal);
}
}
return retVal;
}
}

View File

@ -47,11 +47,11 @@ public interface ITermConceptDao extends JpaRepository<TermConcept, Long>, IHapi
@Query("SELECT COUNT(t) FROM TermConcept t WHERE t.myCodeSystem.myId = :cs_pid")
Integer countByCodeSystemVersion(@Param("cs_pid") Long thePid);
@Query("SELECT c FROM TermConcept c WHERE c.myCodeSystem = :code_system AND c.myCode = :code")
Optional<TermConcept> findByCodeSystemAndCode(@Param("code_system") TermCodeSystemVersion theCodeSystem, @Param("code") String theCode);
@Query("SELECT c FROM TermConcept c WHERE c.myCodeSystemVersionPid = :csv_pid AND c.myCode = :code")
Optional<TermConcept> findByCodeSystemAndCode(@Param("csv_pid") Long theCodeSystemVersionPid, @Param("code") String theCode);
@Query("FROM TermConcept WHERE myCodeSystem = :code_system AND myCode in (:codeList)")
List<TermConcept> findByCodeSystemAndCodeList(@Param("code_system") TermCodeSystemVersion theCodeSystem, @Param("codeList") List<String> theCodeList);
@Query("FROM TermConcept WHERE myCodeSystemVersionPid = :csv_pid AND myCode in (:codeList)")
List<TermConcept> findByCodeSystemAndCodeList(@Param("csv_pid") Long theCodeSystem, @Param("codeList") List<String> theCodeList);
@Modifying
@Query("DELETE FROM TermConcept WHERE myCodeSystem.myId = :cs_pid")

View File

@ -1,179 +0,0 @@
package ca.uhn.fhir.jpa.dao.dstu3;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2022 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.IValidationSupport.CodeValidationResult;
import ca.uhn.fhir.context.support.ValidationSupportContext;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc;
import ca.uhn.fhir.jpa.util.LogicUtil;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_30_40;
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_30_40;
import org.hl7.fhir.dstu3.model.CodeSystem;
import org.hl7.fhir.dstu3.model.CodeableConcept;
import org.hl7.fhir.dstu3.model.Coding;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.Nonnull;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import static ca.uhn.fhir.jpa.dao.FhirResourceDaoValueSetDstu2.toStringOrNull;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
@Transactional
public class FhirResourceDaoCodeSystemDstu3 extends BaseHapiFhirResourceDao<CodeSystem> implements IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoCodeSystemDstu3.class);
@Autowired
protected ITermCodeSystemStorageSvc myTerminologyCodeSystemStorageSvc;
@Autowired
protected ITermDeferredStorageSvc myTermDeferredStorageSvc;
@Autowired
private IValidationSupport myValidationSupport;
@Autowired
private FhirContext myFhirContext;
public FhirResourceDaoCodeSystemDstu3() {
super();
}
@Override
public List<IIdType> findCodeSystemIdsContainingSystemAndCode(String theCode, String theSystem, RequestDetails theRequest) {
List<ResourcePersistentId> ids = searchForIds(new SearchParameterMap(CodeSystem.SP_CODE, new TokenParam(theSystem, theCode)), theRequest);
List<IIdType> valueSetIds = new ArrayList<>();
for (ResourcePersistentId next : ids) {
IIdType id = myIdHelperService.translatePidIdToForcedId(myFhirContext, "CodeSystem", next);
valueSetIds.add(id);
}
return valueSetIds;
}
@Nonnull
@Override
public IValidationSupport.LookupCodeResult lookupCode(IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, Coding theCoding, RequestDetails theRequestDetails) {
return lookupCode(theCode, theSystem, theCoding, null, theRequestDetails);
}
@Nonnull
@Override
public IValidationSupport.LookupCodeResult lookupCode(IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, Coding theCoding, IPrimitiveType<String> theDisplayLanguage, RequestDetails theRequestDetails) {
boolean haveCoding = theCoding != null && isNotBlank(theCoding.getSystem()) && isNotBlank(theCoding.getCode());
boolean haveCode = theCode != null && theCode.isEmpty() == false;
boolean haveSystem = theSystem != null && theSystem.isEmpty() == false;
boolean haveDisplayLanguage = theDisplayLanguage != null && theDisplayLanguage.isEmpty() == false;
if (!haveCoding && !(haveSystem && haveCode)) {
throw new InvalidRequestException(Msg.code(1075) + "No code, coding, or codeableConcept provided to validate");
}
if (!LogicUtil.multiXor(haveCoding, (haveSystem && haveCode)) || (haveSystem != haveCode)) {
throw new InvalidRequestException(Msg.code(1076) + "$lookup can only validate (system AND code) OR (coding.system AND coding.code)");
}
String code;
String system;
if (haveCoding) {
code = theCoding.getCode();
if (theCoding.hasVersion()) {
system = theCoding.getSystem() + "|" + theCoding.getVersion();
} else {
system = theCoding.getSystem();
}
} else {
code = theCode.getValue();
system = theSystem.getValue();
}
String displayLanguage = null;
if (haveDisplayLanguage) {
displayLanguage = theDisplayLanguage.getValue();
}
ourLog.debug("Looking up {} / {}", system, code);
if (myValidationSupport.isCodeSystemSupported(new ValidationSupportContext(myValidationSupport), system)) {
ourLog.debug("Code system {} is supported", system);
IValidationSupport.LookupCodeResult result = myValidationSupport.lookupCode(new ValidationSupportContext(myValidationSupport), system, code, displayLanguage);
if (result != null) {
return result;
}
}
// We didn't find it..
return IValidationSupport.LookupCodeResult.notFound(system, code);
}
@Override
public SubsumesResult subsumes(IPrimitiveType<String> theCodeA, IPrimitiveType<String> theCodeB, IPrimitiveType<String> theSystem, Coding theCodingA, Coding theCodingB, RequestDetails theRequestDetails) {
return myTerminologySvc.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB);
}
@Override
protected void preDelete(CodeSystem theResourceToDelete, ResourceTable theEntityToDelete, RequestDetails theRequestDetails) {
super.preDelete(theResourceToDelete, theEntityToDelete, theRequestDetails);
myTermDeferredStorageSvc.deleteCodeSystemForResource(theEntityToDelete);
}
@Override
public ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theTransactionDetails, theForceUpdate, theCreateNewHistoryEntry);
if (!retVal.isUnchangedInCurrentOperation()) {
CodeSystem csDstu3 = (CodeSystem) theResource;
org.hl7.fhir.r4.model.CodeSystem cs = (org.hl7.fhir.r4.model.CodeSystem) VersionConvertorFactory_30_40.convertResource(csDstu3, new BaseAdvisor_30_40(false));
addPidToResource(theEntity, cs);
myTerminologyCodeSystemStorageSvc.storeNewCodeSystemVersionIfNeeded(cs, (ResourceTable) theEntity);
}
return retVal;
}
@Override
public CodeValidationResult validateCode(IIdType theCodeSystemId, IPrimitiveType<String> theCodeSystemUrl, IPrimitiveType<String> theVersion, IPrimitiveType<String> theCode,
IPrimitiveType<String> theDisplay, Coding theCoding, CodeableConcept theCodeableConcept, RequestDetails theRequestDetails) {
return myTerminologySvc.codeSystemValidateCode(theCodeSystemId, toStringOrNull(theCodeSystemUrl), toStringOrNull(theVersion), toStringOrNull(theCode), toStringOrNull(theDisplay), theCoding, theCodeableConcept);
}
}

View File

@ -1,29 +0,0 @@
package ca.uhn.fhir.jpa.dao.dstu3;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2022 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoMessageHeader;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
import org.hl7.fhir.dstu3.model.MessageHeader;
public class FhirResourceDaoMessageHeaderDstu3 extends BaseHapiFhirResourceDao<MessageHeader> implements IFhirResourceDaoMessageHeader<MessageHeader> {
// nothing right now
}

View File

@ -1,107 +0,0 @@
package ca.uhn.fhir.jpa.dao.dstu3;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2022 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.context.support.ConceptValidationOptions;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoValueSet;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_30_40;
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_30_40;
import org.hl7.fhir.dstu3.model.CodeableConcept;
import org.hl7.fhir.dstu3.model.Coding;
import org.hl7.fhir.dstu3.model.ValueSet;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import java.util.Date;
import static ca.uhn.fhir.jpa.dao.FhirResourceDaoValueSetDstu2.toStringOrNull;
public class FhirResourceDaoValueSetDstu3 extends BaseHapiFhirResourceDao<ValueSet> implements IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> {
@Override
public org.hl7.fhir.dstu3.model.ValueSet expand(IIdType theId, ValueSetExpansionOptions theOptions, RequestDetails theRequestDetails) {
org.hl7.fhir.dstu3.model.ValueSet source = read(theId, theRequestDetails);
return expand(source, theOptions);
}
@Override
public org.hl7.fhir.dstu3.model.ValueSet expand(org.hl7.fhir.dstu3.model.ValueSet theSource, ValueSetExpansionOptions theOptions) {
org.hl7.fhir.r4.model.ValueSet canonicalInput = (org.hl7.fhir.r4.model.ValueSet) VersionConvertorFactory_30_40.convertResource(theSource, new BaseAdvisor_30_40(false));
org.hl7.fhir.r4.model.ValueSet canonicalOutput = myTerminologySvc.expandValueSet(theOptions, canonicalInput);
return (ValueSet) VersionConvertorFactory_30_40.convertResource(canonicalOutput, new BaseAdvisor_30_40(false));
}
@Override
public org.hl7.fhir.dstu3.model.ValueSet expandByIdentifier(String theUri, ValueSetExpansionOptions theOptions) {
org.hl7.fhir.r4.model.ValueSet canonicalOutput = myTerminologySvc.expandValueSet(theOptions, theUri);
return (ValueSet) VersionConvertorFactory_30_40.convertResource(canonicalOutput, new BaseAdvisor_30_40(false));
}
@Override
public IValidationSupport.CodeValidationResult validateCode(IPrimitiveType<String> theValueSetIdentifier, IIdType theId, IPrimitiveType<String> theCode,
IPrimitiveType<String> theSystem, IPrimitiveType<String> theDisplay, Coding theCoding,
CodeableConcept theCodeableConcept, RequestDetails theRequestDetails) {
return myTerminologySvc.validateCode(vsValidateCodeOptions(), theId, toStringOrNull(theValueSetIdentifier), toStringOrNull(theSystem), toStringOrNull(theCode), toStringOrNull(theDisplay), theCoding, theCodeableConcept);
}
@Override
public void purgeCaches() {
// nothing
}
@Override
public ResourceTable updateEntity(RequestDetails theRequestDetails, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theTransactionDetails, theForceUpdate, theCreateNewHistoryEntry);
if (getConfig().isPreExpandValueSets() && !retVal.isUnchangedInCurrentOperation()) {
if (retVal.getDeleted() == null) {
try {
ValueSet valueSet = (ValueSet) theResource;
org.hl7.fhir.r4.model.ValueSet converted = (org.hl7.fhir.r4.model.ValueSet) VersionConvertorFactory_30_40.convertResource(valueSet, new BaseAdvisor_30_40(false));
myTerminologySvc.storeTermValueSet(retVal, converted);
} catch (FHIRException fe) {
throw new InternalErrorException(Msg.code(1080) + fe);
}
} else {
myTerminologySvc.deleteValueSetAndChildren(retVal);
}
}
return retVal;
}
public static ConceptValidationOptions vsValidateCodeOptions() {
return new ConceptValidationOptions().setValidateDisplay(true);
}
}

View File

@ -21,9 +21,8 @@ package ca.uhn.fhir.jpa.dao.dstu3;
*/
import ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao;
import ca.uhn.fhir.jpa.dao.FhirResourceDaoMessageHeaderDstu2;
import ca.uhn.fhir.jpa.dao.JpaResourceDao;
import ca.uhn.fhir.jpa.model.entity.TagDefinition;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.dstu3.model.Meta;
@ -71,7 +70,7 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
@Override
public IBaseBundle processMessage(RequestDetails theRequestDetails, IBaseBundle theMessage) {
return FhirResourceDaoMessageHeaderDstu2.throwProcessMessageNotImplemented();
return JpaResourceDao.throwProcessMessageNotImplemented();
}

View File

@ -1,192 +0,0 @@
package ca.uhn.fhir.jpa.dao.r4;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2022 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.IValidationSupport.CodeValidationResult;
import ca.uhn.fhir.context.support.ValidationSupportContext;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem;
import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc;
import ca.uhn.fhir.jpa.util.LogicUtil;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.CodeableConcept;
import org.hl7.fhir.r4.model.Coding;
import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.Nonnull;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import static ca.uhn.fhir.jpa.dao.FhirResourceDaoValueSetDstu2.toStringOrNull;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.hl7.fhir.common.hapi.validation.support.ValidationConstants.LOINC_LOW;
public class FhirResourceDaoCodeSystemR4 extends BaseHapiFhirResourceDao<CodeSystem> implements IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoCodeSystemR4.class);
@Autowired
protected ITermCodeSystemStorageSvc myTerminologyCodeSystemStorageSvc;
@Autowired
protected ITermDeferredStorageSvc myTermDeferredStorageSvc;
@Autowired
private IValidationSupport myValidationSupport;
@Autowired
private FhirContext myFhirContext;
@Override
public List<IIdType> findCodeSystemIdsContainingSystemAndCode(String theCode, String theSystem, RequestDetails theRequest) {
List<IIdType> valueSetIds;
List<ResourcePersistentId> ids = searchForIds(new SearchParameterMap(CodeSystem.SP_CODE, new TokenParam(theSystem, theCode)), theRequest);
valueSetIds = new ArrayList<>();
for (ResourcePersistentId next : ids) {
IIdType id = myIdHelperService.translatePidIdToForcedId(myFhirContext, "CodeSystem", next);
valueSetIds.add(id);
}
return valueSetIds;
}
@Nonnull
@Override
public IValidationSupport.LookupCodeResult lookupCode(IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, Coding theCoding, RequestDetails theRequestDetails) {
return lookupCode(theCode, theSystem, theCoding, null, theRequestDetails);
}
@Nonnull
@Override
public IValidationSupport.LookupCodeResult lookupCode(IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, Coding theCoding, IPrimitiveType<String> theDisplayLanguage, RequestDetails theRequestDetails) {
boolean haveCoding = theCoding != null && isNotBlank(theCoding.getSystem()) && isNotBlank(theCoding.getCode());
boolean haveCode = theCode != null && theCode.isEmpty() == false;
boolean haveSystem = theSystem != null && theSystem.isEmpty() == false;
boolean haveDisplayLanguage = theDisplayLanguage != null && theDisplayLanguage.isEmpty() == false;
if (!haveCoding && !(haveSystem && haveCode)) {
throw new InvalidRequestException(Msg.code(1108) + "No code, coding, or codeableConcept provided to validate");
}
if (!LogicUtil.multiXor(haveCoding, (haveSystem && haveCode)) || (haveSystem != haveCode)) {
throw new InvalidRequestException(Msg.code(1109) + "$lookup can only validate (system AND code) OR (coding.system AND coding.code)");
}
String code;
String system;
if (haveCoding) {
code = theCoding.getCode();
if (theCoding.hasVersion()) {
system = theCoding.getSystem() + "|" + theCoding.getVersion();
} else {
system = theCoding.getSystem();
}
} else {
code = theCode.getValue();
system = theSystem.getValue();
}
String displayLanguage = null;
if (haveDisplayLanguage) {
displayLanguage = theDisplayLanguage.getValue();
}
ourLog.debug("Looking up {} / {}", system, code);
if (myValidationSupport.isCodeSystemSupported(new ValidationSupportContext(myValidationSupport), system)) {
ourLog.debug("Code system {} is supported", system);
IValidationSupport.LookupCodeResult retVal = myValidationSupport.lookupCode(new ValidationSupportContext(myValidationSupport), system, code, displayLanguage);
if (retVal != null) {
return retVal;
}
}
// We didn't find it..
return IValidationSupport.LookupCodeResult.notFound(system, code);
}
@Override
public SubsumesResult subsumes(IPrimitiveType<String> theCodeA, IPrimitiveType<String> theCodeB, IPrimitiveType<String> theSystem, Coding theCodingA, Coding theCodingB, RequestDetails theRequestDetails) {
return myTerminologySvc.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB);
}
@Override
protected void preDelete(CodeSystem theResourceToDelete, ResourceTable theEntityToDelete, RequestDetails theRequestDetails) {
super.preDelete(theResourceToDelete, theEntityToDelete, theRequestDetails);
myTermDeferredStorageSvc.deleteCodeSystemForResource(theEntityToDelete);
}
@Override
public ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theTransactionDetails, theForceUpdate, theCreateNewHistoryEntry);
if (!retVal.isUnchangedInCurrentOperation()) {
CodeSystem cs = (CodeSystem) theResource;
addPidToResource(theEntity, theResource);
myTerminologyCodeSystemStorageSvc.storeNewCodeSystemVersionIfNeeded(cs, (ResourceTable) theEntity, theRequest);
}
return retVal;
}
@Override
public CodeValidationResult validateCode(IIdType theCodeSystemId, IPrimitiveType<String> theCodeSystemUrl,
IPrimitiveType<String> theVersion, IPrimitiveType<String> theCode, IPrimitiveType<String> theDisplay,
Coding theCoding, CodeableConcept theCodeableConcept, RequestDetails theRequestDetails) {
return myTerminologySvc.codeSystemValidateCode(theCodeSystemId, toStringOrNull(theCodeSystemUrl), toStringOrNull(theVersion), toStringOrNull(theCode), toStringOrNull(theDisplay), theCoding, theCodeableConcept);
}
@Override
public DaoMethodOutcome create(CodeSystem theResource, String theIfNoneExist, boolean thePerformIndexing,
@Nonnull TransactionDetails theTransactionDetails, RequestDetails theRequestDetails) {
// loinc CodeSystem must have an ID
if (isNotBlank(theResource.getUrl()) && theResource.getUrl().contains(LOINC_LOW)
&& isBlank(theResource.getIdElement().getIdPart())) {
throw new InvalidParameterException(Msg.code(1110) + "'loinc' CodeSystem must have an ID");
}
return myTransactionService.execute(theRequestDetails, theTransactionDetails,
tx -> doCreateForPost(theResource, theIfNoneExist, thePerformIndexing, theTransactionDetails, theRequestDetails));
}
}

View File

@ -1,29 +0,0 @@
package ca.uhn.fhir.jpa.dao.r4;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2022 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoMessageHeader;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
import org.hl7.fhir.r4.model.MessageHeader;
public class FhirResourceDaoMessageHeaderR4 extends BaseHapiFhirResourceDao<MessageHeader> implements IFhirResourceDaoMessageHeader<MessageHeader> {
// nothing right now
}

View File

@ -1,93 +0,0 @@
package ca.uhn.fhir.jpa.dao.r4;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2022 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoValueSet;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4.model.CodeableConcept;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.ValueSet;
import java.util.Date;
import static ca.uhn.fhir.jpa.dao.FhirResourceDaoValueSetDstu2.toStringOrNull;
import static ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoValueSetDstu3.vsValidateCodeOptions;
public class FhirResourceDaoValueSetR4 extends BaseHapiFhirResourceDao<ValueSet> implements IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> {
@Override
public ValueSet expand(IIdType theId, ValueSetExpansionOptions theOptions, RequestDetails theRequestDetails) {
ValueSet source = read(theId, theRequestDetails);
return expand(source, theOptions);
}
@Override
public ValueSet expandByIdentifier(String theUri, ValueSetExpansionOptions theOptions) {
return myTerminologySvc.expandValueSet(theOptions, theUri);
}
@Override
public ValueSet expand(ValueSet theSource, ValueSetExpansionOptions theOptions) {
return myTerminologySvc.expandValueSet(theOptions, theSource);
}
@Override
public IValidationSupport.CodeValidationResult validateCode(IPrimitiveType<String> theValueSetIdentifier, IIdType theId, IPrimitiveType<String> theCode,
IPrimitiveType<String> theSystem, IPrimitiveType<String> theDisplay, Coding theCoding,
CodeableConcept theCodeableConcept, RequestDetails theRequestDetails) {
return myTerminologySvc.validateCode(vsValidateCodeOptions(), theId, toStringOrNull(theValueSetIdentifier), toStringOrNull(theSystem), toStringOrNull(theCode), toStringOrNull(theDisplay), theCoding, theCodeableConcept);
}
@Override
public void purgeCaches() {
// nothing
}
@Override
public ResourceTable updateEntity(RequestDetails theRequestDetails, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theTransactionDetails, theForceUpdate, theCreateNewHistoryEntry);
if (getConfig().isPreExpandValueSets() && !retVal.isUnchangedInCurrentOperation()) {
if (retVal.getDeleted() == null) {
ValueSet valueSet = (ValueSet) theResource;
myTerminologySvc.storeTermValueSet(retVal, valueSet);
} else {
myTerminologySvc.deleteValueSetAndChildren(retVal);
}
}
return retVal;
}
}

View File

@ -21,7 +21,7 @@ package ca.uhn.fhir.jpa.dao.r4;
*/
import ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao;
import ca.uhn.fhir.jpa.dao.FhirResourceDaoMessageHeaderDstu2;
import ca.uhn.fhir.jpa.dao.JpaResourceDao;
import ca.uhn.fhir.jpa.model.entity.TagDefinition;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.hl7.fhir.instance.model.api.IBaseBundle;
@ -45,7 +45,7 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
@Override
public IBaseBundle processMessage(RequestDetails theRequestDetails, IBaseBundle theMessage) {
return FhirResourceDaoMessageHeaderDstu2.throwProcessMessageNotImplemented();
return JpaResourceDao.throwProcessMessageNotImplemented();
}

View File

@ -1,183 +0,0 @@
package ca.uhn.fhir.jpa.dao.r4b;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2022 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.IValidationSupport.CodeValidationResult;
import ca.uhn.fhir.context.support.ValidationSupportContext;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem;
import ca.uhn.fhir.jpa.api.svc.IIdHelperService;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc;
import ca.uhn.fhir.jpa.util.LogicUtil;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_40_50;
import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_43_50;
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50;
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_43_50;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4b.model.CodeSystem;
import org.hl7.fhir.r4b.model.CodeableConcept;
import org.hl7.fhir.r4b.model.Coding;
import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import static ca.uhn.fhir.jpa.dao.FhirResourceDaoValueSetDstu2.toStringOrNull;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class FhirResourceDaoCodeSystemR4B extends BaseHapiFhirResourceDao<CodeSystem> implements IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoCodeSystemR4B.class);
@Autowired
protected ITermCodeSystemStorageSvc myTerminologyCodeSystemStorageSvc;
@Autowired
protected IIdHelperService myIdHelperService;
@Autowired
protected ITermDeferredStorageSvc myTermDeferredStorageSvc;
@Autowired
private IValidationSupport myValidationSupport;
@Autowired
private FhirContext myFhirContext;
@Override
public List<IIdType> findCodeSystemIdsContainingSystemAndCode(String theCode, String theSystem, RequestDetails theRequest) {
List<IIdType> valueSetIds;
List<ResourcePersistentId> ids = searchForIds(new SearchParameterMap(org.hl7.fhir.r4.model.CodeSystem.SP_CODE, new TokenParam(theSystem, theCode)), theRequest);
valueSetIds = new ArrayList<>();
for (ResourcePersistentId next : ids) {
IIdType id = myIdHelperService.translatePidIdToForcedId(myFhirContext, "CodeSystem", next);
valueSetIds.add(id);
}
return valueSetIds;
}
@Nonnull
@Override
public IValidationSupport.LookupCodeResult lookupCode(IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, Coding theCoding, RequestDetails theRequestDetails) {
return lookupCode(theCode, theSystem, theCoding, null, theRequestDetails);
}
@Nonnull
@Override
public IValidationSupport.LookupCodeResult lookupCode(IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, Coding theCoding, IPrimitiveType<String> theDisplayLanguage, RequestDetails theRequestDetails) {
boolean haveCoding = theCoding != null && isNotBlank(theCoding.getSystem()) && isNotBlank(theCoding.getCode());
boolean haveCode = theCode != null && theCode.isEmpty() == false;
boolean haveSystem = theSystem != null && theSystem.isEmpty() == false;
boolean haveDisplayLanguage = theDisplayLanguage != null && theDisplayLanguage.isEmpty() == false;
if (!haveCoding && !(haveSystem && haveCode)) {
throw new InvalidRequestException(Msg.code(2148) + "No code, coding, or codeableConcept provided to validate");
}
if (!LogicUtil.multiXor(haveCoding, (haveSystem && haveCode)) || (haveSystem != haveCode)) {
throw new InvalidRequestException(Msg.code(2149) + "$lookup can only validate (system AND code) OR (coding.system AND coding.code)");
}
String code;
String system;
if (haveCoding) {
code = theCoding.getCode();
if (theCoding.hasVersion()) {
system = theCoding.getSystem() + "|" + theCoding.getVersion();
} else {
system = theCoding.getSystem();
}
} else {
code = theCode.getValue();
system = theSystem.getValue();
}
String displayLanguage = null;
if (haveDisplayLanguage) {
displayLanguage = theDisplayLanguage.getValue();
}
ourLog.info("Looking up {} / {}", system, code);
if (myValidationSupport.isCodeSystemSupported(new ValidationSupportContext(myValidationSupport), system)) {
ourLog.info("Code system {} is supported", system);
IValidationSupport.LookupCodeResult retVal = myValidationSupport.lookupCode(new ValidationSupportContext(myValidationSupport), system, code, displayLanguage);
if (retVal != null) {
return retVal;
}
}
// We didn't find it..
return IValidationSupport.LookupCodeResult.notFound(system, code);
}
@Override
public SubsumesResult subsumes(IPrimitiveType<String> theCodeA, IPrimitiveType<String> theCodeB, IPrimitiveType<String> theSystem, Coding theCodingA, Coding theCodingB, RequestDetails theRequestDetails) {
return myTerminologySvc.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB);
}
@Override
protected void preDelete(CodeSystem theResourceToDelete, ResourceTable theEntityToDelete, RequestDetails theRequestDetails) {
super.preDelete(theResourceToDelete, theEntityToDelete, theRequestDetails);
myTermDeferredStorageSvc.deleteCodeSystemForResource(theEntityToDelete);
}
@Override
public ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theTransactionDetails, theForceUpdate, theCreateNewHistoryEntry);
if (!retVal.isUnchangedInCurrentOperation()) {
CodeSystem csR4b = (CodeSystem) theResource;
org.hl7.fhir.r5.model.CodeSystem csR5 = (org.hl7.fhir.r5.model.CodeSystem) VersionConvertorFactory_43_50.convertResource(csR4b, new BaseAdvisor_43_50(false));
org.hl7.fhir.r4.model.CodeSystem csR4 = (org.hl7.fhir.r4.model.CodeSystem) VersionConvertorFactory_40_50.convertResource(csR5, new BaseAdvisor_40_50(false));
addPidToResource(theEntity, csR4);
myTerminologyCodeSystemStorageSvc.storeNewCodeSystemVersionIfNeeded(csR4, (ResourceTable) theEntity);
}
return retVal;
}
@Override
public CodeValidationResult validateCode(IIdType theCodeSystemId, IPrimitiveType<String> theCodeSystemUrl,
IPrimitiveType<String> theVersion, IPrimitiveType<String> theCode, IPrimitiveType<String> theDisplay,
Coding theCoding, CodeableConcept theCodeableConcept, RequestDetails theRequestDetails) {
return myTerminologySvc.codeSystemValidateCode(theCodeSystemId, toStringOrNull(theCodeSystemUrl), toStringOrNull(theVersion), toStringOrNull(theCode), toStringOrNull(theDisplay), theCoding, theCodeableConcept);
}
}

View File

@ -1,29 +0,0 @@
package ca.uhn.fhir.jpa.dao.r4b;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2022 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoMessageHeader;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
import org.hl7.fhir.r4b.model.MessageHeader;
public class FhirResourceDaoMessageHeaderR4B extends BaseHapiFhirResourceDao<MessageHeader> implements IFhirResourceDaoMessageHeader<MessageHeader> {
// nothing right now
}

View File

@ -1,102 +0,0 @@
package ca.uhn.fhir.jpa.dao.r4b;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2022 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoValueSet;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_40_50;
import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_43_50;
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50;
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_43_50;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4b.model.CodeableConcept;
import org.hl7.fhir.r4b.model.Coding;
import org.hl7.fhir.r4b.model.ValueSet;
import java.util.Date;
import static ca.uhn.fhir.jpa.dao.FhirResourceDaoValueSetDstu2.toStringOrNull;
import static ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoValueSetDstu3.vsValidateCodeOptions;
public class FhirResourceDaoValueSetR4B extends BaseHapiFhirResourceDao<ValueSet> implements IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> {
@Override
public ValueSet expand(IIdType theId, ValueSetExpansionOptions theOptions, RequestDetails theRequestDetails) {
ValueSet source = read(theId, theRequestDetails);
return expand(source, theOptions);
}
@Override
public ValueSet expandByIdentifier(String theUri, ValueSetExpansionOptions theOptions) {
org.hl7.fhir.r4.model.ValueSet canonicalOutput = myTerminologySvc.expandValueSet(theOptions, theUri);
org.hl7.fhir.r5.model.ValueSet valueSetR5 = (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_40_50.convertResource(canonicalOutput, new BaseAdvisor_40_50(false));
return (ValueSet) VersionConvertorFactory_43_50.convertResource(valueSetR5, new BaseAdvisor_43_50(false));
}
@Override
public ValueSet expand(ValueSet theSource, ValueSetExpansionOptions theOptions) {
org.hl7.fhir.r5.model.ValueSet canonicalInputR5 = (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_43_50.convertResource(theSource, new BaseAdvisor_43_50(false));
org.hl7.fhir.r4.model.ValueSet canonicalInput = (org.hl7.fhir.r4.model.ValueSet) VersionConvertorFactory_40_50.convertResource(canonicalInputR5, new BaseAdvisor_40_50(false));
org.hl7.fhir.r4.model.ValueSet canonicalOutput = myTerminologySvc.expandValueSet(theOptions, canonicalInput);
org.hl7.fhir.r5.model.ValueSet outputR5 = (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_40_50.convertResource(canonicalOutput, new BaseAdvisor_40_50(false));
return (ValueSet) VersionConvertorFactory_43_50.convertResource(outputR5, new BaseAdvisor_43_50(false));
}
@Override
public IValidationSupport.CodeValidationResult validateCode(IPrimitiveType<String> theValueSetIdentifier, IIdType theId, IPrimitiveType<String> theCode,
IPrimitiveType<String> theSystem, IPrimitiveType<String> theDisplay, Coding theCoding,
CodeableConcept theCodeableConcept, RequestDetails theRequestDetails) {
return myTerminologySvc.validateCode(vsValidateCodeOptions(), theId, toStringOrNull(theValueSetIdentifier), toStringOrNull(theSystem), toStringOrNull(theCode), toStringOrNull(theDisplay), theCoding, theCodeableConcept);
}
@Override
public void purgeCaches() {
// nothing
}
@Override
public ResourceTable updateEntity(RequestDetails theRequestDetails, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theTransactionDetails, theForceUpdate, theCreateNewHistoryEntry);
if (getConfig().isPreExpandValueSets() && !retVal.isUnchangedInCurrentOperation()) {
if (retVal.getDeleted() == null) {
ValueSet valueSet = (ValueSet) theResource;
org.hl7.fhir.r5.model.ValueSet valueSetR5 = (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_43_50.convertResource(valueSet, new BaseAdvisor_43_50(false));
org.hl7.fhir.r4.model.ValueSet valueSetR4 = (org.hl7.fhir.r4.model.ValueSet) VersionConvertorFactory_40_50.convertResource(valueSetR5, new BaseAdvisor_40_50(false));
myTerminologySvc.storeTermValueSet(retVal, valueSetR4);
} else {
myTerminologySvc.deleteValueSetAndChildren(retVal);
}
}
return retVal;
}
}

View File

@ -21,7 +21,7 @@ package ca.uhn.fhir.jpa.dao.r4b;
*/
import ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao;
import ca.uhn.fhir.jpa.dao.FhirResourceDaoMessageHeaderDstu2;
import ca.uhn.fhir.jpa.dao.JpaResourceDao;
import ca.uhn.fhir.jpa.model.entity.TagDefinition;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.hl7.fhir.instance.model.api.IBaseBundle;
@ -47,7 +47,7 @@ public class FhirSystemDaoR4B extends BaseHapiFhirSystemDao<Bundle, Meta> {
@Override
public IBaseBundle processMessage(RequestDetails theRequestDetails, IBaseBundle theMessage) {
return FhirResourceDaoMessageHeaderDstu2.throwProcessMessageNotImplemented();
return JpaResourceDao.throwProcessMessageNotImplemented();
}

View File

@ -1,180 +0,0 @@
package ca.uhn.fhir.jpa.dao.r5;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2022 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.IValidationSupport.CodeValidationResult;
import ca.uhn.fhir.context.support.ValidationSupportContext;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem;
import ca.uhn.fhir.jpa.api.svc.IIdHelperService;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc;
import ca.uhn.fhir.jpa.util.LogicUtil;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_40_50;
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.CodeableConcept;
import org.hl7.fhir.r5.model.Coding;
import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import static ca.uhn.fhir.jpa.dao.FhirResourceDaoValueSetDstu2.toStringOrNull;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class FhirResourceDaoCodeSystemR5 extends BaseHapiFhirResourceDao<CodeSystem> implements IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoCodeSystemR5.class);
@Autowired
protected ITermCodeSystemStorageSvc myTerminologyCodeSystemStorageSvc;
@Autowired
protected IIdHelperService myIdHelperService;
@Autowired
protected ITermDeferredStorageSvc myTermDeferredStorageSvc;
@Autowired
private IValidationSupport myValidationSupport;
@Autowired
private FhirContext myFhirContext;
@Override
public List<IIdType> findCodeSystemIdsContainingSystemAndCode(String theCode, String theSystem, RequestDetails theRequest) {
List<IIdType> valueSetIds;
List<ResourcePersistentId> ids = searchForIds(new SearchParameterMap(org.hl7.fhir.r4.model.CodeSystem.SP_CODE, new TokenParam(theSystem, theCode)), theRequest);
valueSetIds = new ArrayList<>();
for (ResourcePersistentId next : ids) {
IIdType id = myIdHelperService.translatePidIdToForcedId(myFhirContext, "CodeSystem", next);
valueSetIds.add(id);
}
return valueSetIds;
}
@Nonnull
@Override
public IValidationSupport.LookupCodeResult lookupCode(IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, Coding theCoding, RequestDetails theRequestDetails) {
return lookupCode(theCode, theSystem, theCoding, null, theRequestDetails);
}
@Nonnull
@Override
public IValidationSupport.LookupCodeResult lookupCode(IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, Coding theCoding, IPrimitiveType<String> theDisplayLanguage, RequestDetails theRequestDetails) {
boolean haveCoding = theCoding != null && isNotBlank(theCoding.getSystem()) && isNotBlank(theCoding.getCode());
boolean haveCode = theCode != null && theCode.isEmpty() == false;
boolean haveSystem = theSystem != null && theSystem.isEmpty() == false;
boolean haveDisplayLanguage = theDisplayLanguage != null && theDisplayLanguage.isEmpty() == false;
if (!haveCoding && !(haveSystem && haveCode)) {
throw new InvalidRequestException(Msg.code(1126) + "No code, coding, or codeableConcept provided to validate");
}
if (!LogicUtil.multiXor(haveCoding, (haveSystem && haveCode)) || (haveSystem != haveCode)) {
throw new InvalidRequestException(Msg.code(1127) + "$lookup can only validate (system AND code) OR (coding.system AND coding.code)");
}
String code;
String system;
if (haveCoding) {
code = theCoding.getCode();
if (theCoding.hasVersion()) {
system = theCoding.getSystem() + "|" + theCoding.getVersion();
} else {
system = theCoding.getSystem();
}
} else {
code = theCode.getValue();
system = theSystem.getValue();
}
String displayLanguage = null;
if (haveDisplayLanguage) {
displayLanguage = theDisplayLanguage.getValue();
}
ourLog.info("Looking up {} / {}", system, code);
if (myValidationSupport.isCodeSystemSupported(new ValidationSupportContext(myValidationSupport), system)) {
ourLog.info("Code system {} is supported", system);
IValidationSupport.LookupCodeResult retVal = myValidationSupport.lookupCode(new ValidationSupportContext(myValidationSupport), system, code, displayLanguage);
if (retVal != null) {
return retVal;
}
}
// We didn't find it..
return IValidationSupport.LookupCodeResult.notFound(system, code);
}
@Override
public SubsumesResult subsumes(IPrimitiveType<String> theCodeA, IPrimitiveType<String> theCodeB, IPrimitiveType<String> theSystem, Coding theCodingA, Coding theCodingB, RequestDetails theRequestDetails) {
return myTerminologySvc.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB);
}
@Override
protected void preDelete(CodeSystem theResourceToDelete, ResourceTable theEntityToDelete, RequestDetails theRequestDetails) {
super.preDelete(theResourceToDelete, theEntityToDelete, theRequestDetails);
myTermDeferredStorageSvc.deleteCodeSystemForResource(theEntityToDelete);
}
@Override
public ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theTransactionDetails, theForceUpdate, theCreateNewHistoryEntry);
if (!retVal.isUnchangedInCurrentOperation()) {
CodeSystem csR5 = (CodeSystem) theResource;
org.hl7.fhir.r4.model.CodeSystem cs = (org.hl7.fhir.r4.model.CodeSystem) VersionConvertorFactory_40_50.convertResource(csR5, new BaseAdvisor_40_50(false));
addPidToResource(theEntity, cs);
myTerminologyCodeSystemStorageSvc.storeNewCodeSystemVersionIfNeeded(cs, (ResourceTable) theEntity);
}
return retVal;
}
@Override
public CodeValidationResult validateCode(IIdType theCodeSystemId, IPrimitiveType<String> theCodeSystemUrl,
IPrimitiveType<String> theVersion, IPrimitiveType<String> theCode, IPrimitiveType<String> theDisplay,
Coding theCoding, CodeableConcept theCodeableConcept, RequestDetails theRequestDetails) {
return myTerminologySvc.codeSystemValidateCode(theCodeSystemId, toStringOrNull(theCodeSystemUrl), toStringOrNull(theVersion), toStringOrNull(theCode), toStringOrNull(theDisplay), theCoding, theCodeableConcept);
}
}

View File

@ -1,29 +0,0 @@
package ca.uhn.fhir.jpa.dao.r5;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2022 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoMessageHeader;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
import org.hl7.fhir.r5.model.MessageHeader;
public class FhirResourceDaoMessageHeaderR5 extends BaseHapiFhirResourceDao<MessageHeader> implements IFhirResourceDaoMessageHeader<MessageHeader> {
// nothing right now
}

View File

@ -1,95 +0,0 @@
package ca.uhn.fhir.jpa.dao.r5;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2022 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoValueSet;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_40_50;
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r5.model.CodeableConcept;
import org.hl7.fhir.r5.model.Coding;
import org.hl7.fhir.r5.model.ValueSet;
import java.util.Date;
import static ca.uhn.fhir.jpa.dao.FhirResourceDaoValueSetDstu2.toStringOrNull;
import static ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoValueSetDstu3.vsValidateCodeOptions;
public class FhirResourceDaoValueSetR5 extends BaseHapiFhirResourceDao<ValueSet> implements IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> {
@Override
public ValueSet expand(IIdType theId, ValueSetExpansionOptions theOptions, RequestDetails theRequestDetails) {
ValueSet source = read(theId, theRequestDetails);
return expand(source, theOptions);
}
@Override
public ValueSet expandByIdentifier(String theUri, ValueSetExpansionOptions theOptions) {
org.hl7.fhir.r4.model.ValueSet canonicalOutput = myTerminologySvc.expandValueSet(theOptions, theUri);
return (ValueSet) VersionConvertorFactory_40_50.convertResource(canonicalOutput, new BaseAdvisor_40_50(false));
}
@Override
public ValueSet expand(ValueSet theSource, ValueSetExpansionOptions theOptions) {
org.hl7.fhir.r4.model.ValueSet canonicalInput = (org.hl7.fhir.r4.model.ValueSet) VersionConvertorFactory_40_50.convertResource(theSource, new BaseAdvisor_40_50(false));
org.hl7.fhir.r4.model.ValueSet canonicalOutput = myTerminologySvc.expandValueSet(theOptions, canonicalInput);
return (ValueSet) VersionConvertorFactory_40_50.convertResource(canonicalOutput, new BaseAdvisor_40_50(false));
}
@Override
public IValidationSupport.CodeValidationResult validateCode(IPrimitiveType<String> theValueSetIdentifier, IIdType theId, IPrimitiveType<String> theCode,
IPrimitiveType<String> theSystem, IPrimitiveType<String> theDisplay, Coding theCoding,
CodeableConcept theCodeableConcept, RequestDetails theRequestDetails) {
return myTerminologySvc.validateCode(vsValidateCodeOptions(), theId, toStringOrNull(theValueSetIdentifier), toStringOrNull(theSystem), toStringOrNull(theCode), toStringOrNull(theDisplay), theCoding, theCodeableConcept);
}
@Override
public void purgeCaches() {
// nothing
}
@Override
public ResourceTable updateEntity(RequestDetails theRequestDetails, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theTransactionDetails, theForceUpdate, theCreateNewHistoryEntry);
if (getConfig().isPreExpandValueSets() && !retVal.isUnchangedInCurrentOperation()) {
if (retVal.getDeleted() == null) {
ValueSet valueSet = (ValueSet) theResource;
myTerminologySvc.storeTermValueSet(retVal, (org.hl7.fhir.r4.model.ValueSet) VersionConvertorFactory_40_50.convertResource(valueSet, new BaseAdvisor_40_50(false)));
} else {
myTerminologySvc.deleteValueSetAndChildren(retVal);
}
}
return retVal;
}
}

View File

@ -21,7 +21,7 @@ package ca.uhn.fhir.jpa.dao.r5;
*/
import ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao;
import ca.uhn.fhir.jpa.dao.FhirResourceDaoMessageHeaderDstu2;
import ca.uhn.fhir.jpa.dao.JpaResourceDao;
import ca.uhn.fhir.jpa.model.entity.TagDefinition;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.hl7.fhir.instance.model.api.IBaseBundle;
@ -47,7 +47,7 @@ public class FhirSystemDaoR5 extends BaseHapiFhirSystemDao<Bundle, Meta> {
@Override
public IBaseBundle processMessage(RequestDetails theRequestDetails, IBaseBundle theMessage) {
return FhirResourceDaoMessageHeaderDstu2.throwProcessMessageNotImplemented();
return JpaResourceDao.throwProcessMessageNotImplemented();
}

View File

@ -32,9 +32,8 @@ import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import org.hl7.fhir.common.hapi.validation.support.InMemoryTerminologyServerValidationSupport;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseCoding;
import org.hl7.fhir.instance.model.api.IBaseDatatype;
import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
@ -44,6 +43,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import static ca.uhn.fhir.jpa.provider.ValueSetOperationProvider.toValidateCodeResult;
import static ca.uhn.fhir.util.DatatypeUtil.toStringValue;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public abstract class BaseJpaResourceProviderCodeSystem<T extends IBaseResource> extends BaseJpaResourceProvider<T> {
@ -115,7 +116,7 @@ public abstract class BaseJpaResourceProviderCodeSystem<T extends IBaseResource>
}
}
private static void applyVersionToSystem(IPrimitiveType<String> theSystem, IPrimitiveType<String> theVersion) {
static void applyVersionToSystem(IPrimitiveType<String> theSystem, IPrimitiveType<String> theVersion) {
if (theVersion != null && isNotBlank(theVersion.getValueAsString()) && theSystem != null) {
theSystem.setValue(theSystem.getValueAsString() + "|" + theVersion.getValueAsString());
}
@ -138,7 +139,7 @@ public abstract class BaseJpaResourceProviderCodeSystem<T extends IBaseResource>
@OperationParam(name = "code", min = 0, max = 1, typeName = "code") IPrimitiveType<String> theCode,
@OperationParam(name = "display", min = 0, max = 1, typeName = "string") IPrimitiveType<String> theDisplay,
@OperationParam(name = "coding", min = 0, max = 1, typeName = "Coding") IBaseCoding theCoding,
@OperationParam(name = "codeableConcept", min = 0, max = 1, typeName = "CodeableConcept") IBase theCodeableConcept,
@OperationParam(name = "codeableConcept", min = 0, max = 1, typeName = "CodeableConcept") IBaseDatatype theCodeableConcept,
RequestDetails theRequestDetails
) {
@ -168,9 +169,9 @@ public abstract class BaseJpaResourceProviderCodeSystem<T extends IBaseResource>
} else {
// Otherwise, use the local DAO layer to validate the code
IFhirResourceDaoCodeSystem dao = (IFhirResourceDaoCodeSystem) getDao();
result = dao.validateCode(theId, theCodeSystemUrl, theVersion, theCode, theDisplay, theCoding, theCodeableConcept, theRequestDetails);
result = dao.validateCode(theId, theCodeSystemUrl, theVersion, theCode, theDisplay, theCoding, theCodeableConcept, theRequestDetails);
}
return BaseJpaResourceProviderValueSetDstu2.toValidateCodeResult(getContext(), result);
return toValidateCodeResult(getContext(), result);
} finally {
endRequest(theServletRequest);
}

View File

@ -34,7 +34,7 @@ import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.DateRangeParam;
public abstract class BaseJpaResourceProviderEncounterDstu2 extends JpaResourceProviderDstu2<Encounter> {
public abstract class BaseJpaResourceProviderEncounterDstu2 extends BaseJpaResourceProvider<Encounter> {
/**
* Encounter/123/$everything

View File

@ -1,189 +0,0 @@
package ca.uhn.fhir.jpa.provider;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2022 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoValueSet;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt;
import ca.uhn.fhir.model.dstu2.composite.CodingDt;
import ca.uhn.fhir.model.dstu2.resource.Parameters;
import ca.uhn.fhir.model.dstu2.resource.ValueSet;
import ca.uhn.fhir.model.primitive.BooleanDt;
import ca.uhn.fhir.model.primitive.CodeDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.util.ParametersUtil;
import org.hl7.fhir.instance.model.api.IBaseParameters;
import javax.servlet.http.HttpServletRequest;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public abstract class BaseJpaResourceProviderValueSetDstu2 extends JpaResourceProviderDstu2<ValueSet> {
@Operation(name = JpaConstants.OPERATION_EXPAND, idempotent = true)
public ValueSet expand(
HttpServletRequest theServletRequest,
@IdParam(optional = true) IdDt theId,
@OperationParam(name = "valueSet", min = 0, max = 1) ValueSet theValueSet,
@OperationParam(name = "identifier", min = 0, max = 1) UriDt theIdentifier,
@OperationParam(name = "filter", min = 0, max = 1) StringDt theFilter,
RequestDetails theRequestDetails) {
boolean haveId = theId != null && theId.hasIdPart();
boolean haveIdentifier = theIdentifier != null && isNotBlank(theIdentifier.getValue());
boolean haveValueSet = theValueSet != null && theValueSet.isEmpty() == false;
if (!haveId && !haveIdentifier && !haveValueSet) {
throw new InvalidRequestException(Msg.code(1130) + "$expand operation at the type level (no ID specified) requires an identifier or a valueSet as a part of the request");
}
if (moreThanOneTrue(haveId, haveIdentifier, haveValueSet)) {
throw new InvalidRequestException(Msg.code(1131) + "$expand must EITHER be invoked at the type level, or have an identifier specified, or have a ValueSet specified. Can not combine these options.");
}
startRequest(theServletRequest);
try {
IFhirResourceDaoValueSet<ValueSet, CodingDt, CodeableConceptDt> dao = (IFhirResourceDaoValueSet<ValueSet, CodingDt, CodeableConceptDt>) getDao();
if (haveId) {
return dao.expand(theId, toFilterString(theFilter), theRequestDetails);
} else if (haveIdentifier) {
return dao.expandByIdentifier(theIdentifier.getValue(), toFilterString(theFilter));
} else {
return dao.expand(theValueSet, toFilterString(theFilter));
}
} finally {
endRequest(theServletRequest);
}
}
private ValueSetExpansionOptions toFilterString(StringDt theFilter) {
if (theFilter != null) {
return ValueSetExpansionOptions.forOffsetAndCount(0, 1000).setFilter(theFilter.getValue());
}
return null;
}
@Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters = {
@OperationParam(name = "name", type = StringDt.class, min = 1),
@OperationParam(name = "version", type = StringDt.class, min = 0),
@OperationParam(name = "display", type = StringDt.class, min = 1),
@OperationParam(name = "abstract", type = BooleanDt.class, min = 1),
})
public Parameters lookup(
HttpServletRequest theServletRequest,
@OperationParam(name = "code", min = 0, max = 1) CodeDt theCode,
@OperationParam(name = "system", min = 0, max = 1) UriDt theSystem,
@OperationParam(name = "coding", min = 0, max = 1) CodingDt theCoding,
RequestDetails theRequestDetails
) {
startRequest(theServletRequest);
try {
IFhirResourceDaoCodeSystem<ValueSet, CodingDt, CodeableConceptDt> dao = (IFhirResourceDaoCodeSystem<ValueSet, CodingDt, CodeableConceptDt>) getDao();
IValidationSupport.LookupCodeResult result = dao.lookupCode(theCode, theSystem, theCoding, null, theRequestDetails);
if (result.isFound() == false) {
throw new ResourceNotFoundException(Msg.code(1132) + "Unable to find code[" + result.getSearchedForCode() + "] in system[" + result.getSearchedForSystem() + "]");
}
Parameters retVal = new Parameters();
retVal.addParameter().setName("name").setValue(new StringDt(result.getCodeSystemDisplayName()));
if (isNotBlank(result.getCodeSystemVersion())) {
retVal.addParameter().setName("version").setValue(new StringDt(result.getCodeSystemVersion()));
}
retVal.addParameter().setName("display").setValue(new StringDt(result.getCodeDisplay()));
retVal.addParameter().setName("abstract").setValue(new BooleanDt(result.isCodeIsAbstract()));
return retVal;
} finally {
endRequest(theServletRequest);
}
}
@Operation(name = JpaConstants.OPERATION_VALIDATE_CODE, idempotent = true, returnParameters = {
@OperationParam(name = "result", type = BooleanDt.class, min = 1),
@OperationParam(name = "message", type = StringDt.class),
@OperationParam(name = "display", type = StringDt.class)
})
public Parameters validateCode(
HttpServletRequest theServletRequest,
@IdParam(optional = true) IdDt theId,
@OperationParam(name = "identifier", min = 0, max = 1) UriDt theValueSetIdentifier,
@OperationParam(name = "code", min = 0, max = 1) CodeDt theCode,
@OperationParam(name = "system", min = 0, max = 1) UriDt theSystem,
@OperationParam(name = "display", min = 0, max = 1) StringDt theDisplay,
@OperationParam(name = "coding", min = 0, max = 1) CodingDt theCoding,
@OperationParam(name = "codeableConcept", min = 0, max = 1) CodeableConceptDt theCodeableConcept,
RequestDetails theRequestDetails
) {
startRequest(theServletRequest);
try {
IFhirResourceDaoValueSet<ValueSet, CodingDt, CodeableConceptDt> dao = (IFhirResourceDaoValueSet<ValueSet, CodingDt, CodeableConceptDt>) getDao();
IValidationSupport.CodeValidationResult result = dao.validateCode(theValueSetIdentifier, theId, theCode, theSystem, theDisplay, theCoding, theCodeableConcept, theRequestDetails);
return (Parameters) toValidateCodeResult(getContext(), result);
} finally {
endRequest(theServletRequest);
}
}
public static IBaseParameters toValidateCodeResult(FhirContext theContext, IValidationSupport.CodeValidationResult theResult) {
IBaseParameters retVal = ParametersUtil.newInstance(theContext);
ParametersUtil.addParameterToParametersBoolean(theContext, retVal, "result", theResult.isOk());
if (isNotBlank(theResult.getMessage())) {
ParametersUtil.addParameterToParametersString(theContext, retVal, "message", theResult.getMessage());
}
if (isNotBlank(theResult.getDisplay())) {
ParametersUtil.addParameterToParametersString(theContext, retVal, "display", theResult.getDisplay());
}
return retVal;
}
private static boolean moreThanOneTrue(boolean... theBooleans) {
boolean haveOne = false;
for (boolean next : theBooleans) {
if (next) {
if (haveOne) {
return true;
} else {
haveOne = true;
}
}
}
return false;
}
}

View File

@ -102,29 +102,6 @@ public final class JpaSystemProvider<T, MT> extends BaseJpaSystemProvider<T, MT>
return retVal;
}
/**
* $process-message
*/
@Description("Accept a FHIR Message Bundle for processing")
@Operation(name = JpaConstants.OPERATION_PROCESS_MESSAGE, idempotent = false)
public IBaseBundle processMessage(
HttpServletRequest theServletRequest,
RequestDetails theRequestDetails,
@OperationParam(name = "content", min = 1, max = 1, typeName = "Bundle")
@Description(formalDefinition = "The message to process (or, if using asynchronous messaging, it may be a response message to accept)")
IBaseBundle theMessageToProcess
) {
startRequest(theServletRequest);
try {
return getDao().processMessage(theRequestDetails, theMessageToProcess);
} finally {
endRequest(theServletRequest);
}
}
@Operation(name = JpaConstants.OPERATION_GET_RESOURCE_COUNTS, idempotent = true)
@Description(shortDefinition = "Provides the number of resources currently stored on the server, broken down by resource type")
public IBaseParameters getResourceCounts() {

View File

@ -0,0 +1,65 @@
package ca.uhn.fhir.jpa.provider;
/*-
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2022 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.springframework.beans.factory.annotation.Autowired;
import javax.servlet.http.HttpServletRequest;
import static ca.uhn.fhir.jpa.provider.BaseJpaProvider.endRequest;
import static ca.uhn.fhir.jpa.provider.BaseJpaProvider.startRequest;
public class ProcessMessageProvider {
@Autowired
private IFhirSystemDao<?, ?> mySystemDao;
/**
* $process-message
*/
@Description("Accept a FHIR Message Bundle for processing")
@Operation(name = JpaConstants.OPERATION_PROCESS_MESSAGE, idempotent = false)
public IBaseBundle processMessage(
HttpServletRequest theServletRequest,
RequestDetails theRequestDetails,
@OperationParam(name = "content", min = 1, max = 1, typeName = "Bundle")
@Description(shortDefinition = "The message to process (or, if using asynchronous messaging, it may be a response message to accept)")
IBaseBundle theMessageToProcess
) {
startRequest(theServletRequest);
try {
return mySystemDao.processMessage(theRequestDetails, theMessageToProcess);
} finally {
endRequest(theServletRequest);
}
}
}

View File

@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.provider;
* #L%
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.ConceptValidationOptions;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.ValidationSupportContext;
@ -29,20 +30,17 @@ import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoValueSet;
import ca.uhn.fhir.jpa.config.JpaConfig;
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.search.autocomplete.ValueSetAutocompleteOptions;
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
import ca.uhn.fhir.util.ParametersUtil;
import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain;
import org.hl7.fhir.instance.model.api.IBaseCoding;
import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.ICompositeType;
@ -55,12 +53,15 @@ import org.springframework.beans.factory.annotation.Qualifier;
import javax.servlet.http.HttpServletRequest;
import static ca.uhn.fhir.util.DatatypeUtil.toStringValue;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class ValueSetOperationProvider extends BaseJpaProvider {
private static final Logger ourLog = LoggerFactory.getLogger(ValueSetOperationProvider.class);
@Autowired
protected IValidationSupport myValidationSupport;
@Autowired
private DaoConfig myDaoConfig;
@Autowired
private DaoRegistry myDaoRegistry;
@ -69,10 +70,6 @@ public class ValueSetOperationProvider extends BaseJpaProvider {
@Autowired
@Qualifier(JpaConfig.JPA_VALIDATION_SUPPORT_CHAIN)
private ValidationSupportChain myValidationSupportChain;
@Autowired
private IValidationSupport myValidationSupport;
@Autowired(required = false)
private IFulltextSearchSvc myFulltextSearch;
public void setValidationSupport(IValidationSupport theValidationSupport) {
myValidationSupport = theValidationSupport;
@ -110,70 +107,10 @@ public class ValueSetOperationProvider extends BaseJpaProvider {
@OperationParam(name = JpaConstants.OPERATION_EXPAND_PARAM_INCLUDE_HIERARCHY, min = 0, max = 1, typeName = "boolean") IPrimitiveType<Boolean> theIncludeHierarchy,
RequestDetails theRequestDetails) {
boolean haveId = theId != null && theId.hasIdPart();
boolean haveIdentifier = theUrl != null && isNotBlank(theUrl.getValue());
boolean haveValueSet = theValueSet != null && !theValueSet.isEmpty();
boolean haveValueSetVersion = theValueSetVersion != null && !theValueSetVersion.isEmpty();
boolean haveContextDirection = theContextDirection != null && !theContextDirection.isEmpty();
boolean haveContext = theContext != null && !theContext.isEmpty();
boolean isAutocompleteExtension = haveContext && haveContextDirection && "existing".equals(theContextDirection.getValue());
if (isAutocompleteExtension) {
// this is a funky extension for NIH. Do our own thing and return.
ValueSetAutocompleteOptions options = ValueSetAutocompleteOptions.validateAndParseOptions(myDaoConfig, theContext, theFilter, theCount, theId, theUrl, theValueSet);
startRequest(theServletRequest);
try {
if (myFulltextSearch == null || myFulltextSearch.isDisabled()) {
throw new InvalidRequestException(Msg.code(2083) + " Autocomplete is not supported on this server, as the fulltext search service is not configured.");
} else {
return myFulltextSearch.tokenAutocompleteValueSetSearch(options);
}
} finally {
endRequest(theServletRequest);
}
}
if (!haveId && !haveIdentifier && !haveValueSet) {
throw new InvalidRequestException(Msg.code(1133) + "$expand operation at the type level (no ID specified) requires a url or a valueSet as a part of the request.");
}
if (moreThanOneTrue(haveId, haveIdentifier, haveValueSet)) {
throw new InvalidRequestException(Msg.code(1134) + "$expand must EITHER be invoked at the instance level, or have a url specified, or have a ValueSet specified. Can not combine these options.");
}
ValueSetExpansionOptions options = createValueSetExpansionOptions(myDaoConfig, theOffset, theCount, theIncludeHierarchy, theFilter, theDisplayLanguage);
startRequest(theServletRequest);
try {
IFhirResourceDaoValueSet<IBaseResource, ICompositeType, ICompositeType> dao = getDao();
IValidationSupport.ValueSetExpansionOutcome outcome;
if (haveId) {
IBaseResource valueSet = dao.read(theId, theRequestDetails);
outcome = myValidationSupport.expandValueSet(new ValidationSupportContext(myValidationSupport), options, valueSet);
} else if (haveIdentifier) {
String url;
if (haveValueSetVersion) {
url = theUrl.getValue() + "|" + theValueSetVersion.getValue();
} else {
url = theUrl.getValue();
}
outcome = myValidationSupport.expandValueSet(new ValidationSupportContext(myValidationSupport), options, url);
} else {
outcome = myValidationSupport.expandValueSet(new ValidationSupportContext(myValidationSupport), options, theValueSet);
}
if (outcome == null) {
throw new InternalErrorException(Msg.code(2028) + "No validation support module was able to expand the given valueset");
}
if (outcome.getError() != null) {
throw new PreconditionFailedException(Msg.code(2029) + outcome.getError());
}
return outcome.getValueSet();
return getDao().expand(theId, theValueSet, theUrl, theValueSetVersion, theFilter, theContext, theContextDirection, theOffset, theCount, theDisplayLanguage, theIncludeHierarchy, theRequestDetails);
} finally {
endRequest(theServletRequest);
@ -181,8 +118,8 @@ public class ValueSetOperationProvider extends BaseJpaProvider {
}
@SuppressWarnings("unchecked")
private IFhirResourceDaoValueSet<IBaseResource, ICompositeType, ICompositeType> getDao() {
return (IFhirResourceDaoValueSet<IBaseResource, ICompositeType, ICompositeType>) myDaoRegistry.getResourceDao("ValueSet");
protected IFhirResourceDaoValueSet<IBaseResource> getDao() {
return (IFhirResourceDaoValueSet<IBaseResource>) myDaoRegistry.getResourceDao("ValueSet");
}
@SuppressWarnings("unchecked")
@ -196,11 +133,11 @@ public class ValueSetOperationProvider extends BaseJpaProvider {
@IdParam(optional = true) IIdType theId,
@OperationParam(name = "url", min = 0, max = 1, typeName = "uri") IPrimitiveType<String> theValueSetUrl,
@OperationParam(name = "valueSetVersion", min = 0, max = 1, typeName = "string") IPrimitiveType<String> theValueSetVersion,
@OperationParam(name = "code", min = 0, max = 1) IPrimitiveType<String> theCode,
@OperationParam(name = "code", min = 0, max = 1, typeName = "code") IPrimitiveType<String> theCode,
@OperationParam(name = "system", min = 0, max = 1, typeName = "uri") IPrimitiveType<String> theSystem,
@OperationParam(name = "systemVersion", min = 0, max = 1, typeName = "string") IPrimitiveType<String> theSystemVersion,
@OperationParam(name = "display", min = 0, max = 1, typeName = "string") IPrimitiveType<String> theDisplay,
@OperationParam(name = "coding", min = 0, max = 1, typeName = "Coding") ICompositeType theCoding,
@OperationParam(name = "coding", min = 0, max = 1, typeName = "Coding") IBaseCoding theCoding,
@OperationParam(name = "codeableConcept", min = 0, max = 1, typeName = "CodeableConcept") ICompositeType theCodeableConcept,
RequestDetails theRequestDetails
) {
@ -219,7 +156,7 @@ public class ValueSetOperationProvider extends BaseJpaProvider {
new ConceptValidationOptions(), theSystemString, theCodeString, theDisplayString, theValueSetUrlString);
} else {
// Otherwise, use the local DAO layer to validate the code
IFhirResourceDaoValueSet<IBaseResource, ICompositeType, ICompositeType> dao = getDao();
IFhirResourceDaoValueSet<IBaseResource> dao = getDao();
IPrimitiveType<String> valueSetIdentifier;
if (theValueSetUrl != null && theValueSetVersion != null) {
valueSetIdentifier = (IPrimitiveType<String>) getContext().getElementDefinition("uri").newInstance();
@ -236,7 +173,7 @@ public class ValueSetOperationProvider extends BaseJpaProvider {
}
result = dao.validateCode(valueSetIdentifier, theId, theCode, codeSystemIdentifier, theDisplay, theCoding, theCodeableConcept, theRequestDetails);
}
return BaseJpaResourceProviderValueSetDstu2.toValidateCodeResult(getContext(), result);
return toValidateCodeResult(getContext(), result);
} finally {
endRequest(theServletRequest);
}
@ -298,25 +235,26 @@ public class ValueSetOperationProvider extends BaseJpaProvider {
options.setFilter(theFilter.getValue());
}
if( theDisplayLanguage != null ) {
if (theDisplayLanguage != null) {
options.setTheDisplayLanguage(theDisplayLanguage.getValue());
}
return options;
}
private static boolean moreThanOneTrue(boolean... theBooleans) {
boolean haveOne = false;
for (boolean next : theBooleans) {
if (next) {
if (haveOne) {
return true;
} else {
haveOne = true;
}
}
public static IBaseParameters toValidateCodeResult(FhirContext theContext, IValidationSupport.CodeValidationResult theResult) {
IBaseParameters retVal = ParametersUtil.newInstance(theContext);
ParametersUtil.addParameterToParametersBoolean(theContext, retVal, "result", theResult.isOk());
if (isNotBlank(theResult.getMessage())) {
ParametersUtil.addParameterToParametersString(theContext, retVal, "message", theResult.getMessage());
}
return false;
if (isNotBlank(theResult.getDisplay())) {
ParametersUtil.addParameterToParametersString(theContext, retVal, "display", theResult.getDisplay());
}
return retVal;
}
}

View File

@ -0,0 +1,120 @@
package ca.uhn.fhir.jpa.provider;
/*-
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2022 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.jpa.dao.JpaResourceDaoCodeSystem;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.util.FhirTerser;
import org.hl7.fhir.instance.model.api.IBaseCoding;
import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.springframework.beans.factory.annotation.Autowired;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import static ca.uhn.fhir.jpa.provider.BaseJpaResourceProviderCodeSystem.applyVersionToSystem;
public class ValueSetOperationProviderDstu2 extends ValueSetOperationProvider {
@Autowired
private IValidationSupport myValidationSupport;
/**
* Alternate expand implementation since DSTU2 uses "identifier" instead of "url" as a parameter.
*/
@Operation(name = JpaConstants.OPERATION_EXPAND, idempotent = true, typeName = "ValueSet")
public IBaseResource expand(
HttpServletRequest theServletRequest,
@IdParam(optional = true) IIdType theId,
@OperationParam(name = "valueSet", min = 0, max = 1) IBaseResource theValueSet,
@OperationParam(name = "url", min = 0, max = 1, typeName = "uri") IPrimitiveType<String> theUrl,
@OperationParam(name = "identifier", min = 0, max = 1, typeName = "uri") IPrimitiveType<String> theIdentifier,
@OperationParam(name = "valueSetVersion", min = 0, max = 1, typeName = "string") IPrimitiveType<String> theValueSetVersion,
@OperationParam(name = "filter", min = 0, max = 1, typeName = "string") IPrimitiveType<String> theFilter,
@OperationParam(name = "context", min = 0, max = 1, typeName = "string") IPrimitiveType<String> theContext,
@OperationParam(name = "contextDirection", min = 0, max = 1, typeName = "string") IPrimitiveType<String> theContextDirection,
@OperationParam(name = "offset", min = 0, max = 1, typeName = "integer") IPrimitiveType<Integer> theOffset,
@OperationParam(name = "count", min = 0, max = 1, typeName = "integer") IPrimitiveType<Integer> theCount,
@OperationParam(name = JpaConstants.OPERATION_EXPAND_PARAM_DISPLAY_LANGUAGE, min = 0, max = 1, typeName = "code") IPrimitiveType<String> theDisplayLanguage,
@OperationParam(name = JpaConstants.OPERATION_EXPAND_PARAM_INCLUDE_HIERARCHY, min = 0, max = 1, typeName = "boolean") IPrimitiveType<Boolean> theIncludeHierarchy,
RequestDetails theRequestDetails) {
IPrimitiveType<String> url = theUrl;
if (theIdentifier != null) {
url = theIdentifier;
}
startRequest(theServletRequest);
try {
return getDao().expand(theId, theValueSet, url, theValueSetVersion, theFilter, theContext, theContextDirection, theOffset, theCount, theDisplayLanguage, theIncludeHierarchy, theRequestDetails);
} finally {
endRequest(theServletRequest);
}
}
/**
* $lookup operation - This is on CodeSystem after DSTU2 but on ValueSet in DSTU2
*/
@SuppressWarnings("unchecked")
@Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, typeName = "ValueSet", returnParameters = {
@OperationParam(name = "name", typeName = "string", min = 1),
@OperationParam(name = "version", typeName = "string", min = 0),
@OperationParam(name = "display", typeName = "string", min = 1),
@OperationParam(name = "abstract", typeName = "boolean", min = 1),
})
public IBaseParameters lookup(
HttpServletRequest theServletRequest,
@OperationParam(name = "code", min = 0, max = 1, typeName = "code") IPrimitiveType<String> theCode,
@OperationParam(name = "system", min = 0, max = 1, typeName = "uri") IPrimitiveType<String> theSystem,
@OperationParam(name = "coding", min = 0, max = 1, typeName = "Coding") IBaseCoding theCoding,
@OperationParam(name = "version", min = 0, max = 1, typeName = "string") IPrimitiveType<String> theVersion,
@OperationParam(name = "displayLanguage", min = 0, max = 1, typeName = "code") IPrimitiveType<String> theDisplayLanguage,
@OperationParam(name = "property", min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "code") List<IPrimitiveType<String>> theProperties,
RequestDetails theRequestDetails
) {
startRequest(theServletRequest);
try {
IValidationSupport.LookupCodeResult result;
applyVersionToSystem(theSystem, theVersion);
FhirTerser terser = getContext().newTerser();
result = JpaResourceDaoCodeSystem.doLookupCode(getContext(), terser, myValidationSupport, theCode, theSystem, theCoding, theDisplayLanguage);
result.throwNotFoundIfAppropriate();
return result.toParameters(theRequestDetails.getFhirContext(), theProperties);
} finally {
endRequest(theServletRequest);
}
}
}

View File

@ -1,28 +0,0 @@
package ca.uhn.fhir.jpa.provider.dstu3;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2022 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.jpa.provider.BaseJpaResourceProvider;
import org.hl7.fhir.dstu3.model.ValueSet;
public abstract class BaseJpaResourceProviderValueSetDstu3 extends BaseJpaResourceProvider<ValueSet> {
}

View File

@ -1,28 +0,0 @@
package ca.uhn.fhir.jpa.provider.r4;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2022 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.jpa.provider.BaseJpaResourceProvider;
import org.hl7.fhir.r4.model.ValueSet;
public abstract class BaseJpaResourceProviderValueSetR4 extends BaseJpaResourceProvider<ValueSet> {
}

View File

@ -1,5 +1,25 @@
package ca.uhn.fhir.jpa.provider.r4;
/*-
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2022 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import org.hl7.fhir.instance.model.api.IBaseExtension;
import org.hl7.fhir.instance.model.api.IBaseResource;

View File

@ -1,28 +0,0 @@
package ca.uhn.fhir.jpa.provider.r4b;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2022 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.jpa.provider.BaseJpaResourceProvider;
import org.hl7.fhir.r4b.model.ValueSet;
public abstract class BaseJpaResourceProviderValueSetR4B extends BaseJpaResourceProvider<ValueSet> {
}

View File

@ -1,28 +0,0 @@
package ca.uhn.fhir.jpa.provider.r5;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2022 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.jpa.provider.BaseJpaResourceProvider;
import org.hl7.fhir.r5.model.ValueSet;
public abstract class BaseJpaResourceProviderValueSetR5 extends BaseJpaResourceProvider<ValueSet> {
// nothing
}

View File

@ -691,7 +691,7 @@ public class SearchBuilder implements ISearchBuilder {
for (String type : resourceTypes) {
String trimmed = type.trim();
if (!knownResourceTypes.contains(trimmed)) {
throw new ResourceNotFoundException(Msg.code(2132) + "Unknown resource type '" + trimmed + "' in _type parameter.");
throw new ResourceNotFoundException(Msg.code(2197) + "Unknown resource type '" + trimmed + "' in _type parameter.");
}
retVal.add(trimmed);
}

View File

@ -423,7 +423,7 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc {
}
ourLog.debug("Done saving concepts, flushing to database");
if (!myDeferredStorageSvc.isStorageQueueEmpty()) {
if (!myDeferredStorageSvc.isStorageQueueEmpty(true)) {
ourLog.info("Note that some concept saving has been deferred");
}
}
@ -454,7 +454,7 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc {
ourLog.info("Saving concept {} with parent {}", theStatisticsTracker.getUpdatedConceptCount(), parentDescription);
Optional<TermConcept> existingCodeOpt = myConceptDao.findByCodeSystemAndCode(theCsv, nextCodeToAdd);
Optional<TermConcept> existingCodeOpt = myConceptDao.findByCodeSystemAndCode(theCsv.getPid(), nextCodeToAdd);
List<TermConceptParentChildLink> existingParentLinks;
if (existingCodeOpt.isPresent()) {
TermConcept existingCode = existingCodeOpt.get();
@ -476,7 +476,7 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc {
TermConcept nextParentOpt = theCodeToConcept.get(nextParentCode);
if (nextParentOpt == null) {
nextParentOpt = myConceptDao.findByCodeSystemAndCode(theCsv, nextParentCode).orElse(null);
nextParentOpt = myConceptDao.findByCodeSystemAndCode(theCsv.getPid(), nextParentCode).orElse(null);
}
if (nextParentOpt == null) {
throw new InvalidRequestException(Msg.code(846) + "Unable to add code \"" + nextCodeToAdd + "\" to unknown parent: " + nextParentCode);
@ -542,7 +542,7 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc {
String parentCode = nextChild.getParents().get(i).getParent().getCode();
TermConcept parentConcept = theCodeToConcept.get(parentCode);
if (parentConcept == null) {
parentConcept = myConceptDao.findByCodeSystemAndCode(theCsv, parentCode).orElse(null);
parentConcept = myConceptDao.findByCodeSystemAndCode(theCsv.getPid(), parentCode).orElse(null);
}
if (parentConcept == null) {
throw new IllegalArgumentException(Msg.code(847) + "Unknown parent code: " + parentCode);

View File

@ -282,7 +282,9 @@ public class TermDeferredStorageSvcImpl implements ITermDeferredStorageSvc {
Duration.of(SAVE_ALL_DEFERRED_ERROR_MINUTES, ChronoUnit.MINUTES));
}
while (!isStorageQueueEmpty()) {
// Don't include executing jobs here since there's no point in thrashing over and over
// in a busy wait while we wait for batch2 job processes to finish
while (!isStorageQueueEmpty(false)) {
if (myAllowDeferredTasksTimeout) {
if (timeoutManager.checkTimeout()) {
ourLog.info(toString());
@ -389,14 +391,16 @@ public class TermDeferredStorageSvcImpl implements ITermDeferredStorageSvc {
@Override
public boolean isStorageQueueEmpty() {
public boolean isStorageQueueEmpty(boolean theIncludeExecutingJobs) {
boolean retVal = !isProcessDeferredPaused();
retVal &= !isDeferredConcepts();
retVal &= !isConceptLinksToSaveLater();
retVal &= !isDeferredValueSets();
retVal &= !isDeferredConceptMaps();
retVal &= !isDeferredCodeSystemDeletions();
retVal &= !isJobsExecuting();
if (theIncludeExecutingJobs) {
retVal &= !isJobsExecuting();
}
return retVal;
}

View File

@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.term;
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.support.ConceptValidationOptions;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.ValidationSupportContext;
@ -70,7 +71,6 @@ import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc;
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
import ca.uhn.fhir.jpa.term.api.ReindexTerminologyResult;
import ca.uhn.fhir.jpa.term.ex.ExpansionTooCostlyException;
import ca.uhn.fhir.jpa.util.LogicUtil;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
@ -108,13 +108,12 @@ import org.hibernate.search.mapper.orm.Search;
import org.hibernate.search.mapper.orm.common.EntityReference;
import org.hibernate.search.mapper.orm.session.SearchSession;
import org.hibernate.search.mapper.pojo.massindexing.impl.PojoMassIndexingLoggingMonitor;
import org.hl7.fhir.common.hapi.validation.support.CommonCodeSystemsTerminologyService;
import org.hl7.fhir.common.hapi.validation.support.CachingValidationSupport;
import org.hl7.fhir.common.hapi.validation.support.InMemoryTerminologyServerValidationSupport;
import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_40_50;
import org.hl7.fhir.convertors.context.ConversionContext40_50;
import org.hl7.fhir.convertors.conv40_50.VersionConvertor_40_50;
import org.hl7.fhir.convertors.conv40_50.resources40_50.ValueSet40_50;
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseCoding;
import org.hl7.fhir.instance.model.api.IBaseDatatype;
@ -158,14 +157,6 @@ import javax.persistence.EntityManager;
import javax.persistence.NonUniqueResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Fetch;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@ -175,7 +166,6 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
@ -200,14 +190,13 @@ import static org.apache.commons.lang3.StringUtils.isNoneBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.lowerCase;
import static org.apache.commons.lang3.StringUtils.startsWithIgnoreCase;
import static org.hl7.fhir.common.hapi.validation.support.ValidationConstants.LOINC_LOW;
public class TermReadSvcImpl implements ITermReadSvc {
public static final int DEFAULT_FETCH_SIZE = 250;
private static final int SINGLE_FETCH_SIZE = 1;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TermReadSvcImpl.class);
private static final ValueSetExpansionOptions DEFAULT_EXPANSION_OPTIONS = new ValueSetExpansionOptions();
private static final TermCodeSystemVersion NO_CURRENT_VERSION = new TermCodeSystemVersion().setId(-1L);
private static final TermCodeSystemVersionDetails NO_CURRENT_VERSION = new TermCodeSystemVersionDetails(-1L, null);
private static Runnable myInvokeOnNextCallForUnitTest;
private static boolean ourForceDisableHibernateSearchForUnitTest;
@ -222,7 +211,7 @@ public class TermReadSvcImpl implements ITermReadSvc {
private boolean myPreExpandingValueSets = false;
private final Cache<String, TermCodeSystemVersion> myCodeSystemCurrentVersionCache = Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).build();
private final Cache<String, TermCodeSystemVersionDetails> myCodeSystemCurrentVersionCache = Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).build();
@Autowired
protected DaoRegistry myDaoRegistry;
@Autowired
@ -276,11 +265,13 @@ public class TermReadSvcImpl implements ITermReadSvc {
//We need this bean so we can tell which mode hibernate search is running in.
@Autowired
private HibernatePropertiesProvider myHibernatePropertiesProvider;
@Autowired
private CachingValidationSupport myCachingValidationSupport;
@Override
public boolean isCodeSystemSupported(ValidationSupportContext theValidationSupportContext, String theSystem) {
TermCodeSystemVersion cs = getCurrentCodeSystemVersion(theSystem);
TermCodeSystemVersionDetails cs = getCurrentCodeSystemVersion(theSystem);
return cs != null;
}
@ -831,7 +822,6 @@ public class TermReadSvcImpl implements ITermReadSvc {
expandValueSetHandleIncludeOrExcludeUsingDatabase(theExpansionOptions, theValueSetCodeAccumulator,
theAddedCodes, theIncludeOrExclude, theAdd, theExpansionFilter, system, cs);
return;
} else {
@ -1617,6 +1607,9 @@ public class TermReadSvcImpl implements ITermReadSvc {
termValueSet.setExpansionStatus(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED);
termValueSet.setExpansionTimestamp(null);
myTermValueSetDao.save(termValueSet);
afterValueSetExpansionStatusChange();
return myContext.getLocalizer().getMessage(TermReadSvcImpl.class, "valueSetPreExpansionInvalidated", termValueSet.getUrl(), totalConcepts);
}
@ -1700,7 +1693,9 @@ public class TermReadSvcImpl implements ITermReadSvc {
}
}
return createFailureCodeValidationResult(theSystem, theCode, systemVersion, " - Concept Display \"" + theDisplay + "\" does not match expected \"" + concepts.get(0).getDisplay() + "\". " + msg).setDisplay(concepts.get(0).getDisplay());
String expectedDisplay = concepts.get(0).getDisplay();
String append = createMessageAppendForDisplayMismatch(theSystem, theDisplay, expectedDisplay) + " - " + msg;
return createFailureCodeValidationResult(theSystem, theCode, systemVersion, append).setDisplay(expectedDisplay);
}
if (!concepts.isEmpty()) {
@ -1758,7 +1753,7 @@ public class TermReadSvcImpl implements ITermReadSvc {
private Optional<TermConcept> fetchLoadedCode(Long theCodeSystemResourcePid, String theCode) {
TermCodeSystemVersion codeSystem = myCodeSystemVersionDao.findCurrentVersionForCodeSystemResourcePid(theCodeSystemResourcePid);
return myConceptDao.findByCodeSystemAndCode(codeSystem, theCode);
return myConceptDao.findByCodeSystemAndCode(codeSystem.getPid(), theCode);
}
private void fetchParents(TermConcept theConcept, Set<TermConcept> theSetToPopulate) {
@ -1780,31 +1775,31 @@ public class TermReadSvcImpl implements ITermReadSvc {
*/
TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager);
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_MANDATORY);
txTemplate.setReadOnly(true);
return txTemplate.execute(t -> {
TermCodeSystemVersion csv = getCurrentCodeSystemVersion(theCodeSystem);
TermCodeSystemVersionDetails csv = getCurrentCodeSystemVersion(theCodeSystem);
if (csv == null) {
return Optional.empty();
}
return myConceptDao.findByCodeSystemAndCode(csv, theCode);
return myConceptDao.findByCodeSystemAndCode(csv.myPid, theCode);
});
}
@Override
@Transactional(propagation = Propagation.MANDATORY)
public List<TermConcept> findCodes(String theCodeSystem, List<String> theCodeList) {
TermCodeSystemVersion csv = getCurrentCodeSystemVersion(theCodeSystem);
TermCodeSystemVersionDetails csv = getCurrentCodeSystemVersion(theCodeSystem);
if (csv == null) { return Collections.emptyList(); }
return myConceptDao.findByCodeSystemAndCodeList(csv, theCodeList);
return myConceptDao.findByCodeSystemAndCodeList(csv.myPid, theCodeList);
}
@Nullable
private TermCodeSystemVersion getCurrentCodeSystemVersion(String theCodeSystemIdentifier) {
private TermCodeSystemVersionDetails getCurrentCodeSystemVersion(String theCodeSystemIdentifier) {
String version = getVersionFromIdentifier(theCodeSystemIdentifier);
TermCodeSystemVersion retVal = myCodeSystemCurrentVersionCache.get(theCodeSystemIdentifier, t -> myTxTemplate.execute(tx -> {
TermCodeSystemVersionDetails retVal = myCodeSystemCurrentVersionCache.get(theCodeSystemIdentifier, t -> myTxTemplate.execute(tx -> {
TermCodeSystemVersion csv = null;
TermCodeSystem cs = myCodeSystemDao.findByCodeSystemUri(getUrlFromIdentifier(theCodeSystemIdentifier));
if (cs != null) {
@ -1815,7 +1810,7 @@ public class TermReadSvcImpl implements ITermReadSvc {
}
}
if (csv != null) {
return csv;
return new TermCodeSystemVersionDetails(csv.getPid(), csv.getCodeSystemVersionId());
} else {
return NO_CURRENT_VERSION;
}
@ -1826,6 +1821,19 @@ public class TermReadSvcImpl implements ITermReadSvc {
return retVal;
}
private static class TermCodeSystemVersionDetails {
private final long myPid;
private final String myCodeSystemVersionId;
public TermCodeSystemVersionDetails(long thePid, String theCodeSystemVersionId) {
myPid = thePid;
myCodeSystemVersionId = theCodeSystemVersionId;
}
}
private String getVersionFromIdentifier(String theUri) {
String retVal = null;
if (StringUtils.isNotEmpty((theUri))) {
@ -1984,6 +1992,8 @@ public class TermReadSvcImpl implements ITermReadSvc {
});
afterValueSetExpansionStatusChange();
ourLog.info("Pre-expanded ValueSet[{}] with URL[{}] - Saved {} concepts in {}", valueSet.getId(), valueSet.getUrl(), accumulator.getConceptsSaved(), sw);
} catch (Exception e) {
@ -2000,6 +2010,17 @@ public class TermReadSvcImpl implements ITermReadSvc {
}
}
/*
* If a ValueSet has just finished pre-expanding, let's flush the caches. This is
* kind of a blunt tool, but it should ensure that users don't get unpredictable
* results while they test changes, which is probably a worthwhile sacrifice
*/
private void afterValueSetExpansionStatusChange() {
// TODO: JA2 - Move this caching into the memorycacheservice, and only purge the
// relevant individual cache
myCachingValidationSupport.invalidateCaches();
}
private synchronized void setPreExpandingValueSets(boolean thePreExpandingValueSets) {
myPreExpandingValueSets = thePreExpandingValueSets;
}
@ -2008,77 +2029,9 @@ public class TermReadSvcImpl implements ITermReadSvc {
return myPreExpandingValueSets;
}
@Override
@Transactional
public CodeValidationResult validateCode(ConceptValidationOptions theOptions, IIdType theValueSetId, String theValueSetIdentifier, String theCodeSystemIdentifierToValidate, String theCodeToValidate, String theDisplayToValidate, IBaseDatatype theCodingToValidate, IBaseDatatype theCodeableConceptToValidate) {
CodeableConcept codeableConcept = myVersionCanonicalizer.codeableConceptToCanonical(theCodeableConceptToValidate);
boolean haveCodeableConcept = codeableConcept != null && codeableConcept.getCoding().size() > 0;
Coding canonicalCodingToValidate = myVersionCanonicalizer.codingToCanonical((IBaseCoding) theCodingToValidate);
boolean haveCoding = canonicalCodingToValidate != null && !canonicalCodingToValidate.isEmpty();
boolean haveCode = theCodeToValidate != null && !theCodeToValidate.isEmpty();
if (!haveCodeableConcept && !haveCoding && !haveCode) {
throw new InvalidRequestException(Msg.code(899) + "No code, coding, or codeableConcept provided to validate");
}
if (!LogicUtil.multiXor(haveCodeableConcept, haveCoding, haveCode)) {
throw new InvalidRequestException(Msg.code(900) + "$validate-code can only validate (system AND code) OR (coding) OR (codeableConcept)");
}
boolean haveIdentifierParam = isNotBlank(theValueSetIdentifier);
String valueSetIdentifier;
if (theValueSetId != null) {
IBaseResource valueSet = myDaoRegistry.getResourceDao("ValueSet").read(theValueSetId);
StringBuilder valueSetIdentifierBuilder = new StringBuilder(CommonCodeSystemsTerminologyService.getValueSetUrl(valueSet));
String valueSetVersion = CommonCodeSystemsTerminologyService.getValueSetVersion(valueSet);
if (valueSetVersion != null) {
valueSetIdentifierBuilder.append("|").append(valueSetVersion);
}
valueSetIdentifier = valueSetIdentifierBuilder.toString();
} else if (haveIdentifierParam) {
valueSetIdentifier = theValueSetIdentifier;
} else {
throw new InvalidRequestException(Msg.code(901) + "Either ValueSet ID or ValueSet identifier or system and code must be provided. Unable to validate.");
}
ValidationSupportContext validationContext = new ValidationSupportContext(provideValidationSupport());
String codeValueToValidate = theCodeToValidate;
String codeSystemIdentifierValueToValidate = theCodeSystemIdentifierToValidate;
String codeDisplayValueToValidate = theDisplayToValidate;
if (haveCodeableConcept) {
for (int i = 0; i < codeableConcept.getCoding().size(); i++) {
Coding nextCoding = codeableConcept.getCoding().get(i);
String codeSystemIdentifier;
if (nextCoding.hasVersion()) {
codeSystemIdentifier = nextCoding.getSystem() + "|" + nextCoding.getVersion();
} else {
codeSystemIdentifier = nextCoding.getSystem();
}
CodeValidationResult nextValidation = validateCode(validationContext, theOptions, codeSystemIdentifier, nextCoding.getCode(), nextCoding.getDisplay(), valueSetIdentifier);
if (nextValidation.isOk() || i == codeableConcept.getCoding().size() - 1) {
return nextValidation;
}
}
} else if (haveCoding) {
if (canonicalCodingToValidate.hasVersion()) {
codeSystemIdentifierValueToValidate = canonicalCodingToValidate.getSystem() + "|" + canonicalCodingToValidate.getVersion();
} else {
codeSystemIdentifierValueToValidate = canonicalCodingToValidate.getSystem();
}
codeValueToValidate = canonicalCodingToValidate.getCode();
codeDisplayValueToValidate = canonicalCodingToValidate.getDisplay();
}
return validateCode(validationContext, theOptions, codeSystemIdentifierValueToValidate, codeValueToValidate, codeDisplayValueToValidate, valueSetIdentifier);
}
private boolean isNotSafeToPreExpandValueSets() {
return myDeferredStorageSvc != null && !myDeferredStorageSvc.isStorageQueueEmpty();
return myDeferredStorageSvc != null && !myDeferredStorageSvc.isStorageQueueEmpty(true);
}
private Optional<TermValueSet> getNextTermValueSetNotExpanded() {
@ -2289,30 +2242,35 @@ public class TermReadSvcImpl implements ITermReadSvc {
@CoverageIgnore
@Override
public IValidationSupport.CodeValidationResult validateCode(@Nonnull ValidationSupportContext theValidationSupportContext, @Nonnull ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
public IValidationSupport.CodeValidationResult validateCode(@Nonnull ValidationSupportContext theValidationSupportContext, @Nonnull ConceptValidationOptions theOptions, String theCodeSystemUrl, String theCode, String theDisplay, String theValueSetUrl) {
//TODO GGG TRY TO JUST AUTO_PASS HERE AND SEE WHAT HAPPENS.
invokeRunnableForUnitTest();
if (isNotBlank(theValueSetUrl)) {
return validateCodeInValueSet(theValidationSupportContext, theOptions, theValueSetUrl, theCodeSystem, theCode, theDisplay);
return validateCodeInValueSet(theValidationSupportContext, theOptions, theValueSetUrl, theCodeSystemUrl, theCode, theDisplay);
}
TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager);
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
Optional<FhirVersionIndependentConcept> codeOpt = txTemplate.execute(t -> findCode(theCodeSystem, theCode).map(c -> new FhirVersionIndependentConcept(theCodeSystem, c.getCode())));
txTemplate.setReadOnly(true);
Optional<FhirVersionIndependentConcept> codeOpt = txTemplate.execute(tx -> findCode(theCodeSystemUrl, theCode).map(c -> {
String codeSystemVersionId = getCurrentCodeSystemVersion(theCodeSystemUrl).myCodeSystemVersionId;
return new FhirVersionIndependentConcept(theCodeSystemUrl, c.getCode(), c.getDisplay(), codeSystemVersionId);
}));
if (codeOpt != null && codeOpt.isPresent()) {
FhirVersionIndependentConcept code = codeOpt.get();
if (!theOptions.isValidateDisplay() || (isNotBlank(code.getDisplay()) && isNotBlank(theDisplay) && code.getDisplay().equals(theDisplay))) {
if (!theOptions.isValidateDisplay() || isBlank(code.getDisplay()) || isBlank(theDisplay) || code.getDisplay().equals(theDisplay)) {
return new CodeValidationResult()
.setCode(code.getCode())
.setDisplay(code.getDisplay());
} else {
return createFailureCodeValidationResult(theCodeSystem, theCode, code.getSystemVersion(), " - Concept Display \"" + code.getDisplay() + "\" does not match expected \"" + code.getDisplay() + "\"").setDisplay(code.getDisplay());
String messageAppend = createMessageAppendForDisplayMismatch(theCodeSystemUrl, theDisplay, code.getDisplay());
return createFailureCodeValidationResult(theCodeSystemUrl, theCode, code.getSystemVersion(), messageAppend).setDisplay(code.getDisplay());
}
}
return createFailureCodeValidationResult(theCodeSystem, theCode, null, " - Code can not be found in CodeSystem");
return createFailureCodeValidationResult(theCodeSystemUrl, theCode, null, createMessageAppendForCodeNotFoundInCodeSystem(theCodeSystemUrl));
}
IValidationSupport.CodeValidationResult validateCodeInValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theValidationOptions, String theValueSetUrl, String theCodeSystem, String theCode, String theDisplay) {
@ -2345,7 +2303,7 @@ public class TermReadSvcImpl implements ITermReadSvc {
}
// Check if someone is accidentally using a VS url where it should be a CS URL
if (retVal != null && retVal.getCode() == null && theCodeSystem != null) {
if (retVal != null && retVal.getCode() == null && theCodeSystem != null && myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU2)) {
if (isValueSetSupported(theValidationSupportContext, theCodeSystem)) {
if (!isCodeSystemSupported(theValidationSupportContext, theCodeSystem)) {
String newMessage = "Unable to validate code " + theCodeSystem + "#" + theCode + " - Supplied system URL is a ValueSet URL and not a CodeSystem URL, check if it is correct: " + theCodeSystem;
@ -2358,12 +2316,6 @@ public class TermReadSvcImpl implements ITermReadSvc {
}
@Override
public IBaseResource fetchCodeSystem(String theSystem) {
IValidationSupport jpaValidationSupport = provideJpaValidationSupport();
return jpaValidationSupport.fetchCodeSystem(theSystem);
}
@Override
public CodeSystem fetchCanonicalCodeSystemFromCompleteContext(String theSystem) {
IValidationSupport validationSupport = provideValidationSupport();
@ -2494,69 +2446,6 @@ public class TermReadSvcImpl implements ITermReadSvc {
return new FhirVersionIndependentConcept(system, code, null, systemVersion);
}
@Override
@Transactional
public CodeValidationResult codeSystemValidateCode(IIdType theCodeSystemId, String theCodeSystemUrl, String theVersion, String theCode, String theDisplay, IBaseDatatype theCoding, IBaseDatatype theCodeableConcept) {
CodeableConcept codeableConcept = myVersionCanonicalizer.codeableConceptToCanonical(theCodeableConcept);
boolean haveCodeableConcept = codeableConcept != null && codeableConcept.getCoding().size() > 0;
Coding coding = myVersionCanonicalizer.codingToCanonical((IBaseCoding) theCoding);
boolean haveCoding = coding != null && !coding.isEmpty();
boolean haveCode = theCode != null && !theCode.isEmpty();
if (!haveCodeableConcept && !haveCoding && !haveCode) {
throw new InvalidRequestException(Msg.code(906) + "No code, coding, or codeableConcept provided to validate.");
}
if (!LogicUtil.multiXor(haveCodeableConcept, haveCoding, haveCode)) {
throw new InvalidRequestException(Msg.code(907) + "$validate-code can only validate (code) OR (coding) OR (codeableConcept)");
}
boolean haveIdentifierParam = isNotBlank(theCodeSystemUrl);
String codeSystemUrl;
if (theCodeSystemId != null) {
IBaseResource codeSystem = myDaoRegistry.getResourceDao("CodeSystem").read(theCodeSystemId);
codeSystemUrl = CommonCodeSystemsTerminologyService.getCodeSystemUrl(codeSystem);
} else if (haveIdentifierParam) {
codeSystemUrl = theCodeSystemUrl;
} else {
throw new InvalidRequestException(Msg.code(908) + "Either CodeSystem ID or CodeSystem identifier must be provided. Unable to validate.");
}
String code = theCode;
String display = theDisplay;
if (haveCodeableConcept) {
for (int i = 0; i < codeableConcept.getCoding().size(); i++) {
Coding nextCoding = codeableConcept.getCoding().get(i);
if (nextCoding.hasSystem()) {
if (!codeSystemUrl.equalsIgnoreCase(nextCoding.getSystem())) {
throw new InvalidRequestException(Msg.code(909) + "Coding.system '" + nextCoding.getSystem() + "' does not equal with CodeSystem.url '" + theCodeSystemUrl + "'. Unable to validate.");
}
codeSystemUrl = nextCoding.getSystem();
}
code = nextCoding.getCode();
display = nextCoding.getDisplay();
CodeValidationResult nextValidation = codeSystemValidateCode(codeSystemUrl, theVersion, code, display);
if (nextValidation.isOk() || i == codeableConcept.getCoding().size() - 1) {
return nextValidation;
}
}
} else if (haveCoding) {
if (coding.hasSystem()) {
if (!codeSystemUrl.equalsIgnoreCase(coding.getSystem())) {
throw new InvalidRequestException(Msg.code(910) + "Coding.system '" + coding.getSystem() + "' does not equal with CodeSystem.url '" + theCodeSystemUrl + "'. Unable to validate.");
}
codeSystemUrl = coding.getSystem();
}
code = coding.getCode();
display = coding.getDisplay();
}
return codeSystemValidateCode(codeSystemUrl, theVersion, code, display);
}
/**
* When the search is for unversioned loinc system it uses the forcedId to obtain the current
@ -2582,60 +2471,14 @@ public class TermReadSvcImpl implements ITermReadSvc {
return Optional.of(termValueSetList.get(0));
}
@SuppressWarnings("unchecked")
private CodeValidationResult codeSystemValidateCode(String theCodeSystemUrl, String theCodeSystemVersion, String theCode, String theDisplay) {
@Nonnull
private static String createMessageAppendForDisplayMismatch(String theCodeSystemUrl, String theDisplay, String theExpectedDisplay) {
return " - Concept Display \"" + theDisplay + "\" does not match expected \"" + theExpectedDisplay + "\" for CodeSystem: " + theCodeSystemUrl;
}
CriteriaBuilder criteriaBuilder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<TermConcept> query = criteriaBuilder.createQuery(TermConcept.class);
Root<TermConcept> root = query.from(TermConcept.class);
Fetch<TermCodeSystemVersion, TermConcept> systemVersionFetch = root.fetch("myCodeSystem", JoinType.INNER);
Join<TermCodeSystemVersion, TermConcept> systemVersionJoin = (Join<TermCodeSystemVersion, TermConcept>) systemVersionFetch;
Fetch<TermCodeSystem, TermCodeSystemVersion> systemFetch = systemVersionFetch.fetch("myCodeSystem", JoinType.INNER);
Join<TermCodeSystem, TermCodeSystemVersion> systemJoin = (Join<TermCodeSystem, TermCodeSystemVersion>) systemFetch;
ArrayList<Predicate> predicates = new ArrayList<>();
if (isNotBlank(theCode)) {
predicates.add(criteriaBuilder.equal(root.get("myCode"), theCode));
}
if (isNoneBlank(theCodeSystemUrl)) {
predicates.add(criteriaBuilder.equal(systemJoin.get("myCodeSystemUri"), theCodeSystemUrl));
}
// for loinc CodeSystem last version is not necessarily the current anymore, so if no version is present
// we need to query for the current, which is that which version is null
if (isNoneBlank(theCodeSystemVersion)) {
predicates.add(criteriaBuilder.equal(systemVersionJoin.get("myCodeSystemVersionId"), theCodeSystemVersion));
} else {
if (theCodeSystemUrl.toLowerCase(Locale.ROOT).contains(LOINC_LOW)) {
predicates.add(criteriaBuilder.isNull(systemVersionJoin.get("myCodeSystemVersionId")));
} else {
query.orderBy(criteriaBuilder.desc(root.get("myUpdated")));
}
}
Predicate outerPredicate = criteriaBuilder.and(predicates.toArray(new Predicate[0]));
query.where(outerPredicate);
final TypedQuery<TermConcept> typedQuery = myEntityManager.createQuery(query.select(root));
org.hibernate.query.Query<TermConcept> hibernateQuery = (org.hibernate.query.Query<TermConcept>) typedQuery;
hibernateQuery.setFetchSize(SINGLE_FETCH_SIZE);
List<TermConcept> resultsList = hibernateQuery.getResultList();
if (!resultsList.isEmpty()) {
TermConcept concept = resultsList.get(0);
if (isNotBlank(theDisplay) && !theDisplay.equals(concept.getDisplay())) {
String message = "Concept Display \"" + theDisplay + "\" does not match expected \"" + concept.getDisplay() + "\" for CodeSystem: " + theCodeSystemUrl;
return createFailureCodeValidationResult(theCodeSystemUrl, theCode, theCodeSystemVersion, message);
}
return new CodeValidationResult().setCode(concept.getCode()).setDisplay(concept.getDisplay());
}
return createFailureCodeValidationResult(theCodeSystemUrl, theCode, theCodeSystemVersion, " - Code is not found in CodeSystem: " + theCodeSystemUrl);
@Nonnull
private static String createMessageAppendForCodeNotFoundInCodeSystem(String theCodeSystemUrl) {
return " - Code is not found in CodeSystem: " + theCodeSystemUrl;
}
@Override

View File

@ -26,7 +26,6 @@ import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.jpa.model.sched.HapiJob;
import ca.uhn.fhir.jpa.model.sched.ISchedulerService;
import ca.uhn.fhir.jpa.model.sched.ScheduledJobDefinition;
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc;
import ca.uhn.fhir.jpa.term.api.ITermReindexingSvc;
import ca.uhn.fhir.util.StopWatch;
@ -71,7 +70,7 @@ public class TermReindexingSvcImpl implements ITermReindexingSvc {
@Override
public void processReindexing() {
if (myDeferredStorageSvc.isStorageQueueEmpty() == false && !ourForceSaveDeferredAlwaysForUnitTest) {
if (myDeferredStorageSvc.isStorageQueueEmpty(true) == false && !ourForceSaveDeferredAlwaysForUnitTest) {
return;
}

View File

@ -37,7 +37,20 @@ public interface ITermDeferredStorageSvc {
void saveDeferred();
boolean isStorageQueueEmpty();
/**
* @deprecated Use {@link #isStorageQueueEmpty(boolean)} instead
*/
@Deprecated
default boolean isStorageQueueEmpty() {
return isStorageQueueEmpty(true);
}
/**
* If you are calling this in a loop or something similar, consider the impact of
* setting {@literal theIncludeExecutingJobs} to true. When that parameter is set
* to true, each call to this method results in a database lookup!
*/
boolean isStorageQueueEmpty(boolean theIncludeExecutingJobs);
/**
* This is mostly for unit tests - we can disable processing of deferred concepts

View File

@ -17,10 +17,10 @@ import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.ValueSet;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Optional;
import java.util.Set;
@ -98,11 +98,6 @@ public interface ITermReadSvc extends IValidationSupport {
void preExpandDeferredValueSetsToTerminologyTables();
/**
* Version independent
*/
CodeValidationResult validateCode(ConceptValidationOptions theOptions, IIdType theValueSetId, String theValueSetUrl, String theSystem, String theCode, String theDisplay, IBaseDatatype theCoding, IBaseDatatype theCodeableConcept);
/**
* Version independent
*/
@ -116,11 +111,6 @@ public interface ITermReadSvc extends IValidationSupport {
*/
boolean isValueSetPreExpandedForCodeValidation(IBaseResource theValueSet);
/**
* Version independent
*/
CodeValidationResult codeSystemValidateCode(IIdType theCodeSystemId, String theValueSetUrl, String theVersion, String theCode, String theDisplay, IBaseDatatype theCoding, IBaseDatatype theCodeableConcept);
String invalidatePreCalculatedExpansion(IIdType theValueSetId, RequestDetails theRequestDetails);
/**

View File

@ -32,14 +32,20 @@ public class LogicUtil {
// nothing
}
/**
* Returns true IF and ONLY IF exactly 1 of the provided boolean(s) is true
*/
public static boolean multiXor(boolean... theValues) {
int count = 0;
for (int i = 0; i < theValues.length; i++) {
if (theValues[i]) {
count++;
boolean foundOne = false;
for (boolean next : theValues) {
if (next) {
if (foundOne) {
return false;
}
foundOne = true;
}
}
return count == 1;
return foundOne;
}
}

View File

@ -7,7 +7,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.3.0-PRE3-SNAPSHOT</version>
<version>6.3.0-PRE4-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -44,6 +44,12 @@ public class BaseCqlR4Test extends BaseJpaR4Test implements CqlProviderTestBase
@RegisterExtension
protected PartitionHelper myPartitionHelper;
// FIXME: restore?
// @Override
// public void beforeResetInterceptors() {
// myInterceptorRegistry.unregisterInterceptorsIf(t->!(t instanceof PartitionHelper.MyTestInterceptor));
// }
@Autowired
protected
DaoRegistry myDaoRegistry;

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.3.0-PRE3-SNAPSHOT</version>
<version>6.3.0-PRE4-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>
@ -87,15 +87,15 @@
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-api</artifactId>
<artifactId>websocket-jetty-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-client</artifactId>
<artifactId>websocket-core-client</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-server</artifactId>
<artifactId>websocket-jetty-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>

View File

@ -165,7 +165,7 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl
protected ISearchParamRegistry mySearchParamRegistry;
@Autowired
@Qualifier("myValueSetDaoR4")
protected IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> myValueSetDao;
protected IFhirResourceDaoValueSet<ValueSet> myValueSetDao;
@Autowired
protected ITermReadSvc myTermSvc;
@Autowired

View File

@ -61,14 +61,14 @@ public class FhirResourceDaoR4TerminologyElasticsearchIT extends BaseJpaTest {
protected DaoConfig myDaoConfig;
@Autowired
@Qualifier("myCodeSystemDaoR4")
protected IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> myCodeSystemDao;
protected IFhirResourceDaoCodeSystem<CodeSystem> myCodeSystemDao;
@Autowired
protected IResourceTableDao myResourceTableDao;
@Autowired
protected ITermCodeSystemStorageSvc myTermCodeSystemStorageSvc;
@Autowired
@Qualifier("myValueSetDaoR4")
protected IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> myValueSetDao;
protected IFhirResourceDaoValueSet<ValueSet> myValueSetDao;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
protected ServletRequestDetails mySrd;
@Autowired

View File

@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.provider.r4;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.config.TestR4ConfigWithElasticHSearch;
import ca.uhn.fhir.jpa.provider.BaseJpaResourceProvider;
import ca.uhn.fhir.jpa.provider.BaseResourceProviderR4Test;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.test.utilities.docker.RequiresDocker;
import org.apache.commons.io.IOUtils;
@ -89,7 +90,7 @@ public class ResourceProviderR4ElasticTest extends BaseResourceProviderR4Test {
createObservationWithCode(mean_blood_pressure);
// when
HttpGet expandQuery = new HttpGet(BaseResourceProviderR4Test.ourServerBase + "/ValueSet/$expand?contextDirection=existing&context=Observation.code:text&filter=pressure");
HttpGet expandQuery = new HttpGet(ourServerBase + "/ValueSet/$expand?contextDirection=existing&context=Observation.code:text&filter=pressure");
try (CloseableHttpResponse response = BaseResourceProviderR4Test.ourHttpClient.execute(expandQuery)) {
// then
@ -166,7 +167,7 @@ public class ResourceProviderR4ElasticTest extends BaseResourceProviderR4Test {
Coding blood_count = new Coding("http://loinc.org", "789-8", "Erythrocytes in Blood by Automated count for code: " + (index + 1));
createObservationWithCode(blood_count);
});
HttpGet countQuery = new HttpGet(BaseResourceProviderR4Test.ourServerBase + "/Observation?code=789-8&_count=5&_total=accurate");
HttpGet countQuery = new HttpGet(ourServerBase + "/Observation?code=789-8&_count=5&_total=accurate");
myCaptureQueriesListener.clear();
try (CloseableHttpResponse response = BaseResourceProviderR4Test.ourHttpClient.execute(countQuery)) {
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
@ -187,7 +188,7 @@ public class ResourceProviderR4ElasticTest extends BaseResourceProviderR4Test {
Coding blood_count = new Coding("http://loinc.org", "789-8", "Erythrocytes in Blood by Automated count for code: " + (index + 1));
createObservationWithCode(blood_count);
});
HttpGet countQuery = new HttpGet(BaseResourceProviderR4Test.ourServerBase + "/Observation?code=789-8&_count=0");
HttpGet countQuery = new HttpGet(ourServerBase + "/Observation?code=789-8&_count=0");
myCaptureQueriesListener.clear();
try (CloseableHttpResponse response = BaseResourceProviderR4Test.ourHttpClient.execute(countQuery)) {
myCaptureQueriesListener.logSelectQueriesForCurrentThread();

View File

@ -30,8 +30,6 @@ import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
import ca.uhn.fhir.test.utilities.docker.RequiresDocker;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.CodeableConcept;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.Enumerations;
import org.hl7.fhir.r4.model.ValueSet;
import org.junit.jupiter.api.AfterEach;
@ -77,14 +75,14 @@ public class ValueSetExpansionR4ElasticsearchIT extends BaseJpaTest {
protected DaoConfig myDaoConfig;
@Autowired
@Qualifier("myCodeSystemDaoR4")
protected IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> myCodeSystemDao;
protected IFhirResourceDaoCodeSystem<CodeSystem> myCodeSystemDao;
@Autowired
protected IResourceTableDao myResourceTableDao;
@Autowired
protected ITermCodeSystemStorageSvc myTermCodeSystemStorageSvc;
@Autowired
@Qualifier("myValueSetDaoR4")
protected IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> myValueSetDao;
protected IFhirResourceDaoValueSet<ValueSet> myValueSetDao;
@Autowired
protected ITermReadSvc myTermSvc;
@Autowired
@ -250,7 +248,7 @@ public class ValueSetExpansionR4ElasticsearchIT extends BaseJpaTest {
new SystemRequestDetails(), Collections.singletonList(valueSet), Collections.emptyList());
myTerminologyDeferredStorageSvc.saveAllDeferred();
await().atMost(10, SECONDS).until(myTerminologyDeferredStorageSvc::isStorageQueueEmpty);
await().atMost(10, SECONDS).until(() -> myTerminologyDeferredStorageSvc.isStorageQueueEmpty(true));
myTermSvc.preExpandDeferredValueSetsToTerminologyTables();

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.3.0-PRE3-SNAPSHOT</version>
<version>6.3.0-PRE4-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>
@ -63,12 +63,6 @@
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jpaserver-test-utilities</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>

Some files were not shown because too many files have changed in this diff Show More