Merge branch 'master' into analyzer-def-using-search-mapping
This commit is contained in:
commit
49f4ac4fc5
|
@ -141,6 +141,8 @@ local.properties
|
||||||
**/.target
|
**/.target
|
||||||
**/.project
|
**/.project
|
||||||
**/.classpath
|
**/.classpath
|
||||||
|
**/.factorypath
|
||||||
|
**/.springBeans
|
||||||
|
|
||||||
|
|
||||||
# PDT-specific
|
# PDT-specific
|
||||||
|
|
|
@ -22,4 +22,4 @@ before_script:
|
||||||
script:
|
script:
|
||||||
# - mvn -e -B clean install && cd hapi-fhir-ra && mvn -e -B -DTRAVIS_JOB_ID=$TRAVIS_JOB_ID clean test jacoco:report coveralls:report
|
# - mvn -e -B clean install && cd hapi-fhir-ra && mvn -e -B -DTRAVIS_JOB_ID=$TRAVIS_JOB_ID clean test jacoco:report coveralls:report
|
||||||
# - mvn -Dci=true -e -B -P ALLMODULES,NOPARALLEL,ERRORPRONE clean install && cd hapi-fhir-jacoco && mvn -e -B -DTRAVIS_JOB_ID=$TRAVIS_JOB_ID jacoco:report coveralls:report
|
# - mvn -Dci=true -e -B -P ALLMODULES,NOPARALLEL,ERRORPRONE clean install && cd hapi-fhir-jacoco && mvn -e -B -DTRAVIS_JOB_ID=$TRAVIS_JOB_ID jacoco:report coveralls:report
|
||||||
- mvn -Dci=true -e -B -P ALLMODULES,MINPARALLEL clean install && cd hapi-fhir-jacoco && mvn -e -B -DTRAVIS_JOB_ID=$TRAVIS_JOB_ID jacoco:report coveralls:report
|
- mvn -Dci=true -e -B -P ALLMODULES,MINPARALLEL,ERRORPRONE clean install && cd hapi-fhir-jacoco && mvn -e -B -DTRAVIS_JOB_ID=$TRAVIS_JOB_ID jacoco:report coveralls:report
|
||||||
|
|
|
@ -63,6 +63,65 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.basepom.maven</groupId>
|
||||||
|
<artifactId>duplicate-finder-maven-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>default</id>
|
||||||
|
<phase>verify</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>check</goal>
|
||||||
|
</goals>
|
||||||
|
<inherited>true</inherited>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
<configuration>
|
||||||
|
<printEqualFiles>false</printEqualFiles>
|
||||||
|
<failBuildInCaseOfDifferentContentConflict>true</failBuildInCaseOfDifferentContentConflict>
|
||||||
|
<failBuildInCaseOfEqualContentConflict>true</failBuildInCaseOfEqualContentConflict>
|
||||||
|
<failBuildInCaseOfConflict>true</failBuildInCaseOfConflict>
|
||||||
|
<checkCompileClasspath>true</checkCompileClasspath>
|
||||||
|
<checkRuntimeClasspath>false</checkRuntimeClasspath>
|
||||||
|
<checkTestClasspath>false</checkTestClasspath>
|
||||||
|
<skip>false</skip>
|
||||||
|
<quiet>false</quiet>
|
||||||
|
<preferLocal>true</preferLocal>
|
||||||
|
<useResultFile>true</useResultFile>
|
||||||
|
<resultFileMinClasspathCount>2</resultFileMinClasspathCount>
|
||||||
|
<resultFile>${project.build.directory}/duplicate-finder-result.xml</resultFile>
|
||||||
|
<ignoredDependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.el</groupId>
|
||||||
|
<artifactId>javax.el-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.mail</groupId>
|
||||||
|
<artifactId>javax.mail-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.phloc</groupId>
|
||||||
|
<artifactId>phloc-commons</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-logging</groupId>
|
||||||
|
<artifactId>commons-logging</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jscience</groupId>
|
||||||
|
<artifactId>jscience</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-jcl</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</ignoredDependencies>
|
||||||
|
<ignoredResources>
|
||||||
|
<ignoredResource>changelog.txt</ignoredResource>
|
||||||
|
<ignoredResource>javac.bat</ignoredResource>
|
||||||
|
</ignoredResources>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,25 @@
|
||||||
<groupId>commons-codec</groupId>
|
<groupId>commons-codec</groupId>
|
||||||
<artifactId>commons-codec</artifactId>
|
<artifactId>commons-codec</artifactId>
|
||||||
</exclusion>
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.codehaus.woodstox</groupId>
|
||||||
|
<artifactId>woodstox-core-asl</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
|
<artifactId>hapi-fhir-client</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.apache.httpcomponents</groupId>
|
||||||
|
<artifactId>httpcore</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.apache.httpcomponents</groupId>
|
||||||
|
<artifactId>httpclient</artifactId>
|
||||||
|
</exclusion>
|
||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -92,52 +111,14 @@
|
||||||
</configuration>
|
</configuration>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>dstu2_shade</id>
|
<id>it</id>
|
||||||
<goals>
|
<goals>
|
||||||
<goal>integration-test</goal>
|
<goal>integration-test</goal>
|
||||||
<goal>verify</goal>
|
|
||||||
</goals>
|
</goals>
|
||||||
<configuration>
|
<configuration>
|
||||||
<includes>
|
<includes>
|
||||||
<include>**/*Dstu2ShadeIT.java</include>
|
<include>**/*IT.java</include>
|
||||||
</includes>
|
</includes>
|
||||||
<classpathDependencyExcludes>
|
|
||||||
<!--<classpathDependencyExclude>ca.uhn.hapi.fhir:*</classpathDependencyExclude>-->
|
|
||||||
<classpathDependencyExclude>org.codehaus.woodstox:woodstox-core-asl</classpathDependencyExclude>
|
|
||||||
<classpathDependencyExclude>org.codehaus.woodstox:stax2-api</classpathDependencyExclude>
|
|
||||||
</classpathDependencyExcludes>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
<execution>
|
|
||||||
<id>dstu2</id>
|
|
||||||
<goals>
|
|
||||||
<goal>integration-test</goal>
|
|
||||||
<goal>verify</goal>
|
|
||||||
</goals>
|
|
||||||
<configuration>
|
|
||||||
<includes>
|
|
||||||
<include>**/*Dstu2IT.java</include>
|
|
||||||
</includes>
|
|
||||||
<classpathDependencyExcludes>
|
|
||||||
<classpathDependencyExclude>org.codehaus.woodstox:woodstox-core-asl</classpathDependencyExclude>
|
|
||||||
<classpathDependencyExclude>org.codehaus.woodstox:stax2-api</classpathDependencyExclude>
|
|
||||||
</classpathDependencyExcludes>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
<execution>
|
|
||||||
<id>dstu3</id>
|
|
||||||
<goals>
|
|
||||||
<goal>integration-test</goal>
|
|
||||||
<goal>verify</goal>
|
|
||||||
</goals>
|
|
||||||
<configuration>
|
|
||||||
<includes>
|
|
||||||
<include>**/*Dstu3IT.java</include>
|
|
||||||
</includes>
|
|
||||||
<classpathDependencyExcludes>
|
|
||||||
<classpathDependencyExclude>org.codehaus.woodstox:woodstox-core-asl</classpathDependencyExclude>
|
|
||||||
<classpathDependencyExclude>org.codehaus.woodstox:stax2-api</classpathDependencyExclude>
|
|
||||||
</classpathDependencyExcludes>
|
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
|
|
|
@ -9,6 +9,7 @@ import java.util.zip.ZipFile;
|
||||||
|
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.apache.commons.io.filefilter.WildcardFileFilter;
|
import org.apache.commons.io.filefilter.WildcardFileFilter;
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
@ -21,6 +22,7 @@ public class BuiltJarDstu2ShadeIT {
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BuiltJarDstu2ShadeIT.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BuiltJarDstu2ShadeIT.class);
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@Ignore
|
||||||
public void testParserXml() throws Exception {
|
public void testParserXml() throws Exception {
|
||||||
|
|
||||||
FhirContext ctx = FhirContext.forDstu2();
|
FhirContext ctx = FhirContext.forDstu2();
|
||||||
|
|
|
@ -7,7 +7,6 @@ import static org.mockito.Mockito.when;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
import org.apache.http.client.ClientProtocolException;
|
|
||||||
import org.hl7.fhir.dstu3.model.*;
|
import org.hl7.fhir.dstu3.model.*;
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
|
@ -82,6 +81,7 @@ public class GenericClientDstu3IT {
|
||||||
* TODO: narratives don't work without stax
|
* TODO: narratives don't work without stax
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
|
@Ignore
|
||||||
public void testBinaryCreateWithFhirContentType() throws Exception {
|
public void testBinaryCreateWithFhirContentType() throws Exception {
|
||||||
IParser p = ourCtx.newXmlParser();
|
IParser p = ourCtx.newXmlParser();
|
||||||
|
|
||||||
|
@ -142,7 +142,7 @@ public class GenericClientDstu3IT {
|
||||||
.returnBundle(Bundle.class)
|
.returnBundle(Bundle.class)
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
assertEquals("http://example.com/fhir/Patient", capt.getAllValues().get(idx).url().toString());
|
assertEquals("http://example.com/fhir/Patient?_format=json", capt.getAllValues().get(idx).url().toString());
|
||||||
idx++;
|
idx++;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -177,12 +177,12 @@ public class GenericClientDstu3IT {
|
||||||
Request request = capt.getAllValues().get(0);
|
Request request = capt.getAllValues().get(0);
|
||||||
ourLog.info(request.headers().toString());
|
ourLog.info(request.headers().toString());
|
||||||
|
|
||||||
assertEquals("http://example.com/fhir/Binary", request.url().toString());
|
assertEquals("http://example.com/fhir/Binary?_format=json", request.url().toString());
|
||||||
validateUserAgent(capt);
|
validateUserAgent(capt);
|
||||||
|
|
||||||
assertEquals(Constants.CT_FHIR_XML_NEW + ";charset=utf-8", request.body().contentType().toString().toLowerCase().replace(" ", ""));
|
assertEquals(Constants.CT_FHIR_JSON_NEW + ";charset=utf-8", request.body().contentType().toString().toLowerCase().replace(" ", ""));
|
||||||
assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_NON_LEGACY, request.header("Accept"));
|
assertEquals(Constants.HEADER_ACCEPT_VALUE_JSON_NON_LEGACY, request.header("Accept"));
|
||||||
assertArrayEquals(new byte[] { 0, 1, 2, 3, 4 }, ourCtx.newXmlParser().parseResource(Binary.class, extractBodyAsString(capt)).getContent());
|
assertArrayEquals(new byte[] { 0, 1, 2, 3, 4 }, ourCtx.newJsonParser().parseResource(Binary.class, extractBodyAsString(capt)).getContent());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,11 +257,11 @@ public class GenericClientDstu3IT {
|
||||||
assertNotNull(outcome.getResource());
|
assertNotNull(outcome.getResource());
|
||||||
|
|
||||||
assertEquals("<div xmlns=\"http://www.w3.org/1999/xhtml\">FINAL VALUE</div>", ((Patient) outcome.getResource()).getText().getDivAsString());
|
assertEquals("<div xmlns=\"http://www.w3.org/1999/xhtml\">FINAL VALUE</div>", ((Patient) outcome.getResource()).getText().getDivAsString());
|
||||||
assertEquals("http://example.com/fhir/Patient", capt.getAllValues().get(0).url().toString());
|
assertEquals("http://example.com/fhir/Patient?_format=json", capt.getAllValues().get(0).url().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private ArgumentCaptor<Request> prepareClientForSearchResponse() throws IOException, ClientProtocolException {
|
private ArgumentCaptor<Request> prepareClientForSearchResponse() throws IOException {
|
||||||
final String respString = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}";
|
final String respString = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}";
|
||||||
myHttpResponse = new Response.Builder()
|
myHttpResponse = new Response.Builder()
|
||||||
.request(myRequest)
|
.request(myRequest)
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.codehaus.woodstox</groupId>
|
<groupId>org.codehaus.woodstox</groupId>
|
||||||
<artifactId>woodstox-core-asl</artifactId>
|
<artifactId>woodstox-core-asl</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- Only required for narrative generator support -->
|
<!-- Only required for narrative generator support -->
|
||||||
|
@ -124,6 +125,7 @@
|
||||||
<argLine>${argLine} -Dfile.encoding=UTF-8 -Xmx712m</argLine>
|
<argLine>${argLine} -Dfile.encoding=UTF-8 -Xmx712m</argLine>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<!--
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-source-plugin</artifactId>
|
<artifactId>maven-source-plugin</artifactId>
|
||||||
|
@ -136,6 +138,7 @@
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
-->
|
||||||
</plugins>
|
</plugins>
|
||||||
<resources>
|
<resources>
|
||||||
<resource>
|
<resource>
|
||||||
|
|
|
@ -78,6 +78,10 @@ public enum FhirVersionEnum {
|
||||||
return myVersionImplementation;
|
return myVersionImplementation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isEqualOrNewerThan(FhirVersionEnum theVersion) {
|
||||||
|
return ordinal() >= theVersion.ordinal();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isEquivalentTo(FhirVersionEnum theVersion) {
|
public boolean isEquivalentTo(FhirVersionEnum theVersion) {
|
||||||
if (this.equals(theVersion)) {
|
if (this.equals(theVersion)) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -152,7 +152,8 @@ public class RuntimeSearchParam {
|
||||||
public enum RuntimeSearchParamStatusEnum {
|
public enum RuntimeSearchParamStatusEnum {
|
||||||
ACTIVE,
|
ACTIVE,
|
||||||
DRAFT,
|
DRAFT,
|
||||||
RETIRED
|
RETIRED,
|
||||||
|
UNKNOWN
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,29 +20,29 @@ package ca.uhn.fhir.model.primitive;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.io.StringReader;
|
|
||||||
import java.io.StringWriter;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.xml.stream.FactoryConfigurationError;
|
|
||||||
import javax.xml.stream.XMLEventReader;
|
|
||||||
import javax.xml.stream.XMLEventWriter;
|
|
||||||
import javax.xml.stream.XMLStreamException;
|
|
||||||
import javax.xml.stream.events.XMLEvent;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.context.ConfigurationException;
|
|
||||||
import ca.uhn.fhir.model.api.BasePrimitive;
|
import ca.uhn.fhir.model.api.BasePrimitive;
|
||||||
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
|
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
|
||||||
import ca.uhn.fhir.model.api.annotation.SimpleSetter;
|
import ca.uhn.fhir.model.api.annotation.SimpleSetter;
|
||||||
import ca.uhn.fhir.parser.DataFormatException;
|
import ca.uhn.fhir.parser.DataFormatException;
|
||||||
|
import ca.uhn.fhir.util.XmlDetectionUtil;
|
||||||
import ca.uhn.fhir.util.XmlUtil;
|
import ca.uhn.fhir.util.XmlUtil;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Note that as of HAPI FHIR 3.1.0, this method no longer uses
|
||||||
|
* the StAX XMLEvent type as the XML representation, and uses a
|
||||||
|
* String instead. If you need to work with XML as StAX events, you
|
||||||
|
* can use the {@link XmlUtil#parse(String)} and {@link XmlUtil#encode(List)}
|
||||||
|
* methods to do so.
|
||||||
|
*/
|
||||||
@DatatypeDef(name = "xhtml")
|
@DatatypeDef(name = "xhtml")
|
||||||
public class XhtmlDt extends BasePrimitive<List<XMLEvent>> {
|
public class XhtmlDt extends BasePrimitive<String> {
|
||||||
|
|
||||||
private static final String DECL_XMLNS = " xmlns=\"http://www.w3.org/1999/xhtml\"";
|
private static final String DECL_XMLNS = " xmlns=\"http://www.w3.org/1999/xhtml\"";
|
||||||
private static final String DIV_OPEN_FIRST = "<div" + DECL_XMLNS + ">";
|
public static final String DIV_OPEN_FIRST = "<div" + DECL_XMLNS + ">";
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -63,29 +63,12 @@ public class XhtmlDt extends BasePrimitive<List<XMLEvent>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String encode(List<XMLEvent> theValue) {
|
protected String encode(String theValue) {
|
||||||
try {
|
return theValue;
|
||||||
StringWriter w = new StringWriter();
|
|
||||||
XMLEventWriter ew = XmlUtil.createXmlFragmentWriter(w);
|
|
||||||
|
|
||||||
for (XMLEvent next : getValue()) {
|
|
||||||
if (next.isCharacters()) {
|
|
||||||
ew.add(next);
|
|
||||||
} else {
|
|
||||||
ew.add(next);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ew.close();
|
|
||||||
return w.toString();
|
|
||||||
} catch (XMLStreamException e) {
|
|
||||||
throw new DataFormatException("Problem with the contained XML events", e);
|
|
||||||
} catch (FactoryConfigurationError e) {
|
|
||||||
throw new ConfigurationException(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasContent() {
|
public boolean hasContent() {
|
||||||
return getValue() != null && getValue().size() > 0;
|
return isNotBlank(getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -94,40 +77,37 @@ public class XhtmlDt extends BasePrimitive<List<XMLEvent>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<XMLEvent> parse(String theValue) {
|
protected String parse(String theValue) {
|
||||||
String val = theValue.trim();
|
if (XmlDetectionUtil.isStaxPresent()) {
|
||||||
if (!val.startsWith("<")) {
|
// for validation
|
||||||
val = DIV_OPEN_FIRST + val + "</div>";
|
XmlUtil.parse(theValue);
|
||||||
}
|
|
||||||
boolean hasProcessingInstruction = val.startsWith("<?");
|
|
||||||
if (hasProcessingInstruction && val.endsWith("?>")) {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
return theValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
try {
|
/**
|
||||||
ArrayList<XMLEvent> value = new ArrayList<XMLEvent>();
|
* Note that as of HAPI FHIR 3.1.0, this method no longer uses
|
||||||
StringReader reader = new StringReader(val);
|
* the StAX XMLEvent type as the XML representation, and uses a
|
||||||
XMLEventReader er = XmlUtil.createXmlReader(reader);
|
* String instead. If you need to work with XML as StAX events, you
|
||||||
boolean first = true;
|
* can use the {@link XmlUtil#parse(String)} and {@link XmlUtil#encode(List)}
|
||||||
while (er.hasNext()) {
|
* methods to do so.
|
||||||
XMLEvent next = er.nextEvent();
|
*/
|
||||||
if (first) {
|
@Override
|
||||||
first = false;
|
public String getValue() {
|
||||||
continue;
|
return super.getValue();
|
||||||
}
|
}
|
||||||
if (er.hasNext()) {
|
|
||||||
// don't add the last event
|
|
||||||
value.add(next);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
|
|
||||||
} catch (XMLStreamException e) {
|
/**
|
||||||
throw new DataFormatException("String does not appear to be valid XML/XHTML (error is \"" + e.getMessage() + "\"): " + theValue, e);
|
* Note that as of HAPI FHIR 3.1.0, this method no longer uses
|
||||||
} catch (FactoryConfigurationError e) {
|
* the StAX XMLEvent type as the XML representation, and uses a
|
||||||
throw new ConfigurationException(e);
|
* String instead. If you need to work with XML as StAX events, you
|
||||||
}
|
* can use the {@link XmlUtil#parse(String)} and {@link XmlUtil#encode(List)}
|
||||||
|
* methods to do so.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public BasePrimitive<String> setValue(String theValue) throws DataFormatException {
|
||||||
|
return super.setValue(theValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -43,7 +43,7 @@ public abstract class BaseParser implements IParser {
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseParser.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseParser.class);
|
||||||
|
|
||||||
private ContainedResources myContainedResources;
|
private ContainedResources myContainedResources;
|
||||||
|
private boolean myEncodeElementsAppliesToChildResourcesOnly;
|
||||||
private FhirContext myContext;
|
private FhirContext myContext;
|
||||||
private Set<String> myDontEncodeElements;
|
private Set<String> myDontEncodeElements;
|
||||||
private boolean myDontEncodeElementsIncludesStars;
|
private boolean myDontEncodeElementsIncludesStars;
|
||||||
|
@ -556,6 +556,16 @@ public abstract class BaseParser implements IParser {
|
||||||
&& theIncludedResource == false;
|
&& theIncludedResource == false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEncodeElementsAppliesToChildResourcesOnly() {
|
||||||
|
return myEncodeElementsAppliesToChildResourcesOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEncodeElementsAppliesToChildResourcesOnly(boolean theEncodeElementsAppliesToChildResourcesOnly) {
|
||||||
|
myEncodeElementsAppliesToChildResourcesOnly = theEncodeElementsAppliesToChildResourcesOnly;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isOmitResourceId() {
|
public boolean isOmitResourceId() {
|
||||||
return myOmitResourceId;
|
return myOmitResourceId;
|
||||||
|
@ -1039,7 +1049,13 @@ public abstract class BaseParser implements IParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkIfParentShouldBeEncodedAndBuildPath(StringBuilder thePathBuilder, boolean theStarPass) {
|
private boolean checkIfParentShouldBeEncodedAndBuildPath(StringBuilder thePathBuilder, boolean theStarPass) {
|
||||||
return checkIfPathMatchesForEncoding(thePathBuilder, theStarPass, myEncodeElementsAppliesToResourceTypes, myEncodeElements, true);
|
Set<String> encodeElements = myEncodeElements;
|
||||||
|
if (encodeElements != null && encodeElements.isEmpty() == false) {
|
||||||
|
if (isEncodeElementsAppliesToChildResourcesOnly() && !mySubResource) {
|
||||||
|
encodeElements = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return checkIfPathMatchesForEncoding(thePathBuilder, theStarPass, myEncodeElementsAppliesToResourceTypes, encodeElements, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkIfParentShouldNotBeEncodedAndBuildPath(StringBuilder thePathBuilder, boolean theStarPass) {
|
private boolean checkIfParentShouldNotBeEncodedAndBuildPath(StringBuilder thePathBuilder, boolean theStarPass) {
|
||||||
|
@ -1058,6 +1074,9 @@ public abstract class BaseParser implements IParser {
|
||||||
} else {
|
} else {
|
||||||
thePathBuilder.append(myResDef.getName());
|
thePathBuilder.append(myResDef.getName());
|
||||||
}
|
}
|
||||||
|
if (theElements == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (theElements.contains(thePathBuilder.toString())) {
|
if (theElements.contains(thePathBuilder.toString())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -206,6 +206,22 @@ public interface IParser {
|
||||||
*/
|
*/
|
||||||
void setEncodeElements(Set<String> theEncodeElements);
|
void setEncodeElements(Set<String> theEncodeElements);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set to <code>true</code> (default is false), the values supplied
|
||||||
|
* to {@link #setEncodeElements(Set)} will not be applied to the root
|
||||||
|
* resource (typically a Bundle), but will be applied to any sub-resources
|
||||||
|
* contained within it (i.e. search result resources in that bundle)
|
||||||
|
*/
|
||||||
|
void setEncodeElementsAppliesToChildResourcesOnly(boolean theEncodeElementsAppliesToChildResourcesOnly);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set to <code>true</code> (default is false), the values supplied
|
||||||
|
* to {@link #setEncodeElements(Set)} will not be applied to the root
|
||||||
|
* resource (typically a Bundle), but will be applied to any sub-resources
|
||||||
|
* contained within it (i.e. search result resources in that bundle)
|
||||||
|
*/
|
||||||
|
boolean isEncodeElementsAppliesToChildResourcesOnly();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If provided, tells the parse which resource types to apply {@link #setEncodeElements(Set) encode elements} to. Any
|
* If provided, tells the parse which resource types to apply {@link #setEncodeElements(Set) encode elements} to. Any
|
||||||
* resource types not specified here will be encoded completely, with no elements excluded.
|
* resource types not specified here will be encoded completely, with no elements excluded.
|
||||||
|
|
|
@ -19,19 +19,6 @@ package ca.uhn.fhir.parser;
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
import static org.apache.commons.lang3.StringUtils.*;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.apache.commons.lang3.Validate;
|
|
||||||
import org.apache.commons.lang3.text.WordUtils;
|
|
||||||
import org.hl7.fhir.instance.model.api.*;
|
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
import com.google.gson.GsonBuilder;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.context.*;
|
import ca.uhn.fhir.context.*;
|
||||||
import ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum;
|
import ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum;
|
||||||
|
@ -46,9 +33,27 @@ import ca.uhn.fhir.parser.json.*;
|
||||||
import ca.uhn.fhir.parser.json.JsonLikeValue.ScalarType;
|
import ca.uhn.fhir.parser.json.JsonLikeValue.ScalarType;
|
||||||
import ca.uhn.fhir.parser.json.JsonLikeValue.ValueType;
|
import ca.uhn.fhir.parser.json.JsonLikeValue.ValueType;
|
||||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||||
|
import ca.uhn.fhir.util.BinaryUtil;
|
||||||
import ca.uhn.fhir.util.ElementUtil;
|
import ca.uhn.fhir.util.ElementUtil;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
import org.apache.commons.lang3.text.WordUtils;
|
||||||
|
import org.hl7.fhir.instance.model.api.*;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import static ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum.ID_DATATYPE;
|
import static ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum.ID_DATATYPE;
|
||||||
import static ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum.PRIMITIVE_DATATYPE;
|
import static ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum.PRIMITIVE_DATATYPE;
|
||||||
|
import static org.apache.commons.lang3.StringUtils.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is the FHIR JSON parser/encoder. Users should not interact with this class directly, but should use
|
* This class is the FHIR JSON parser/encoder. Users should not interact with this class directly, but should use
|
||||||
|
@ -88,7 +93,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean addToHeldExtensions(int valueIdx, List<? extends IBaseExtension<?, ?>> ext, ArrayList<ArrayList<HeldExtension>> list, boolean theIsModifier, CompositeChildElement theChildElem,
|
private boolean addToHeldExtensions(int valueIdx, List<? extends IBaseExtension<?, ?>> ext, ArrayList<ArrayList<HeldExtension>> list, boolean theIsModifier, CompositeChildElement theChildElem,
|
||||||
CompositeChildElement theParent) {
|
CompositeChildElement theParent) {
|
||||||
if (ext.size() > 0) {
|
if (ext.size() > 0) {
|
||||||
list.ensureCapacity(valueIdx);
|
list.ensureCapacity(valueIdx);
|
||||||
while (list.size() <= valueIdx) {
|
while (list.size() <= valueIdx) {
|
||||||
|
@ -139,12 +144,6 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doEncodeResourceToWriter(IBaseResource theResource, Writer theWriter) throws IOException {
|
|
||||||
JsonLikeWriter eventWriter = createJsonWriter(theWriter);
|
|
||||||
doEncodeResourceToJsonLikeWriter(theResource, eventWriter);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void doEncodeResourceToJsonLikeWriter(IBaseResource theResource, JsonLikeWriter theEventWriter) throws IOException {
|
public void doEncodeResourceToJsonLikeWriter(IBaseResource theResource, JsonLikeWriter theEventWriter) throws IOException {
|
||||||
if (myPrettyPrint) {
|
if (myPrettyPrint) {
|
||||||
theEventWriter.setPrettyPrint(myPrettyPrint);
|
theEventWriter.setPrettyPrint(myPrettyPrint);
|
||||||
|
@ -156,6 +155,12 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
||||||
theEventWriter.flush();
|
theEventWriter.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doEncodeResourceToWriter(IBaseResource theResource, Writer theWriter) throws IOException {
|
||||||
|
JsonLikeWriter eventWriter = createJsonWriter(theWriter);
|
||||||
|
doEncodeResourceToJsonLikeWriter(theResource, eventWriter);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T extends IBaseResource> T doParseResource(Class<T> theResourceType, Reader theReader) {
|
public <T extends IBaseResource> T doParseResource(Class<T> theResourceType, Reader theReader) {
|
||||||
JsonLikeStructure jsonStructure = new GsonStructure();
|
JsonLikeStructure jsonStructure = new GsonStructure();
|
||||||
|
@ -191,136 +196,136 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void encodeChildElementToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonLikeWriter theEventWriter, IBase theNextValue,
|
private void encodeChildElementToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonLikeWriter theEventWriter, IBase theNextValue,
|
||||||
BaseRuntimeElementDefinition<?> theChildDef, String theChildName, boolean theContainedResource, boolean theSubResource, CompositeChildElement theChildElem,
|
BaseRuntimeElementDefinition<?> theChildDef, String theChildName, boolean theContainedResource, boolean theSubResource, CompositeChildElement theChildElem,
|
||||||
boolean theForceEmpty) throws IOException {
|
boolean theForceEmpty) throws IOException {
|
||||||
|
|
||||||
switch (theChildDef.getChildType()) {
|
switch (theChildDef.getChildType()) {
|
||||||
case ID_DATATYPE: {
|
case ID_DATATYPE: {
|
||||||
IIdType value = (IIdType) theNextValue;
|
IIdType value = (IIdType) theNextValue;
|
||||||
String encodedValue = "id".equals(theChildName) ? value.getIdPart() : value.getValue();
|
String encodedValue = "id".equals(theChildName) ? value.getIdPart() : value.getValue();
|
||||||
if (isBlank(encodedValue)) {
|
if (isBlank(encodedValue)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (theChildName != null) {
|
if (theChildName != null) {
|
||||||
write(theEventWriter, theChildName, encodedValue);
|
write(theEventWriter, theChildName, encodedValue);
|
||||||
} else {
|
} else {
|
||||||
theEventWriter.write(encodedValue);
|
theEventWriter.write(encodedValue);
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case PRIMITIVE_DATATYPE: {
|
|
||||||
final IPrimitiveType<?> value = (IPrimitiveType<?>) theNextValue;
|
|
||||||
if (isBlank(value.getValueAsString())) {
|
|
||||||
if (theForceEmpty) {
|
|
||||||
theEventWriter.writeNull();
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case PRIMITIVE_DATATYPE: {
|
||||||
if (value instanceof IBaseIntegerDatatype) {
|
final IPrimitiveType<?> value = (IPrimitiveType<?>) theNextValue;
|
||||||
if (theChildName != null) {
|
if (isBlank(value.getValueAsString())) {
|
||||||
write(theEventWriter, theChildName, ((IBaseIntegerDatatype) value).getValue());
|
if (theForceEmpty) {
|
||||||
} else {
|
theEventWriter.writeNull();
|
||||||
theEventWriter.write(((IBaseIntegerDatatype) value).getValue());
|
|
||||||
}
|
|
||||||
} else if (value instanceof IBaseDecimalDatatype) {
|
|
||||||
BigDecimal decimalValue = ((IBaseDecimalDatatype) value).getValue();
|
|
||||||
decimalValue = new BigDecimal(decimalValue.toString()) {
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return value.getValueAsString();
|
|
||||||
}
|
}
|
||||||
};
|
break;
|
||||||
if (theChildName != null) {
|
|
||||||
write(theEventWriter, theChildName, decimalValue);
|
|
||||||
} else {
|
|
||||||
theEventWriter.write(decimalValue);
|
|
||||||
}
|
}
|
||||||
} else if (value instanceof IBaseBooleanDatatype) {
|
|
||||||
if (theChildName != null) {
|
if (value instanceof IBaseIntegerDatatype) {
|
||||||
write(theEventWriter, theChildName, ((IBaseBooleanDatatype) value).getValue());
|
if (theChildName != null) {
|
||||||
|
write(theEventWriter, theChildName, ((IBaseIntegerDatatype) value).getValue());
|
||||||
|
} else {
|
||||||
|
theEventWriter.write(((IBaseIntegerDatatype) value).getValue());
|
||||||
|
}
|
||||||
|
} else if (value instanceof IBaseDecimalDatatype) {
|
||||||
|
BigDecimal decimalValue = ((IBaseDecimalDatatype) value).getValue();
|
||||||
|
decimalValue = new BigDecimal(decimalValue.toString()) {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return value.getValueAsString();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (theChildName != null) {
|
||||||
|
write(theEventWriter, theChildName, decimalValue);
|
||||||
|
} else {
|
||||||
|
theEventWriter.write(decimalValue);
|
||||||
|
}
|
||||||
|
} else if (value instanceof IBaseBooleanDatatype) {
|
||||||
|
if (theChildName != null) {
|
||||||
|
write(theEventWriter, theChildName, ((IBaseBooleanDatatype) value).getValue());
|
||||||
|
} else {
|
||||||
|
Boolean booleanValue = ((IBaseBooleanDatatype) value).getValue();
|
||||||
|
if (booleanValue != null) {
|
||||||
|
theEventWriter.write(booleanValue.booleanValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Boolean booleanValue = ((IBaseBooleanDatatype) value).getValue();
|
String valueStr = value.getValueAsString();
|
||||||
if (booleanValue != null) {
|
if (theChildName != null) {
|
||||||
theEventWriter.write(booleanValue.booleanValue());
|
write(theEventWriter, theChildName, valueStr);
|
||||||
|
} else {
|
||||||
|
theEventWriter.write(valueStr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
break;
|
||||||
String valueStr = value.getValueAsString();
|
}
|
||||||
|
case RESOURCE_BLOCK:
|
||||||
|
case COMPOSITE_DATATYPE: {
|
||||||
if (theChildName != null) {
|
if (theChildName != null) {
|
||||||
write(theEventWriter, theChildName, valueStr);
|
theEventWriter.beginObject(theChildName);
|
||||||
} else {
|
} else {
|
||||||
theEventWriter.write(valueStr);
|
theEventWriter.beginObject();
|
||||||
}
|
}
|
||||||
|
encodeCompositeElementToStreamWriter(theResDef, theResource, theNextValue, theEventWriter, theContainedResource, theSubResource, theChildElem);
|
||||||
|
theEventWriter.endObject();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
case CONTAINED_RESOURCE_LIST:
|
||||||
}
|
case CONTAINED_RESOURCES: {
|
||||||
case RESOURCE_BLOCK:
|
|
||||||
case COMPOSITE_DATATYPE: {
|
|
||||||
if (theChildName != null) {
|
|
||||||
theEventWriter.beginObject(theChildName);
|
|
||||||
} else {
|
|
||||||
theEventWriter.beginObject();
|
|
||||||
}
|
|
||||||
encodeCompositeElementToStreamWriter(theResDef, theResource, theNextValue, theEventWriter, theContainedResource, theSubResource, theChildElem);
|
|
||||||
theEventWriter.endObject();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CONTAINED_RESOURCE_LIST:
|
|
||||||
case CONTAINED_RESOURCES: {
|
|
||||||
/*
|
/*
|
||||||
* Disabled per #103 ContainedDt value = (ContainedDt) theNextValue; for (IResource next :
|
* Disabled per #103 ContainedDt value = (ContainedDt) theNextValue; for (IResource next :
|
||||||
* value.getContainedResources()) { if (getContainedResources().getResourceId(next) != null) { continue; }
|
* value.getContainedResources()) { if (getContainedResources().getResourceId(next) != null) { continue; }
|
||||||
* encodeResourceToJsonStreamWriter(theResDef, next, theWriter, null, true,
|
* encodeResourceToJsonStreamWriter(theResDef, next, theWriter, null, true,
|
||||||
* fixContainedResourceId(next.getId().getValue())); }
|
* fixContainedResourceId(next.getId().getValue())); }
|
||||||
*/
|
*/
|
||||||
List<IBaseResource> containedResources = getContainedResources().getContainedResources();
|
List<IBaseResource> containedResources = getContainedResources().getContainedResources();
|
||||||
if (containedResources.size() > 0) {
|
if (containedResources.size() > 0) {
|
||||||
beginArray(theEventWriter, theChildName);
|
beginArray(theEventWriter, theChildName);
|
||||||
|
|
||||||
for (IBaseResource next : containedResources) {
|
for (IBaseResource next : containedResources) {
|
||||||
IIdType resourceId = getContainedResources().getResourceId(next);
|
IIdType resourceId = getContainedResources().getResourceId(next);
|
||||||
encodeResourceToJsonStreamWriter(theResDef, next, theEventWriter, null, true, false, fixContainedResourceId(resourceId.getValue()));
|
encodeResourceToJsonStreamWriter(theResDef, next, theEventWriter, null, true, false, fixContainedResourceId(resourceId.getValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
theEventWriter.endArray();
|
theEventWriter.endArray();
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case PRIMITIVE_XHTML_HL7ORG:
|
|
||||||
case PRIMITIVE_XHTML: {
|
|
||||||
if (!isSuppressNarratives()) {
|
|
||||||
IPrimitiveType<?> dt = (IPrimitiveType<?>) theNextValue;
|
|
||||||
if (theChildName != null) {
|
|
||||||
write(theEventWriter, theChildName, dt.getValueAsString());
|
|
||||||
} else {
|
|
||||||
theEventWriter.write(dt.getValueAsString());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (theChildName != null) {
|
|
||||||
// do nothing
|
|
||||||
} else {
|
|
||||||
theEventWriter.writeNull();
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
case PRIMITIVE_XHTML_HL7ORG:
|
||||||
}
|
case PRIMITIVE_XHTML: {
|
||||||
case RESOURCE:
|
if (!isSuppressNarratives()) {
|
||||||
IBaseResource resource = (IBaseResource) theNextValue;
|
IPrimitiveType<?> dt = (IPrimitiveType<?>) theNextValue;
|
||||||
RuntimeResourceDefinition def = myContext.getResourceDefinition(resource);
|
if (theChildName != null) {
|
||||||
encodeResourceToJsonStreamWriter(def, resource, theEventWriter, theChildName, false, true);
|
write(theEventWriter, theChildName, dt.getValueAsString());
|
||||||
break;
|
} else {
|
||||||
case UNDECL_EXT:
|
theEventWriter.write(dt.getValueAsString());
|
||||||
default:
|
}
|
||||||
throw new IllegalStateException("Should not have this state here: " + theChildDef.getChildType().name());
|
} else {
|
||||||
|
if (theChildName != null) {
|
||||||
|
// do nothing
|
||||||
|
} else {
|
||||||
|
theEventWriter.writeNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case RESOURCE:
|
||||||
|
IBaseResource resource = (IBaseResource) theNextValue;
|
||||||
|
RuntimeResourceDefinition def = myContext.getResourceDefinition(resource);
|
||||||
|
encodeResourceToJsonStreamWriter(def, resource, theEventWriter, theChildName, false, true);
|
||||||
|
break;
|
||||||
|
case UNDECL_EXT:
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("Should not have this state here: " + theChildDef.getChildType().name());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void encodeCompositeElementChildrenToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theElement, JsonLikeWriter theEventWriter,
|
private void encodeCompositeElementChildrenToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theElement, JsonLikeWriter theEventWriter,
|
||||||
boolean theContainedResource, boolean theSubResource, CompositeChildElement theParent) throws IOException {
|
boolean theContainedResource, boolean theSubResource, CompositeChildElement theParent) throws IOException {
|
||||||
|
|
||||||
{
|
{
|
||||||
String elementId = getCompositeElementId(theElement);
|
String elementId = getCompositeElementId(theElement);
|
||||||
|
@ -335,7 +340,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
||||||
BaseRuntimeChildDefinition nextChild = nextChildElem.getDef();
|
BaseRuntimeChildDefinition nextChild = nextChildElem.getDef();
|
||||||
|
|
||||||
if (nextChildElem.getDef().getElementName().equals("extension") || nextChildElem.getDef().getElementName().equals("modifierExtension")
|
if (nextChildElem.getDef().getElementName().equals("extension") || nextChildElem.getDef().getElementName().equals("modifierExtension")
|
||||||
|| nextChild instanceof RuntimeChildDeclaredExtensionDefinition) {
|
|| nextChild instanceof RuntimeChildDeclaredExtensionDefinition) {
|
||||||
if (!haveWrittenExtensions) {
|
if (!haveWrittenExtensions) {
|
||||||
extractAndWriteExtensionsAsDirectChild(theElement, theEventWriter, myContext.getElementDefinition(theElement.getClass()), theResDef, theResource, nextChildElem, theParent);
|
extractAndWriteExtensionsAsDirectChild(theElement, theEventWriter, myContext.getElementDefinition(theElement.getClass()), theResDef, theResource, nextChildElem, theParent);
|
||||||
haveWrittenExtensions = true;
|
haveWrittenExtensions = true;
|
||||||
|
@ -451,15 +456,15 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
||||||
if (nextChild.getMax() > 1 || nextChild.getMax() == Child.MAX_UNLIMITED) {
|
if (nextChild.getMax() > 1 || nextChild.getMax() == Child.MAX_UNLIMITED) {
|
||||||
beginArray(theEventWriter, childName);
|
beginArray(theEventWriter, childName);
|
||||||
inArray = true;
|
inArray = true;
|
||||||
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childDef, null, theContainedResource, theSubResource,nextChildElem, force);
|
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childDef, null, theContainedResource, theSubResource, nextChildElem, force);
|
||||||
} else if (nextChild instanceof RuntimeChildNarrativeDefinition && theContainedResource) {
|
} else if (nextChild instanceof RuntimeChildNarrativeDefinition && theContainedResource) {
|
||||||
// suppress narratives from contained resources
|
// suppress narratives from contained resources
|
||||||
} else {
|
} else {
|
||||||
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childDef, childName, theContainedResource, theSubResource,nextChildElem, false);
|
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childDef, childName, theContainedResource, theSubResource, nextChildElem, false);
|
||||||
}
|
}
|
||||||
currentChildName = childName;
|
currentChildName = childName;
|
||||||
} else {
|
} else {
|
||||||
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childDef, null, theContainedResource,theSubResource, nextChildElem, force);
|
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childDef, null, theContainedResource, theSubResource, nextChildElem, force);
|
||||||
}
|
}
|
||||||
|
|
||||||
valueIdx++;
|
valueIdx++;
|
||||||
|
@ -541,7 +546,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void encodeCompositeElementToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theNextValue, JsonLikeWriter theEventWriter, boolean theContainedResource, boolean theSubResource,
|
private void encodeCompositeElementToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theNextValue, JsonLikeWriter theEventWriter, boolean theContainedResource, boolean theSubResource,
|
||||||
CompositeChildElement theParent) throws IOException, DataFormatException {
|
CompositeChildElement theParent) throws IOException, DataFormatException {
|
||||||
|
|
||||||
writeCommentsPreAndPost(theNextValue, theEventWriter);
|
writeCommentsPreAndPost(theNextValue, theEventWriter);
|
||||||
encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theNextValue, theEventWriter, theContainedResource, theSubResource, theParent);
|
encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theNextValue, theEventWriter, theContainedResource, theSubResource, theParent);
|
||||||
|
@ -554,14 +559,14 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
||||||
|
|
||||||
if (theResource.getStructureFhirVersionEnum() != myContext.getVersion().getVersion()) {
|
if (theResource.getStructureFhirVersionEnum() != myContext.getVersion().getVersion()) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"This parser is for FHIR version " + myContext.getVersion().getVersion() + " - Can not encode a structure for version " + theResource.getStructureFhirVersionEnum());
|
"This parser is for FHIR version " + myContext.getVersion().getVersion() + " - Can not encode a structure for version " + theResource.getStructureFhirVersionEnum());
|
||||||
}
|
}
|
||||||
|
|
||||||
doEncodeResourceToJsonLikeWriter(theResource, theJsonLikeWriter);
|
doEncodeResourceToJsonLikeWriter(theResource, theJsonLikeWriter);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonLikeWriter theEventWriter, String theObjectNameOrNull,
|
private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonLikeWriter theEventWriter, String theObjectNameOrNull,
|
||||||
boolean theContainedResource, boolean theSubResource) throws IOException {
|
boolean theContainedResource, boolean theSubResource) throws IOException {
|
||||||
IIdType resourceId = null;
|
IIdType resourceId = null;
|
||||||
// if (theResource instanceof IResource) {
|
// if (theResource instanceof IResource) {
|
||||||
// IResource res = (IResource) theResource;
|
// IResource res = (IResource) theResource;
|
||||||
|
@ -598,7 +603,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonLikeWriter theEventWriter, String theObjectNameOrNull,
|
private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonLikeWriter theEventWriter, String theObjectNameOrNull,
|
||||||
boolean theContainedResource, boolean theSubResource, IIdType theResourceId) throws IOException {
|
boolean theContainedResource, boolean theSubResource, IIdType theResourceId) throws IOException {
|
||||||
if (!theContainedResource) {
|
if (!theContainedResource) {
|
||||||
super.containResourcesForEncoding(theResource);
|
super.containResourcesForEncoding(theResource);
|
||||||
}
|
}
|
||||||
|
@ -612,28 +617,28 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
write(theEventWriter, "resourceType", resDef.getName());
|
write(theEventWriter, "resourceType", resDef.getName());
|
||||||
if (theResourceId != null && theResourceId.hasIdPart()) {
|
if (theResourceId != null && theResourceId.hasIdPart()) {
|
||||||
write(theEventWriter, "id", theResourceId.getIdPart());
|
write(theEventWriter, "id", theResourceId.getIdPart());
|
||||||
final List<HeldExtension> extensions = new ArrayList<HeldExtension>(0);
|
final List<HeldExtension> extensions = new ArrayList<HeldExtension>(0);
|
||||||
final List<HeldExtension> modifierExtensions = new ArrayList<HeldExtension>(0);
|
final List<HeldExtension> modifierExtensions = new ArrayList<HeldExtension>(0);
|
||||||
// Undeclared extensions
|
// Undeclared extensions
|
||||||
extractUndeclaredExtensions(theResourceId, extensions, modifierExtensions, null, null);
|
extractUndeclaredExtensions(theResourceId, extensions, modifierExtensions, null, null);
|
||||||
boolean haveExtension = false;
|
boolean haveExtension = false;
|
||||||
if (!extensions.isEmpty()) {
|
if (!extensions.isEmpty()) {
|
||||||
haveExtension = true;
|
haveExtension = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (theResourceId.hasFormatComment() || haveExtension) {
|
if (theResourceId.hasFormatComment() || haveExtension) {
|
||||||
beginObject(theEventWriter, "_id");
|
beginObject(theEventWriter, "_id");
|
||||||
if (theResourceId.hasFormatComment()) {
|
if (theResourceId.hasFormatComment()) {
|
||||||
writeCommentsPreAndPost(theResourceId, theEventWriter);
|
writeCommentsPreAndPost(theResourceId, theEventWriter);
|
||||||
}
|
}
|
||||||
if (haveExtension) {
|
if (haveExtension) {
|
||||||
writeExtensionsAsDirectChild(theResource, theEventWriter, theResDef, extensions, modifierExtensions);
|
writeExtensionsAsDirectChild(theResource, theEventWriter, theResDef, extensions, modifierExtensions);
|
||||||
}
|
}
|
||||||
theEventWriter.endObject();
|
theEventWriter.endObject();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (theResource instanceof IResource) {
|
if (theResource instanceof IResource) {
|
||||||
IResource resource = (IResource) theResource;
|
IResource resource = (IResource) theResource;
|
||||||
|
@ -695,19 +700,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (theResource instanceof IBaseBinary) {
|
encodeCompositeElementToStreamWriter(theResDef, theResource, theResource, theEventWriter, theContainedResource, theSubResource, new CompositeChildElement(resDef, theSubResource));
|
||||||
IBaseBinary bin = (IBaseBinary) theResource;
|
|
||||||
String contentType = bin.getContentType();
|
|
||||||
if (isNotBlank(contentType)) {
|
|
||||||
write(theEventWriter, "contentType", contentType);
|
|
||||||
}
|
|
||||||
String contentAsBase64 = bin.getContentAsBase64();
|
|
||||||
if (isNotBlank(contentAsBase64)) {
|
|
||||||
write(theEventWriter, "content", contentAsBase64);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
encodeCompositeElementToStreamWriter(theResDef, theResource, theResource, theEventWriter, theContainedResource, theSubResource, new CompositeChildElement(resDef, theSubResource));
|
|
||||||
}
|
|
||||||
|
|
||||||
theEventWriter.endObject();
|
theEventWriter.endObject();
|
||||||
}
|
}
|
||||||
|
@ -720,7 +713,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
||||||
* @param theParent
|
* @param theParent
|
||||||
*/
|
*/
|
||||||
private void extractAndWriteExtensionsAsDirectChild(IBase theElement, JsonLikeWriter theEventWriter, BaseRuntimeElementDefinition<?> theElementDef, RuntimeResourceDefinition theResDef,
|
private void extractAndWriteExtensionsAsDirectChild(IBase theElement, JsonLikeWriter theEventWriter, BaseRuntimeElementDefinition<?> theElementDef, RuntimeResourceDefinition theResDef,
|
||||||
IBaseResource theResource, CompositeChildElement theChildElem, CompositeChildElement theParent) throws IOException {
|
IBaseResource theResource, CompositeChildElement theChildElem, CompositeChildElement theParent) throws IOException {
|
||||||
List<HeldExtension> extensions = new ArrayList<HeldExtension>(0);
|
List<HeldExtension> extensions = new ArrayList<HeldExtension>(0);
|
||||||
List<HeldExtension> modifierExtensions = new ArrayList<HeldExtension>(0);
|
List<HeldExtension> modifierExtensions = new ArrayList<HeldExtension>(0);
|
||||||
|
|
||||||
|
@ -737,7 +730,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void extractDeclaredExtensions(IBase theResource, BaseRuntimeElementDefinition<?> resDef, List<HeldExtension> extensions, List<HeldExtension> modifierExtensions,
|
private void extractDeclaredExtensions(IBase theResource, BaseRuntimeElementDefinition<?> resDef, List<HeldExtension> extensions, List<HeldExtension> modifierExtensions,
|
||||||
CompositeChildElement theChildElem) {
|
CompositeChildElement theChildElem) {
|
||||||
for (RuntimeChildDeclaredExtensionDefinition nextDef : resDef.getExtensionsNonModifier()) {
|
for (RuntimeChildDeclaredExtensionDefinition nextDef : resDef.getExtensionsNonModifier()) {
|
||||||
for (IBase nextValue : nextDef.getAccessor().getValues(theResource)) {
|
for (IBase nextValue : nextDef.getAccessor().getValues(theResource)) {
|
||||||
if (nextValue != null) {
|
if (nextValue != null) {
|
||||||
|
@ -761,7 +754,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void extractUndeclaredExtensions(IBase theElement, List<HeldExtension> extensions, List<HeldExtension> modifierExtensions, CompositeChildElement theChildElem,
|
private void extractUndeclaredExtensions(IBase theElement, List<HeldExtension> extensions, List<HeldExtension> modifierExtensions, CompositeChildElement theChildElem,
|
||||||
CompositeChildElement theParent) {
|
CompositeChildElement theParent) {
|
||||||
if (theElement instanceof ISupportsUndeclaredExtensions) {
|
if (theElement instanceof ISupportsUndeclaredExtensions) {
|
||||||
ISupportsUndeclaredExtensions element = (ISupportsUndeclaredExtensions) theElement;
|
ISupportsUndeclaredExtensions element = (ISupportsUndeclaredExtensions) theElement;
|
||||||
List<ExtensionDt> ext = element.getUndeclaredExtensions();
|
List<ExtensionDt> ext = element.getUndeclaredExtensions();
|
||||||
|
@ -1029,78 +1022,78 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void parseExtension(ParserState<?> theState, JsonLikeArray theValues, boolean theIsModifier) {
|
private void parseExtension(ParserState<?> theState, JsonLikeArray theValues, boolean theIsModifier) {
|
||||||
int allUnderscoreNames = 0;
|
int allUnderscoreNames = 0;
|
||||||
int handledUnderscoreNames = 0;
|
int handledUnderscoreNames = 0;
|
||||||
|
|
||||||
for (int i = 0; i < theValues.size(); i++) {
|
for (int i = 0; i < theValues.size(); i++) {
|
||||||
JsonLikeObject nextExtObj = JsonLikeValue.asObject(theValues.get(i));
|
JsonLikeObject nextExtObj = JsonLikeValue.asObject(theValues.get(i));
|
||||||
JsonLikeValue jsonElement = nextExtObj.get("url");
|
JsonLikeValue jsonElement = nextExtObj.get("url");
|
||||||
String url;
|
String url;
|
||||||
if (null == jsonElement || !(jsonElement.isScalar())) {
|
if (null == jsonElement || !(jsonElement.isScalar())) {
|
||||||
String parentElementName;
|
String parentElementName;
|
||||||
if (theIsModifier) {
|
if (theIsModifier) {
|
||||||
parentElementName = "modifierExtension";
|
parentElementName = "modifierExtension";
|
||||||
} else {
|
} else {
|
||||||
parentElementName = "extension";
|
parentElementName = "extension";
|
||||||
}
|
}
|
||||||
getErrorHandler().missingRequiredElement(new ParseLocation(parentElementName), "url");
|
getErrorHandler().missingRequiredElement(new ParseLocation(parentElementName), "url");
|
||||||
url = null;
|
url = null;
|
||||||
} else {
|
} else {
|
||||||
url = getExtensionUrl(jsonElement.getAsString());
|
url = getExtensionUrl(jsonElement.getAsString());
|
||||||
}
|
}
|
||||||
theState.enteringNewElementExtension(null, url, theIsModifier, getServerBaseUrl());
|
theState.enteringNewElementExtension(null, url, theIsModifier, getServerBaseUrl());
|
||||||
for (String next : nextExtObj.keySet()) {
|
for (String next : nextExtObj.keySet()) {
|
||||||
if ("url".equals(next)) {
|
if ("url".equals(next)) {
|
||||||
continue;
|
continue;
|
||||||
} else if ("extension".equals(next)) {
|
} else if ("extension".equals(next)) {
|
||||||
JsonLikeArray jsonVal = JsonLikeValue.asArray(nextExtObj.get(next));
|
JsonLikeArray jsonVal = JsonLikeValue.asArray(nextExtObj.get(next));
|
||||||
parseExtension(theState, jsonVal, false);
|
parseExtension(theState, jsonVal, false);
|
||||||
} else if ("modifierExtension".equals(next)) {
|
} else if ("modifierExtension".equals(next)) {
|
||||||
JsonLikeArray jsonVal = JsonLikeValue.asArray(nextExtObj.get(next));
|
JsonLikeArray jsonVal = JsonLikeValue.asArray(nextExtObj.get(next));
|
||||||
parseExtension(theState, jsonVal, true);
|
parseExtension(theState, jsonVal, true);
|
||||||
} else if (next.charAt(0) == '_') {
|
} else if (next.charAt(0) == '_') {
|
||||||
allUnderscoreNames++;
|
allUnderscoreNames++;
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
JsonLikeValue jsonVal = nextExtObj.get(next);
|
JsonLikeValue jsonVal = nextExtObj.get(next);
|
||||||
String alternateName = '_' + next;
|
String alternateName = '_' + next;
|
||||||
JsonLikeValue alternateVal = nextExtObj.get(alternateName);
|
JsonLikeValue alternateVal = nextExtObj.get(alternateName);
|
||||||
if (alternateVal != null) {
|
if (alternateVal != null) {
|
||||||
handledUnderscoreNames++;
|
handledUnderscoreNames++;
|
||||||
}
|
}
|
||||||
parseChildren(theState, next, jsonVal, alternateVal, alternateName, false);
|
parseChildren(theState, next, jsonVal, alternateVal, alternateName, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This happens if an element has an extension but no actual value. I.e.
|
* This happens if an element has an extension but no actual value. I.e.
|
||||||
* if a resource has a "_status" element but no corresponding "status"
|
* if a resource has a "_status" element but no corresponding "status"
|
||||||
* element. This could be used to handle a null value with an extension
|
* element. This could be used to handle a null value with an extension
|
||||||
* for example.
|
* for example.
|
||||||
*/
|
*/
|
||||||
if (allUnderscoreNames > handledUnderscoreNames) {
|
if (allUnderscoreNames > handledUnderscoreNames) {
|
||||||
for (String alternateName : nextExtObj.keySet()) {
|
for (String alternateName : nextExtObj.keySet()) {
|
||||||
if (alternateName.startsWith("_") && alternateName.length() > 1) {
|
if (alternateName.startsWith("_") && alternateName.length() > 1) {
|
||||||
JsonLikeValue nextValue = nextExtObj.get(alternateName);
|
JsonLikeValue nextValue = nextExtObj.get(alternateName);
|
||||||
if (nextValue != null) {
|
if (nextValue != null) {
|
||||||
if (nextValue.isObject()) {
|
if (nextValue.isObject()) {
|
||||||
String nextName = alternateName.substring(1);
|
String nextName = alternateName.substring(1);
|
||||||
if (nextExtObj.get(nextName) == null) {
|
if (nextExtObj.get(nextName) == null) {
|
||||||
theState.enteringNewElement(null, nextName);
|
theState.enteringNewElement(null, nextName);
|
||||||
parseAlternates(nextValue, theState, alternateName, alternateName);
|
parseAlternates(nextValue, theState, alternateName, alternateName);
|
||||||
theState.endingElement();
|
theState.endingElement();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
getErrorHandler().incorrectJsonType(null, alternateName, ValueType.OBJECT, null, nextValue.getJsonType(), null);
|
getErrorHandler().incorrectJsonType(null, alternateName, ValueType.OBJECT, null, nextValue.getJsonType(), null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
theState.endingElement();
|
theState.endingElement();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void parseFhirComments(JsonLikeValue theObject, ParserState<?> theState) {
|
private void parseFhirComments(JsonLikeValue theObject, ParserState<?> theState) {
|
||||||
if (theObject.isArray()) {
|
if (theObject.isArray()) {
|
||||||
|
@ -1254,7 +1247,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeExtensionsAsDirectChild(IBaseResource theResource, JsonLikeWriter theEventWriter, RuntimeResourceDefinition resDef, List<HeldExtension> extensions,
|
private void writeExtensionsAsDirectChild(IBaseResource theResource, JsonLikeWriter theEventWriter, RuntimeResourceDefinition resDef, List<HeldExtension> extensions,
|
||||||
List<HeldExtension> modifierExtensions) throws IOException {
|
List<HeldExtension> modifierExtensions) throws IOException {
|
||||||
if (extensions.isEmpty() == false) {
|
if (extensions.isEmpty() == false) {
|
||||||
beginArray(theEventWriter, "extension");
|
beginArray(theEventWriter, "extension");
|
||||||
for (HeldExtension next : extensions) {
|
for (HeldExtension next : extensions) {
|
||||||
|
@ -1328,6 +1321,28 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
||||||
return url1.compareTo(url2);
|
return url1.compareTo(url2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void managePrimitiveExtension(final IBase theValue, final RuntimeResourceDefinition theResDef, final IBaseResource theResource, final JsonLikeWriter theEventWriter, final BaseRuntimeElementDefinition<?> def, final String childName) throws IOException {
|
||||||
|
if (def.getChildType().equals(ID_DATATYPE) || def.getChildType().equals(PRIMITIVE_DATATYPE)) {
|
||||||
|
final List<HeldExtension> extensions = new ArrayList<HeldExtension>(0);
|
||||||
|
final List<HeldExtension> modifierExtensions = new ArrayList<HeldExtension>(0);
|
||||||
|
// Undeclared extensions
|
||||||
|
extractUndeclaredExtensions(theValue, extensions, modifierExtensions, myParent, null);
|
||||||
|
// Declared extensions
|
||||||
|
if (def != null) {
|
||||||
|
extractDeclaredExtensions(theValue, def, extensions, modifierExtensions, myParent);
|
||||||
|
}
|
||||||
|
boolean haveContent = false;
|
||||||
|
if (!extensions.isEmpty() || !modifierExtensions.isEmpty()) {
|
||||||
|
haveContent = true;
|
||||||
|
}
|
||||||
|
if (haveContent) {
|
||||||
|
beginObject(theEventWriter, '_' + childName);
|
||||||
|
writeExtensionsAsDirectChild(theResource, theEventWriter, theResDef, extensions, modifierExtensions);
|
||||||
|
theEventWriter.endObject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void write(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonLikeWriter theEventWriter) throws IOException {
|
public void write(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonLikeWriter theEventWriter) throws IOException {
|
||||||
if (myUndeclaredExtension != null) {
|
if (myUndeclaredExtension != null) {
|
||||||
writeUndeclaredExtension(theResDef, theResource, theEventWriter, myUndeclaredExtension);
|
writeUndeclaredExtension(theResDef, theResource, theEventWriter, myUndeclaredExtension);
|
||||||
|
@ -1367,7 +1382,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
||||||
} else {
|
} else {
|
||||||
String childName = myDef.getChildNameByDatatype(myValue.getClass());
|
String childName = myDef.getChildNameByDatatype(myValue.getClass());
|
||||||
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, myValue, def, childName, false, false, myParent, false);
|
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, myValue, def, childName, false, false, myParent, false);
|
||||||
managePrimitiveExtension(myValue, theResDef, theResource, theEventWriter, def, childName);
|
managePrimitiveExtension(myValue, theResDef, theResource, theEventWriter, def, childName);
|
||||||
}
|
}
|
||||||
|
|
||||||
theEventWriter.endObject();
|
theEventWriter.endObject();
|
||||||
|
@ -1422,34 +1437,12 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
||||||
throw new ConfigurationException("Unable to encode extension, unregognized child element type: " + value.getClass().getCanonicalName());
|
throw new ConfigurationException("Unable to encode extension, unregognized child element type: " + value.getClass().getCanonicalName());
|
||||||
}
|
}
|
||||||
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, value, childDef, childName, true, false, myParent, false);
|
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, value, childDef, childName, true, false, myParent, false);
|
||||||
managePrimitiveExtension(value, theResDef, theResource, theEventWriter, childDef, childName);
|
managePrimitiveExtension(value, theResDef, theResource, theEventWriter, childDef, childName);
|
||||||
}
|
}
|
||||||
|
|
||||||
// theEventWriter.name(myUndeclaredExtension.get);
|
// theEventWriter.name(myUndeclaredExtension.get);
|
||||||
|
|
||||||
theEventWriter.endObject();
|
theEventWriter.endObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void managePrimitiveExtension(final IBase theValue, final RuntimeResourceDefinition theResDef, final IBaseResource theResource, final JsonLikeWriter theEventWriter, final BaseRuntimeElementDefinition<?> def, final String childName) throws IOException {
|
|
||||||
if (def.getChildType().equals(ID_DATATYPE) || def.getChildType().equals(PRIMITIVE_DATATYPE)) {
|
|
||||||
final List<HeldExtension> extensions = new ArrayList<HeldExtension>(0);
|
|
||||||
final List<HeldExtension> modifierExtensions = new ArrayList<HeldExtension>(0);
|
|
||||||
// Undeclared extensions
|
|
||||||
extractUndeclaredExtensions(theValue, extensions, modifierExtensions, myParent, null);
|
|
||||||
// Declared extensions
|
|
||||||
if (def != null) {
|
|
||||||
extractDeclaredExtensions(theValue, def, extensions, modifierExtensions, myParent);
|
|
||||||
}
|
|
||||||
boolean haveContent = false;
|
|
||||||
if (!extensions.isEmpty() || !modifierExtensions.isEmpty()) {
|
|
||||||
haveContent = true;
|
|
||||||
}
|
|
||||||
if (haveContent) {
|
|
||||||
beginObject(theEventWriter, '_' + childName);
|
|
||||||
writeExtensionsAsDirectChild(theResource, theEventWriter, theResDef, extensions, modifierExtensions);
|
|
||||||
theEventWriter.endObject();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -606,7 +606,7 @@ class ParserState<T> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case RESOURCE: {
|
case RESOURCE: {
|
||||||
if (myInstance instanceof IAnyResource || myInstance instanceof IBaseBackboneElement) {
|
if (myInstance instanceof IAnyResource || myInstance instanceof IBaseBackboneElement || myInstance instanceof IBaseElement) {
|
||||||
ParserState<T>.PreResourceStateHl7Org state = new PreResourceStateHl7Org(myInstance, child.getMutator(), null);
|
ParserState<T>.PreResourceStateHl7Org state = new PreResourceStateHl7Org(myInstance, child.getMutator(), null);
|
||||||
push(state);
|
push(state);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1559,7 +1559,8 @@ class ParserState<T> {
|
||||||
|
|
||||||
if (theEvent.isEndElement()) {
|
if (theEvent.isEndElement()) {
|
||||||
if (myDepth == 0) {
|
if (myDepth == 0) {
|
||||||
myDt.setValue(myEvents);
|
String eventsAsString = XmlUtil.encode(myEvents);
|
||||||
|
myDt.setValue(eventsAsString);
|
||||||
doPop();
|
doPop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
|
import java.io.StringReader;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
@ -605,13 +606,16 @@ public class XmlParser extends BaseParser /* implements IParser */ {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void encodeXhtml(XhtmlDt theDt, XMLStreamWriter theEventWriter) throws XMLStreamException {
|
private void encodeXhtml(XhtmlDt theDt, XMLStreamWriter theEventWriter) throws XMLStreamException {
|
||||||
if (theDt == null || theDt.getValue() == null) {
|
if (theDt == null || theDt.getValue() == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<XMLEvent> events = XmlUtil.parse(theDt.getValue());
|
||||||
boolean firstElement = true;
|
boolean firstElement = true;
|
||||||
for (XMLEvent event : theDt.getValue()) {
|
|
||||||
|
for (XMLEvent event : events) {
|
||||||
switch (event.getEventType()) {
|
switch (event.getEventType()) {
|
||||||
case XMLStreamConstants.ATTRIBUTE:
|
case XMLStreamConstants.ATTRIBUTE:
|
||||||
Attribute attr = (Attribute) event;
|
Attribute attr = (Attribute) event;
|
||||||
|
|
|
@ -31,6 +31,16 @@ public class Constants {
|
||||||
public static final String CHARSET_NAME_UTF8 = "UTF-8";
|
public static final String CHARSET_NAME_UTF8 = "UTF-8";
|
||||||
public static final Charset CHARSET_UTF8;
|
public static final Charset CHARSET_UTF8;
|
||||||
public static final String CHARSET_UTF8_CTSUFFIX = "; charset=" + CHARSET_NAME_UTF8;
|
public static final String CHARSET_UTF8_CTSUFFIX = "; charset=" + CHARSET_NAME_UTF8;
|
||||||
|
/**
|
||||||
|
* Contains a standard set of headers which are used by FHIR / HAPI FHIR, and therefore
|
||||||
|
* would make a useful set for CORS AllowedHeader declarations
|
||||||
|
*/
|
||||||
|
public static final Set<String> CORS_ALLOWED_HEADERS;
|
||||||
|
/**
|
||||||
|
* Contains a standard set of HTTP Methods which are used by FHIR / HAPI FHIR, and therefore
|
||||||
|
* would make a useful set for CORS AllowedMethod declarations
|
||||||
|
*/
|
||||||
|
public static final Set<String> CORS_ALLWED_METHODS;
|
||||||
public static final String CT_FHIR_JSON = "application/json+fhir";
|
public static final String CT_FHIR_JSON = "application/json+fhir";
|
||||||
public static final String CT_FHIR_JSON_NEW = "application/fhir+json";
|
public static final String CT_FHIR_JSON_NEW = "application/fhir+json";
|
||||||
public static final String CT_FHIR_XML = "application/xml+fhir";
|
public static final String CT_FHIR_XML = "application/xml+fhir";
|
||||||
|
@ -176,12 +186,13 @@ public class Constants {
|
||||||
public static final String URL_TOKEN_METADATA = "metadata";
|
public static final String URL_TOKEN_METADATA = "metadata";
|
||||||
public static final String OO_INFOSTATUS_PROCESSING = "processing";
|
public static final String OO_INFOSTATUS_PROCESSING = "processing";
|
||||||
public static final String PARAM_GRAPHQL_QUERY = "query";
|
public static final String PARAM_GRAPHQL_QUERY = "query";
|
||||||
|
public static final String HEADER_X_CACHE = "X-Cache";
|
||||||
|
public static final String HEADER_X_SECURITY_CONTEXT = "X-Security-Context";
|
||||||
|
|
||||||
static {
|
static {
|
||||||
CHARSET_UTF8 = Charset.forName(CHARSET_NAME_UTF8);
|
CHARSET_UTF8 = Charset.forName(CHARSET_NAME_UTF8);
|
||||||
|
|
||||||
HashMap<Integer, String> statusNames = new HashMap<Integer, String>();
|
HashMap<Integer, String> statusNames = new HashMap<>();
|
||||||
|
|
||||||
statusNames.put(200, "OK");
|
statusNames.put(200, "OK");
|
||||||
statusNames.put(201, "Created");
|
statusNames.put(201, "Created");
|
||||||
statusNames.put(202, "Accepted");
|
statusNames.put(202, "Accepted");
|
||||||
|
@ -246,11 +257,31 @@ public class Constants {
|
||||||
statusNames.put(511, "Network Authentication Required");
|
statusNames.put(511, "Network Authentication Required");
|
||||||
HTTP_STATUS_NAMES = Collections.unmodifiableMap(statusNames);
|
HTTP_STATUS_NAMES = Collections.unmodifiableMap(statusNames);
|
||||||
|
|
||||||
Set<String> formatsHtml = new HashSet<String>();
|
Set<String> formatsHtml = new HashSet<>();
|
||||||
formatsHtml.add(CT_HTML);
|
formatsHtml.add(CT_HTML);
|
||||||
formatsHtml.add(FORMAT_HTML);
|
formatsHtml.add(FORMAT_HTML);
|
||||||
FORMATS_HTML = Collections.unmodifiableSet(formatsHtml);
|
FORMATS_HTML = Collections.unmodifiableSet(formatsHtml);
|
||||||
|
|
||||||
|
// *********************************************************
|
||||||
|
// Update CorsInterceptor's constructor documentation if you change these:
|
||||||
|
// *********************************************************
|
||||||
|
HashSet<String> corsAllowedHeaders = new HashSet<>();
|
||||||
|
corsAllowedHeaders.add("Accept");
|
||||||
|
corsAllowedHeaders.add("Access-Control-Request-Headers");
|
||||||
|
corsAllowedHeaders.add("Access-Control-Request-Method");
|
||||||
|
corsAllowedHeaders.add("Cache-Control");
|
||||||
|
corsAllowedHeaders.add("Content-Type");
|
||||||
|
corsAllowedHeaders.add("Origin");
|
||||||
|
corsAllowedHeaders.add("Prefer");
|
||||||
|
corsAllowedHeaders.add("X-Requested-With");
|
||||||
|
CORS_ALLOWED_HEADERS = Collections.unmodifiableSet(corsAllowedHeaders);
|
||||||
|
|
||||||
|
// *********************************************************
|
||||||
|
// Update CorsInterceptor's constructor documentation if you change these:
|
||||||
|
// *********************************************************
|
||||||
|
HashSet<String> corsAllowedMethods = new HashSet<>();
|
||||||
|
corsAllowedMethods.addAll(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"));
|
||||||
|
CORS_ALLWED_METHODS = Collections.unmodifiableSet(corsAllowedMethods);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ public interface IVersionSpecificBundleFactory {
|
||||||
|
|
||||||
void addResourcesToBundle(List<IBaseResource> theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set<Include> theIncludes);
|
void addResourcesToBundle(List<IBaseResource> theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set<Include> theIncludes);
|
||||||
|
|
||||||
void addRootPropertiesToBundle(String theAuthor, String theServerBase, String theLinkSelf, String theLinkPrev, String theLinkNext, Integer theTotalResults, BundleTypeEnum theBundleType, IPrimitiveType<Date> theLastUpdated);
|
void addRootPropertiesToBundle(String theId, String theServerBase, String theLinkSelf, String theLinkPrev, String theLinkNext, Integer theTotalResults, BundleTypeEnum theBundleType, IPrimitiveType<Date> theLastUpdated);
|
||||||
|
|
||||||
IBaseResource getResourceBundle();
|
IBaseResource getResourceBundle();
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
package ca.uhn.fhir.util;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
||||||
|
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseBinary;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseReference;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class BinaryUtil {
|
||||||
|
|
||||||
|
private BinaryUtil() {
|
||||||
|
// non instantiable
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IBaseReference getSecurityContext(FhirContext theCtx, IBaseBinary theBinary) {
|
||||||
|
RuntimeResourceDefinition def = theCtx.getResourceDefinition("Binary");
|
||||||
|
BaseRuntimeChildDefinition child = def.getChildByName("securityContext");
|
||||||
|
IBaseReference retVal = null;
|
||||||
|
if (child != null) {
|
||||||
|
List<IBase> values = child.getAccessor().getValues(theBinary);
|
||||||
|
if (values.size() > 0) {
|
||||||
|
retVal = (IBaseReference) values.get(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IBaseBinary newBinary(FhirContext theCtx) {
|
||||||
|
return (IBaseBinary) theCtx.getResourceDefinition("Binary").newInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setSecurityContext(FhirContext theCtx, IBaseBinary theBinary, String theSecurityContext) {
|
||||||
|
RuntimeResourceDefinition def = theCtx.getResourceDefinition("Binary");
|
||||||
|
BaseRuntimeChildDefinition child = def.getChildByName("securityContext");
|
||||||
|
|
||||||
|
BaseRuntimeElementDefinition<?> referenceDef = theCtx.getElementDefinition("reference");
|
||||||
|
IBaseReference reference = (IBaseReference) referenceDef.newInstance();
|
||||||
|
child.getMutator().addValue(theBinary, reference);
|
||||||
|
|
||||||
|
reference.setReference(theSecurityContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -21,12 +21,14 @@ package ca.uhn.fhir.util;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides server ports
|
* Provides server ports
|
||||||
*/
|
*/
|
||||||
@CoverageIgnore
|
@CoverageIgnore
|
||||||
public class PortUtil {
|
public class PortUtil {
|
||||||
|
private static LinkedHashSet<Integer> ourPorts = new LinkedHashSet<>();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Non instantiable
|
* Non instantiable
|
||||||
|
@ -41,9 +43,13 @@ public class PortUtil {
|
||||||
public static int findFreePort() {
|
public static int findFreePort() {
|
||||||
ServerSocket server;
|
ServerSocket server;
|
||||||
try {
|
try {
|
||||||
server = new ServerSocket(0);
|
int port;
|
||||||
int port = server.getLocalPort();
|
do {
|
||||||
server.close();
|
server = new ServerSocket(0);
|
||||||
|
port = server.getLocalPort();
|
||||||
|
server.close();
|
||||||
|
} while (!ourPorts.add(port));
|
||||||
|
|
||||||
Thread.sleep(500);
|
Thread.sleep(500);
|
||||||
return port;
|
return port;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|
|
@ -20,26 +20,27 @@ package ca.uhn.fhir.util;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.i18n.HapiLocalizer;
|
||||||
|
import ch.qos.logback.classic.Level;
|
||||||
|
import ch.qos.logback.classic.Logger;
|
||||||
|
import ch.qos.logback.classic.LoggerContext;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
|
||||||
import org.slf4j.LoggerFactory;
|
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
|
||||||
import ca.uhn.fhir.i18n.HapiLocalizer;
|
|
||||||
import ch.qos.logback.classic.Level;
|
|
||||||
import ch.qos.logback.classic.Logger;
|
|
||||||
import ch.qos.logback.classic.LoggerContext;
|
|
||||||
|
|
||||||
public class TestUtil {
|
public class TestUtil {
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TestUtil.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TestUtil.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <b>THIS IS FOR UNIT TESTS ONLY - DO NOT CALL THIS METHOD FROM USER CODE</b>
|
* <b>THIS IS FOR UNIT TESTS ONLY - DO NOT CALL THIS METHOD FROM USER CODE</b>
|
||||||
*
|
* <p>
|
||||||
* When we run the unit tests in cobertura, JUnit doesn't seem to clean up static fields which leads to
|
* When we run the unit tests in cobertura, JUnit doesn't seem to clean up static fields which leads to
|
||||||
* tons of memory being used by the end and the JVM crashes in Travis. Manually clearing all of the
|
* tons of memory being used by the end and the JVM crashes in Travis. Manually clearing all of the
|
||||||
* static fields seems to solve this.
|
* static fields seems to solve this.
|
||||||
|
@ -104,7 +105,7 @@ public class TestUtil {
|
||||||
* environment
|
* environment
|
||||||
*/
|
*/
|
||||||
public static void randomizeLocale() {
|
public static void randomizeLocale() {
|
||||||
Locale[] availableLocales = { Locale.CANADA, Locale.GERMANY, Locale.TAIWAN };
|
Locale[] availableLocales = {Locale.CANADA, Locale.GERMANY, Locale.TAIWAN};
|
||||||
Locale.setDefault(availableLocales[(int) (Math.random() * availableLocales.length)]);
|
Locale.setDefault(availableLocales[(int) (Math.random() * availableLocales.length)]);
|
||||||
ourLog.info("Tests are running in locale: " + Locale.getDefault().getDisplayName());
|
ourLog.info("Tests are running in locale: " + Locale.getDefault().getDisplayName());
|
||||||
if (Math.random() < 0.5) {
|
if (Math.random() < 0.5) {
|
||||||
|
@ -116,10 +117,19 @@ public class TestUtil {
|
||||||
System.setProperty("file.encoding", "UTF-8");
|
System.setProperty("file.encoding", "UTF-8");
|
||||||
System.setProperty("line.separator", "\n");
|
System.setProperty("line.separator", "\n");
|
||||||
}
|
}
|
||||||
String availableTimeZones[] = { "GMT+08:00", "GMT-05:00", "GMT+00:00", "GMT+03:30" };
|
String availableTimeZones[] = {"GMT+08:00", "GMT-05:00", "GMT+00:00", "GMT+03:30"};
|
||||||
String timeZone = availableTimeZones[(int) (Math.random() * availableTimeZones.length)];
|
String timeZone = availableTimeZones[(int) (Math.random() * availableTimeZones.length)];
|
||||||
TimeZone.setDefault(TimeZone.getTimeZone(timeZone));
|
TimeZone.setDefault(TimeZone.getTimeZone(timeZone));
|
||||||
ourLog.info("Tests are using time zone: {}", TimeZone.getDefault().getID());
|
ourLog.info("Tests are using time zone: {}", TimeZone.getDefault().getID());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <b>THIS IS FOR UNIT TESTS ONLY - DO NOT CALL THIS METHOD FROM USER CODE</b>
|
||||||
|
* <p>
|
||||||
|
* Strip \r chars from a string to account for line ending platform differences
|
||||||
|
*/
|
||||||
|
public static String stripReturns(String theString) {
|
||||||
|
return defaultString(theString).replace("\r", "");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
package ca.uhn.fhir.util;
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - Core Library
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2017 University Health Network
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.io.StringReader;
|
||||||
|
|
||||||
|
public class XmlDetectionUtil {
|
||||||
|
|
||||||
|
private static final Logger ourLog = LoggerFactory.getLogger(XmlDetectionUtil.class);
|
||||||
|
private static Boolean ourStaxPresent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method will return <code>true</code> if a StAX XML parsing library is present
|
||||||
|
* on the classpath
|
||||||
|
*/
|
||||||
|
public static boolean isStaxPresent() {
|
||||||
|
Boolean retVal = ourStaxPresent;
|
||||||
|
if (retVal == null) {
|
||||||
|
try {
|
||||||
|
Class.forName("javax.xml.stream.events.XMLEvent");
|
||||||
|
Class<?> xmlUtilClazz = Class.forName("ca.uhn.fhir.util.XmlUtil");
|
||||||
|
xmlUtilClazz.getMethod("createXmlReader", Reader.class).invoke(xmlUtilClazz, new StringReader(""));
|
||||||
|
ourStaxPresent = Boolean.TRUE;
|
||||||
|
retVal = Boolean.TRUE;
|
||||||
|
} catch (Throwable t) {
|
||||||
|
ourLog.info("StAX not detected on classpath, XML processing will be disabled");
|
||||||
|
ourStaxPresent = Boolean.FALSE;
|
||||||
|
retVal = Boolean.FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -20,12 +20,13 @@ package ca.uhn.fhir.util;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.Collections;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.xml.stream.*;
|
import javax.xml.stream.*;
|
||||||
|
import javax.xml.stream.events.XMLEvent;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.model.primitive.XhtmlDt;
|
||||||
|
import ca.uhn.fhir.parser.DataFormatException;
|
||||||
import org.apache.commons.lang3.StringEscapeUtils;
|
import org.apache.commons.lang3.StringEscapeUtils;
|
||||||
import org.codehaus.stax2.XMLOutputFactory2;
|
import org.codehaus.stax2.XMLOutputFactory2;
|
||||||
import org.codehaus.stax2.io.EscapingWriterFactory;
|
import org.codehaus.stax2.io.EscapingWriterFactory;
|
||||||
|
@ -37,21 +38,22 @@ import ca.uhn.fhir.context.ConfigurationException;
|
||||||
import ca.uhn.fhir.util.jar.DependencyLogFactory;
|
import ca.uhn.fhir.util.jar.DependencyLogFactory;
|
||||||
import ca.uhn.fhir.util.jar.IDependencyLog;
|
import ca.uhn.fhir.util.jar.IDependencyLog;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility methods for working with the StAX API.
|
* Utility methods for working with the StAX API.
|
||||||
*
|
*
|
||||||
* This class contains code adapted from the Apache Axiom project.
|
* This class contains code adapted from the Apache Axiom project.
|
||||||
*/
|
*/
|
||||||
public class XmlUtil {
|
public class XmlUtil {
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(XmlUtil.class);
|
||||||
|
private static final Map<String, Integer> VALID_ENTITY_NAMES;
|
||||||
|
private static final ExtendedEntityReplacingXmlResolver XML_RESOLVER = new ExtendedEntityReplacingXmlResolver();
|
||||||
private static XMLOutputFactory ourFragmentOutputFactory;
|
private static XMLOutputFactory ourFragmentOutputFactory;
|
||||||
private static volatile boolean ourHaveLoggedStaxImplementation;
|
private static volatile boolean ourHaveLoggedStaxImplementation;
|
||||||
private static volatile XMLInputFactory ourInputFactory;
|
private static volatile XMLInputFactory ourInputFactory;
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(XmlUtil.class);
|
|
||||||
private static Throwable ourNextException;
|
private static Throwable ourNextException;
|
||||||
private static volatile XMLOutputFactory ourOutputFactory;
|
private static volatile XMLOutputFactory ourOutputFactory;
|
||||||
private static Boolean ourStaxPresent;
|
|
||||||
private static final Map<String, Integer> VALID_ENTITY_NAMES;
|
|
||||||
private static final ExtendedEntityReplacingXmlResolver XML_RESOLVER = new ExtendedEntityReplacingXmlResolver();
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
HashMap<String, Integer> validEntityNames = new HashMap<String, Integer>(1448);
|
HashMap<String, Integer> validEntityNames = new HashMap<String, Integer>(1448);
|
||||||
|
@ -1567,6 +1569,30 @@ public class XmlUtil {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode a set of StAX events into a String
|
||||||
|
*/
|
||||||
|
public static String encode(List<XMLEvent> theEvents) {
|
||||||
|
try {
|
||||||
|
StringWriter w = new StringWriter();
|
||||||
|
XMLEventWriter ew = XmlUtil.createXmlFragmentWriter(w);
|
||||||
|
|
||||||
|
for (XMLEvent next : theEvents) {
|
||||||
|
if (next.isCharacters()) {
|
||||||
|
ew.add(next);
|
||||||
|
} else {
|
||||||
|
ew.add(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ew.close();
|
||||||
|
return w.toString();
|
||||||
|
} catch (XMLStreamException e) {
|
||||||
|
throw new DataFormatException("Problem with the contained XML events", e);
|
||||||
|
} catch (FactoryConfigurationError e) {
|
||||||
|
throw new ConfigurationException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static XMLOutputFactory getOrCreateFragmentOutputFactory() throws FactoryConfigurationError {
|
private static XMLOutputFactory getOrCreateFragmentOutputFactory() throws FactoryConfigurationError {
|
||||||
XMLOutputFactory retVal = ourFragmentOutputFactory;
|
XMLOutputFactory retVal = ourFragmentOutputFactory;
|
||||||
if (retVal == null) {
|
if (retVal == null) {
|
||||||
|
@ -1652,6 +1678,7 @@ public class XmlUtil {
|
||||||
return ourOutputFactory;
|
return ourOutputFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static void logStaxImplementation(Class<?> theClass) {
|
private static void logStaxImplementation(Class<?> theClass) {
|
||||||
IDependencyLog logger = DependencyLogFactory.createJarLogger();
|
IDependencyLog logger = DependencyLogFactory.createJarLogger();
|
||||||
if (logger != null) {
|
if (logger != null) {
|
||||||
|
@ -1683,6 +1710,49 @@ public class XmlUtil {
|
||||||
return outputFactory;
|
return outputFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses an XML string into a set of StAX events
|
||||||
|
*/
|
||||||
|
public static List<XMLEvent> parse(String theValue) {
|
||||||
|
if (isBlank(theValue)) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
String val = theValue.trim();
|
||||||
|
if (!val.startsWith("<")) {
|
||||||
|
val = XhtmlDt.DIV_OPEN_FIRST + val + "</div>";
|
||||||
|
}
|
||||||
|
boolean hasProcessingInstruction = val.startsWith("<?");
|
||||||
|
if (hasProcessingInstruction && val.endsWith("?>")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
ArrayList<XMLEvent> value = new ArrayList<>();
|
||||||
|
StringReader reader = new StringReader(val);
|
||||||
|
XMLEventReader er = XmlUtil.createXmlReader(reader);
|
||||||
|
boolean first = true;
|
||||||
|
while (er.hasNext()) {
|
||||||
|
XMLEvent next = er.nextEvent();
|
||||||
|
if (first) {
|
||||||
|
first = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (er.hasNext()) {
|
||||||
|
// don't add the last event
|
||||||
|
value.add(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
|
||||||
|
} catch (XMLStreamException e) {
|
||||||
|
throw new DataFormatException("String does not appear to be valid XML/XHTML (error is \"" + e.getMessage() + "\"): " + theValue, e);
|
||||||
|
} catch (FactoryConfigurationError e) {
|
||||||
|
throw new ConfigurationException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FOR UNIT TESTS ONLY - Throw this exception for the next operation
|
* FOR UNIT TESTS ONLY - Throw this exception for the next operation
|
||||||
*/
|
*/
|
||||||
|
@ -1712,26 +1782,6 @@ public class XmlUtil {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This method will return <code>true</code> if a StAX XML parsing library is present
|
|
||||||
* on the classpath
|
|
||||||
*/
|
|
||||||
public static boolean isStaxPresent() {
|
|
||||||
Boolean retVal = ourStaxPresent;
|
|
||||||
if (retVal == null) {
|
|
||||||
try {
|
|
||||||
newInputFactory();
|
|
||||||
ourStaxPresent = Boolean.TRUE;
|
|
||||||
retVal = Boolean.TRUE;
|
|
||||||
} catch (ConfigurationException e) {
|
|
||||||
ourLog.info("StAX not detected on classpath, XML processing will be disabled");
|
|
||||||
ourStaxPresent = Boolean.FALSE;
|
|
||||||
retVal = Boolean.FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class MyEscaper implements EscapingWriterFactory {
|
public static class MyEscaper implements EscapingWriterFactory {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
package org.hl7.fhir.exceptions;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR - Core Library
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2017 University Health Network
|
|
||||||
* %%
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
* #L%
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
public class DefinitionException extends FHIRException {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 8175803009974854088L;
|
|
||||||
|
|
||||||
public DefinitionException() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
public DefinitionException(String message, Throwable cause) {
|
|
||||||
super(message, cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DefinitionException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DefinitionException(Throwable cause) {
|
|
||||||
super(cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
package org.hl7.fhir.exceptions;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR - Core Library
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2017 University Health Network
|
|
||||||
* %%
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
* #L%
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
public class FHIRException extends Exception {
|
|
||||||
|
|
||||||
// Note that the 4-argument constructor has been removed as it is not JDK6 compatible
|
|
||||||
|
|
||||||
private static final long serialVersionUID = -1793365096090608037L;
|
|
||||||
|
|
||||||
public FHIRException() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
public FHIRException(String message, Throwable cause) {
|
|
||||||
super(message, cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
public FHIRException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public FHIRException(Throwable cause) {
|
|
||||||
super(cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
package org.hl7.fhir.exceptions;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR - Core Library
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2017 University Health Network
|
|
||||||
* %%
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
* #L%
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
public class FHIRFormatError extends FHIRException {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
public FHIRFormatError() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
public FHIRFormatError(String message, Throwable cause) {
|
|
||||||
super(message, cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
public FHIRFormatError(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public FHIRFormatError(Throwable cause) {
|
|
||||||
super(cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
package org.hl7.fhir.exceptions;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR - Core Library
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2017 University Health Network
|
|
||||||
* %%
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
* #L%
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
public class NoTerminologyServiceException extends FHIRException {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 8196224236579018584L;
|
|
||||||
|
|
||||||
public NoTerminologyServiceException() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
public NoTerminologyServiceException(String message, Throwable cause) {
|
|
||||||
super(message, cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
public NoTerminologyServiceException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public NoTerminologyServiceException(Throwable cause) {
|
|
||||||
super(cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
package org.hl7.fhir.exceptions;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR - Core Library
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2017 University Health Network
|
|
||||||
* %%
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
* #L%
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
public class PathEngineException extends FHIRException {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = -8550234068917355391L;
|
|
||||||
|
|
||||||
public PathEngineException() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
public PathEngineException(String message, Throwable cause) {
|
|
||||||
super(message, cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
public PathEngineException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public PathEngineException(Throwable cause) {
|
|
||||||
super(cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
package org.hl7.fhir.exceptions;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR - Core Library
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2017 University Health Network
|
|
||||||
* %%
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
* #L%
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
public class TerminologyServiceException extends FHIRException {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = -1547510329455394225L;
|
|
||||||
|
|
||||||
public TerminologyServiceException() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
public TerminologyServiceException(String message, Throwable cause) {
|
|
||||||
super(message, cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
public TerminologyServiceException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public TerminologyServiceException(Throwable cause) {
|
|
||||||
super(cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
package org.hl7.fhir.exceptions;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR - Core Library
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2017 University Health Network
|
|
||||||
* %%
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
* #L%
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
public class UcumException extends FHIRException {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = -8535757334881835619L;
|
|
||||||
|
|
||||||
public UcumException() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
public UcumException(String message, Throwable cause) {
|
|
||||||
super(message, cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
public UcumException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public UcumException(Throwable cause) {
|
|
||||||
super(cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -73,6 +73,7 @@ public class IgPackUploader extends BaseCommand {
|
||||||
.and(StructureDefinition.URL.matches().value(nextResourceUrl))
|
.and(StructureDefinition.URL.matches().value(nextResourceUrl))
|
||||||
.execute();
|
.execute();
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new ParseException("This command does not support FHIR version " + ctx.getVersion().getVersion());
|
throw new ParseException("This command does not support FHIR version " + ctx.getVersion().getVersion());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,28 +1,36 @@
|
||||||
package ca.uhn.fhir.jpa.demo;
|
package ca.uhn.fhir.jpa.demo;
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
import javax.servlet.ServletException;
|
|
||||||
|
|
||||||
import org.springframework.web.context.ContextLoaderListener;
|
|
||||||
import org.springframework.web.context.WebApplicationContext;
|
|
||||||
import org.springframework.web.cors.CorsConfiguration;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
||||||
import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2;
|
import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2;
|
||||||
import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2;
|
import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2;
|
||||||
import ca.uhn.fhir.jpa.provider.dstu3.*;
|
import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3;
|
||||||
import ca.uhn.fhir.jpa.provider.r4.*;
|
import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3;
|
||||||
|
import ca.uhn.fhir.jpa.provider.dstu3.TerminologyUploaderProviderDstu3;
|
||||||
|
import ca.uhn.fhir.jpa.provider.r4.JpaConformanceProviderR4;
|
||||||
|
import ca.uhn.fhir.jpa.provider.r4.JpaSystemProviderR4;
|
||||||
|
import ca.uhn.fhir.jpa.provider.r4.TerminologyUploaderProviderR4;
|
||||||
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
|
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
||||||
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||||
import ca.uhn.fhir.rest.server.*;
|
import ca.uhn.fhir.rest.server.ETagSupportEnum;
|
||||||
|
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.CorsInterceptor;
|
import ca.uhn.fhir.rest.server.interceptor.CorsInterceptor;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||||
|
import org.springframework.web.context.ContextLoaderListener;
|
||||||
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
|
import org.springframework.web.cors.CorsConfiguration;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class JpaServerDemo extends RestfulServer {
|
public class JpaServerDemo extends RestfulServer {
|
||||||
|
|
||||||
|
@ -134,19 +142,7 @@ public class JpaServerDemo extends RestfulServer {
|
||||||
setPagingProvider(new FifoMemoryPagingProvider(10));
|
setPagingProvider(new FifoMemoryPagingProvider(10));
|
||||||
|
|
||||||
// Register a CORS filter
|
// Register a CORS filter
|
||||||
CorsConfiguration config = new CorsConfiguration();
|
CorsInterceptor corsInterceptor = new CorsInterceptor();
|
||||||
CorsInterceptor corsInterceptor = new CorsInterceptor(config);
|
|
||||||
config.addAllowedHeader("x-fhir-starter");
|
|
||||||
config.addAllowedHeader("Origin");
|
|
||||||
config.addAllowedHeader("Accept");
|
|
||||||
config.addAllowedHeader("X-Requested-With");
|
|
||||||
config.addAllowedHeader("Content-Type");
|
|
||||||
config.addAllowedHeader("Access-Control-Request-Method");
|
|
||||||
config.addAllowedHeader("Access-Control-Request-Headers");
|
|
||||||
config.addAllowedOrigin("*");
|
|
||||||
config.addExposedHeader("Location");
|
|
||||||
config.addExposedHeader("Content-Location");
|
|
||||||
config.setAllowedMethods(Arrays.asList("GET","POST","PUT","DELETE","OPTIONS"));
|
|
||||||
registerInterceptor(corsInterceptor);
|
registerInterceptor(corsInterceptor);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -59,6 +59,11 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- Unit test dependencies -->
|
<!-- Unit test dependencies -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.codehaus.woodstox</groupId>
|
||||||
|
<artifactId>woodstox-core-asl</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.guava</groupId>
|
<groupId>com.google.guava</groupId>
|
||||||
<artifactId>guava</artifactId>
|
<artifactId>guava</artifactId>
|
||||||
|
|
|
@ -98,24 +98,23 @@ public class ApacheHttpResponse implements IHttpResponse {
|
||||||
}
|
}
|
||||||
if (charset == null) {
|
if (charset == null) {
|
||||||
if (Constants.STATUS_HTTP_204_NO_CONTENT != myResponse.getStatusLine().getStatusCode()) {
|
if (Constants.STATUS_HTTP_204_NO_CONTENT != myResponse.getStatusLine().getStatusCode()) {
|
||||||
ourLog.warn("Response did not specify a charset.");
|
ourLog.debug("Response did not specify a charset, defaulting to utf-8");
|
||||||
}
|
}
|
||||||
charset = Charset.forName("UTF-8");
|
charset = Charset.forName("UTF-8");
|
||||||
}
|
}
|
||||||
|
|
||||||
Reader reader = new InputStreamReader(readEntity(), charset);
|
return new InputStreamReader(readEntity(), charset);
|
||||||
return reader;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, List<String>> getAllHeaders() {
|
public Map<String, List<String>> getAllHeaders() {
|
||||||
Map<String, List<String>> headers = new HashMap<String, List<String>>();
|
Map<String, List<String>> headers = new HashMap<>();
|
||||||
if (myResponse.getAllHeaders() != null) {
|
if (myResponse.getAllHeaders() != null) {
|
||||||
for (Header next : myResponse.getAllHeaders()) {
|
for (Header next : myResponse.getAllHeaders()) {
|
||||||
String name = next.getName().toLowerCase();
|
String name = next.getName().toLowerCase();
|
||||||
List<String> list = headers.get(name);
|
List<String> list = headers.get(name);
|
||||||
if (list == null) {
|
if (list == null) {
|
||||||
list = new ArrayList<String>();
|
list = new ArrayList<>();
|
||||||
headers.put(name, list);
|
headers.put(name, list);
|
||||||
}
|
}
|
||||||
list.add(next.getValue());
|
list.add(next.getValue());
|
||||||
|
@ -131,7 +130,7 @@ public class ApacheHttpResponse implements IHttpResponse {
|
||||||
if (headers == null) {
|
if (headers == null) {
|
||||||
headers = new Header[0];
|
headers = new Header[0];
|
||||||
}
|
}
|
||||||
List<String> retVal = new ArrayList<String>();
|
List<String> retVal = new ArrayList<>();
|
||||||
for (Header next : headers) {
|
for (Header next : headers) {
|
||||||
retVal.add(next.getValue());
|
retVal.add(next.getValue());
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import ca.uhn.fhir.rest.api.CacheControlDirective;
|
import ca.uhn.fhir.rest.api.CacheControlDirective;
|
||||||
|
import ca.uhn.fhir.util.XmlDetectionUtil;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
|
@ -110,7 +111,7 @@ public abstract class BaseClient implements IRestfulClient {
|
||||||
setKeepResponses(true);
|
setKeepResponses(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (XmlUtil.isStaxPresent() == false) {
|
if (XmlDetectionUtil.isStaxPresent() == false) {
|
||||||
myEncoding = EncodingEnum.JSON;
|
myEncoding = EncodingEnum.JSON;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -330,6 +330,8 @@ public abstract class RestfulClientFactory implements IRestfulClientFactory {
|
||||||
serverFhirVersionEnum = FhirVersionEnum.DSTU2_1;
|
serverFhirVersionEnum = FhirVersionEnum.DSTU2_1;
|
||||||
} else if (serverFhirVersionString.equals(FhirVersionEnum.DSTU3.getFhirVersionString())) {
|
} else if (serverFhirVersionString.equals(FhirVersionEnum.DSTU3.getFhirVersionString())) {
|
||||||
serverFhirVersionEnum = FhirVersionEnum.DSTU3;
|
serverFhirVersionEnum = FhirVersionEnum.DSTU3;
|
||||||
|
} else if (serverFhirVersionString.equals(FhirVersionEnum.R4.getFhirVersionString())) {
|
||||||
|
serverFhirVersionEnum = FhirVersionEnum.R4;
|
||||||
} else {
|
} else {
|
||||||
// we'll be lenient and accept this
|
// we'll be lenient and accept this
|
||||||
ourLog.debug("Server conformance statement indicates unknown FHIR version: {}", serverFhirVersionString);
|
ourLog.debug("Server conformance statement indicates unknown FHIR version: {}", serverFhirVersionString);
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1346,7 +1346,7 @@ public class VersionConvertor_14_40 {
|
||||||
org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionSlicingComponent tgt = new org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionSlicingComponent();
|
org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionSlicingComponent tgt = new org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionSlicingComponent();
|
||||||
copyElement(src, tgt);
|
copyElement(src, tgt);
|
||||||
for (org.hl7.fhir.dstu2016may.model.StringType t : src.getDiscriminator())
|
for (org.hl7.fhir.dstu2016may.model.StringType t : src.getDiscriminator())
|
||||||
tgt.addDiscriminator(ProfileUtilities.interpretR2Discriminator(t.getValue()));
|
tgt.addDiscriminator(ProfileUtilities.interpretR2Discriminator(t.getValue(), true));
|
||||||
if (src.hasDescription())
|
if (src.hasDescription())
|
||||||
tgt.setDescription(src.getDescription());
|
tgt.setDescription(src.getDescription());
|
||||||
if (src.hasOrdered())
|
if (src.hasOrdered())
|
||||||
|
|
|
@ -1,83 +0,0 @@
|
||||||
package org.hl7.fhir.utilities;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR - Converter
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2017 University Health Network
|
|
||||||
* %%
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
* #L%
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
public class OIDUtils {
|
|
||||||
|
|
||||||
/*
|
|
||||||
2.16.840.1.113883.3.72.5.2 - NIST owns this
|
|
||||||
2.16.840.1.113883.4.6 - National Provider Identifier
|
|
||||||
2.16.840.1.113883.6.21 - UB92
|
|
||||||
2.16.840.1.113883.6.69 - NDC
|
|
||||||
*/
|
|
||||||
|
|
||||||
public static String getUriForOid(String r) {
|
|
||||||
if (r.equals("2.16.840.1.113883.6.96"))
|
|
||||||
return "http://snomed.info/sct";
|
|
||||||
if (r.equals("2.16.840.1.113883.6.1"))
|
|
||||||
return "http://loinc.org";
|
|
||||||
if (r.equals("2.16.840.1.113883.6.8"))
|
|
||||||
return "http://unitsofmeasure.org";
|
|
||||||
if (r.equals("2.16.840.1.113883.6.3"))
|
|
||||||
return "http://hl7.org/fhir/sid/icd-10";
|
|
||||||
if (r.equals("2.16.840.1.113883.6.42"))
|
|
||||||
return "http://hl7.org/fhir/sid/icd-9";
|
|
||||||
if (r.equals("2.16.840.1.113883.6.104"))
|
|
||||||
return "http://hl7.org/fhir/sid/icd-9";
|
|
||||||
if (r.equals("2.16.840.1.113883.6.103"))
|
|
||||||
return "http://hl7.org/fhir/sid/icd-9"; //todo: confirm this
|
|
||||||
if (r.equals("2.16.840.1.113883.6.73"))
|
|
||||||
return "http://hl7.org/fhir/sid/atc";
|
|
||||||
if (r.equals("2.16.840.1.113883.3.26.1.1"))
|
|
||||||
return "http://ncimeta.nci.nih.gov";
|
|
||||||
if (r.equals("2.16.840.1.113883.3.26.1.1.1"))
|
|
||||||
return "http://ncimeta.nci.nih.gov";
|
|
||||||
if (r.equals("2.16.840.1.113883.6.88"))
|
|
||||||
return "http://www.nlm.nih.gov/research/umls/rxnorm"; // todo: confirm this
|
|
||||||
|
|
||||||
if (r.equals("2.16.840.1.113883.5.1008"))
|
|
||||||
return "http://hl7.org/fhir/v3/NullFlavor";
|
|
||||||
if (r.equals("2.16.840.1.113883.5.111"))
|
|
||||||
return "http://hl7.org/fhir/v3/RoleCode";
|
|
||||||
if (r.equals("2.16.840.1.113883.5.4"))
|
|
||||||
return "http://hl7.org/fhir/v3/ActCode";
|
|
||||||
if (r.equals("2.16.840.1.113883.5.8"))
|
|
||||||
return "http://hl7.org/fhir/v3/ActReason";
|
|
||||||
if (r.equals("2.16.840.1.113883.5.83"))
|
|
||||||
return "http://hl7.org/fhir/v3/ObservationInterpretation";
|
|
||||||
if (r.equals("2.16.840.1.113883.6.238"))
|
|
||||||
return "http://hl7.org/fhir/v3/Race";
|
|
||||||
|
|
||||||
if (r.equals("2.16.840.1.113883.6.59"))
|
|
||||||
return "http://hl7.org/fhir/sid/cvx";
|
|
||||||
if (r.equals("2.16.840.1.113883.12.292"))
|
|
||||||
return "http://hl7.org/fhir/sid/cvx";
|
|
||||||
|
|
||||||
if (r.equals("2.16.840.1.113883.6.12"))
|
|
||||||
return "http://www.ama-assn.org/go/cpt";
|
|
||||||
|
|
||||||
if (r.startsWith("2.16.840.1.113883.12."))
|
|
||||||
return "http://hl7.org/fhir/sid/v2-"+r.substring(21);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,193 +0,0 @@
|
||||||
package org.hl7.fhir.utilities.ucum;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR - Converter
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2017 University Health Network
|
|
||||||
* %%
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
* #L%
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
* Crown Copyright (c) 2006+, Copyright (c) 2006 - 2014 Kestral Computing & Health Intersections.
|
|
||||||
* All rights reserved. This program and the accompanying materials
|
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
|
||||||
* which accompanies this distribution, and is available at
|
|
||||||
* http://www.eclipse.org/legal/epl-v10.html
|
|
||||||
*
|
|
||||||
* Contributors:
|
|
||||||
* Kestral Computing P/L - initial implementation
|
|
||||||
* Health Intersections - ongoing maintenance
|
|
||||||
*******************************************************************************/
|
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.hl7.fhir.exceptions.UcumException;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* General Ucum Service
|
|
||||||
*
|
|
||||||
* UCUM functionality that is useful for applications that make use of UCUM codes
|
|
||||||
*
|
|
||||||
* This is a tightly bound interface - consumers use the internal model classes
|
|
||||||
*
|
|
||||||
* @author Grahame Grieve
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public interface UcumService {
|
|
||||||
|
|
||||||
public class UcumVersionDetails {
|
|
||||||
private Date releaseDate;
|
|
||||||
private String version;
|
|
||||||
/**
|
|
||||||
* @param releaseDate
|
|
||||||
* @param version
|
|
||||||
*/
|
|
||||||
public UcumVersionDetails(Date releaseDate, String version) {
|
|
||||||
super();
|
|
||||||
this.releaseDate = releaseDate;
|
|
||||||
this.version = version;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @return the releaseDate
|
|
||||||
*/
|
|
||||||
public Date getReleaseDate() {
|
|
||||||
return releaseDate;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @return the version
|
|
||||||
*/
|
|
||||||
public String getVersion() {
|
|
||||||
return version;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* return Ucum Identification details for the version in use
|
|
||||||
*/
|
|
||||||
public abstract UcumVersionDetails ucumIdentification();
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check UCUM. Note that this stands as a test of the service
|
|
||||||
* more than UCUM itself (for version 1.7, there are no known
|
|
||||||
* semantic errors in UCUM). But you should always run this test at least
|
|
||||||
* once with the version of UCUM you are using to ensure that
|
|
||||||
* the service implementation correctly understands the UCUM data
|
|
||||||
* to which it is bound
|
|
||||||
*
|
|
||||||
* @return a list of internal errors in the UCUM spec.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public abstract List<String> validateUCUM();
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* return a list of the defined types of units in this UCUM version
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public abstract Set<String> getProperties();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* validate whether a unit code are valid UCUM units
|
|
||||||
*
|
|
||||||
* @param units - the unit code to check
|
|
||||||
* @return nil if valid, or an error message describing the problem
|
|
||||||
*/
|
|
||||||
public abstract String validate(String unit);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* given a unit, return a formal description of what the units stand for using
|
|
||||||
* full names
|
|
||||||
* @param units the unit code
|
|
||||||
* @return formal description
|
|
||||||
* @throws UcumException
|
|
||||||
* @throws OHFException
|
|
||||||
*/
|
|
||||||
public String analyse(String unit) throws UcumException ;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* validate whether a units are valid UCUM units and additionally require that the
|
|
||||||
* units from a particular property
|
|
||||||
*
|
|
||||||
* @param units - the unit code to check
|
|
||||||
* @return nil if valid, or an error message describing the problem
|
|
||||||
*/
|
|
||||||
public abstract String validateInProperty(String unit, String property);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* validate whether a units are valid UCUM units and additionally require that the
|
|
||||||
* units match a particular base canonical unit
|
|
||||||
*
|
|
||||||
* @param units - the unit code to check
|
|
||||||
* @return nil if valid, or an error message describing the problem
|
|
||||||
*/
|
|
||||||
public abstract String validateCanonicalUnits(String unit, String canonical);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* given a set of units, return their canonical form
|
|
||||||
* @param unit
|
|
||||||
* @return the canonical form
|
|
||||||
* @throws UcumException
|
|
||||||
* @throws OHFException
|
|
||||||
*/
|
|
||||||
public abstract String getCanonicalUnits(String unit) throws UcumException ;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* given two pairs of units, return true if they sahre the same canonical base
|
|
||||||
*
|
|
||||||
* @param units1
|
|
||||||
* @param units2
|
|
||||||
* @return
|
|
||||||
* @throws UcumException
|
|
||||||
* @
|
|
||||||
*/
|
|
||||||
public abstract boolean isComparable(String units1, String units2) throws UcumException ;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* given a value and source unit, return the value in the given dest unit
|
|
||||||
* an exception is thrown if the conversion is not possible
|
|
||||||
*
|
|
||||||
* @param value
|
|
||||||
* @param sourceUnit
|
|
||||||
* @param destUnit
|
|
||||||
* @return the value if a conversion is possible
|
|
||||||
* @throws UcumException
|
|
||||||
* @throws OHFException
|
|
||||||
*/
|
|
||||||
public abstract Decimal convert(Decimal value, String sourceUnit, String destUnit) throws UcumException ;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* given a set of UCUM units, return a likely preferred human dense form
|
|
||||||
*
|
|
||||||
* SI units - as is.
|
|
||||||
* Other units - improved by manual fixes, or the removal of []
|
|
||||||
*
|
|
||||||
* @param code
|
|
||||||
* @return the preferred human display form
|
|
||||||
*/
|
|
||||||
public abstract String getCommonDisplay(String code);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,8 +1,13 @@
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<!-- The parent of this project is the deployable POM. This project isn't deployable, but this keeps it before the root pom in the reactor order when building the site. I don't know why this works...
|
<!--
|
||||||
Need to investigate this. -->
|
The parent of this project is the deployable POM. This project isn't
|
||||||
|
deployable, but this keeps it before the root pom in the reactor
|
||||||
|
order when building the site. I don't know why this works...
|
||||||
|
Need to investigate this.
|
||||||
|
-->
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
|
@ -225,14 +230,14 @@
|
||||||
<artifactId>spring-web</artifactId>
|
<artifactId>spring-web</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
For some reason JavaDoc crashed during site generation unless we have this dependency
|
For some reason JavaDoc crashed during site generation unless we have this dependency
|
||||||
-->
|
-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>javax.interceptor</groupId>
|
<groupId>javax.interceptor</groupId>
|
||||||
<artifactId>javax.interceptor-api</artifactId>
|
<artifactId>javax.interceptor-api</artifactId>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
@ -253,6 +258,13 @@
|
||||||
</plugins>
|
</plugins>
|
||||||
</pluginManagement>
|
</pluginManagement>
|
||||||
<plugins>
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.basepom.maven</groupId>
|
||||||
|
<artifactId>duplicate-finder-maven-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<skip>true</skip>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.jacoco</groupId>
|
<groupId>org.jacoco</groupId>
|
||||||
<artifactId>jacoco-maven-plugin</artifactId>
|
<artifactId>jacoco-maven-plugin</artifactId>
|
||||||
|
@ -285,19 +297,17 @@
|
||||||
</fileSets>
|
</fileSets>
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
<execution>
|
<execution>
|
||||||
<id>post-integration-test</id>
|
<id>post-integration-test</id>
|
||||||
<phase>install</phase>
|
<phase>install</phase>
|
||||||
<goals>
|
<goals>
|
||||||
<goal>report</goal>
|
<goal>report</goal>
|
||||||
</goals>
|
</goals>
|
||||||
<configuration>
|
<configuration>
|
||||||
<dataFile>${project.build.directory}/jacoco.exec</dataFile>
|
<dataFile>${project.build.directory}/jacoco.exec</dataFile>
|
||||||
<outputDirectory>${project.reporting.outputDirectory}/jacoco-report</outputDirectory>
|
<outputDirectory>${project.reporting.outputDirectory}/jacoco-report</outputDirectory>
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
|
|
||||||
|
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
|
|
|
@ -74,6 +74,11 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- Unit test dependencies -->
|
<!-- Unit test dependencies -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.codehaus.woodstox</groupId>
|
||||||
|
<artifactId>woodstox-core-asl</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.guava</groupId>
|
<groupId>com.google.guava</groupId>
|
||||||
<artifactId>guava</artifactId>
|
<artifactId>guava</artifactId>
|
||||||
|
|
|
@ -34,12 +34,14 @@ import ca.uhn.fhir.rest.client.impl.RestfulClientFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Restful Client Factory, based on Jax Rs
|
* A Restful Client Factory, based on Jax Rs
|
||||||
*
|
* Default Jax-Rs client is NOT thread safe in static context, you should create a new factory every time or
|
||||||
|
* use a specific Jax-Rs client implementation which managed connection pool.
|
||||||
* @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare
|
* @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare
|
||||||
*/
|
*/
|
||||||
public class JaxRsRestfulClientFactory extends RestfulClientFactory {
|
public class JaxRsRestfulClientFactory extends RestfulClientFactory {
|
||||||
|
|
||||||
private Client myNativeClient;
|
private Client myNativeClient;
|
||||||
|
private List<Class<?>> registeredComponents;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor. Note that you must set the {@link FhirContext} manually using {@link #setFhirContext(FhirContext)} if this constructor is used!
|
* Constructor. Note that you must set the {@link FhirContext} manually using {@link #setFhirContext(FhirContext)} if this constructor is used!
|
||||||
|
@ -64,6 +66,12 @@ public class JaxRsRestfulClientFactory extends RestfulClientFactory {
|
||||||
myNativeClient = builder.build();
|
myNativeClient = builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (registeredComponents != null && !registeredComponents.isEmpty()) {
|
||||||
|
for (Class<?> c : registeredComponents) {
|
||||||
|
myNativeClient = myNativeClient.register(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return myNativeClient;
|
return myNativeClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,29 +81,46 @@ public class JaxRsRestfulClientFactory extends RestfulClientFactory {
|
||||||
return new JaxRsHttpClient(client, url, theIfNoneExistParams, theIfNoneExistString, theRequestType, theHeaders);
|
return new JaxRsHttpClient(client, url, theIfNoneExistParams, theIfNoneExistString, theRequestType, theHeaders);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Not supported with default Jax-Rs client implementation
|
||||||
|
* @param theHost
|
||||||
|
* The host (or null to disable proxying, as is the default)
|
||||||
|
* @param thePort
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void setProxy(String theHost, Integer thePort) {
|
public void setProxy(String theHost, Integer thePort) {
|
||||||
throw new UnsupportedOperationException("Proxies are not supported yet in JAX-RS client");
|
throw new UnsupportedOperationException("Proxies are not supported yet in JAX-RS client");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Only accept clients of type javax.ws.rs.client.Client
|
* Only accept clients of type javax.ws.rs.client.Client
|
||||||
*
|
* Can be used to set a specific Client implementation
|
||||||
* @param theHttpClient
|
* @param theHttpClient
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public synchronized void setHttpClient(Object theHttpClient) {
|
public synchronized void setHttpClient(Object theHttpClient) {
|
||||||
this.myNativeClient = (Client) theHttpClient;
|
this.myNativeClient = (Client) theHttpClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a list of Jax-Rs component (provider, filter...)
|
||||||
|
* @param components list of Jax-Rs components to register
|
||||||
|
*/
|
||||||
|
public void register(List<Class<?>> components) {
|
||||||
|
registeredComponents = components;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected JaxRsHttpClient getHttpClient(String theServerBase) {
|
protected JaxRsHttpClient getHttpClient(String theServerBase) {
|
||||||
return new JaxRsHttpClient(getNativeClientClient(), new StringBuilder(theServerBase), null, null, null, null);
|
return new JaxRsHttpClient(getNativeClientClient(), new StringBuilder(theServerBase), null, null, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void resetHttpClient() {
|
protected void resetHttpClient() {
|
||||||
this.myNativeClient = null;
|
if (myNativeClient != null)
|
||||||
}
|
myNativeClient.close(); // close client to avoid memory leak
|
||||||
|
myNativeClient = null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
package ca.uhn.fhir.jaxrs.client;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import javax.ws.rs.client.Client;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Sebastien Riviere on 31/07/2017.
|
||||||
|
*/
|
||||||
|
public class JaxRsRestfulClientFactoryTest {
|
||||||
|
|
||||||
|
private final FhirContext context = FhirContext.forDstu2();
|
||||||
|
private JaxRsRestfulClientFactory factory;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
factory = new JaxRsRestfulClientFactory(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void emptyConstructorTest() {
|
||||||
|
assertNotNull(new JaxRsRestfulClientFactory());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getDefaultNativeClientTest() {
|
||||||
|
assertNotNull(factory.getNativeClientClient());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getNativeClientEmptyRegisteredComponentListTest() {
|
||||||
|
factory.register(new ArrayList<Class<?>>());
|
||||||
|
final Client result = factory.getNativeClientClient();
|
||||||
|
assertNotNull(result);
|
||||||
|
assertTrue(result.getConfiguration().getClasses().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getNativeClientRegisteredComponentListTest() {
|
||||||
|
factory.register(Arrays.asList(MyFilter.class, String.class));
|
||||||
|
final Client result = factory.getNativeClientClient();
|
||||||
|
assertNotNull(result);
|
||||||
|
assertEquals(1, result.getConfiguration().getClasses().size());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package ca.uhn.fhir.jaxrs.client;
|
||||||
|
|
||||||
|
import javax.ws.rs.client.ClientRequestContext;
|
||||||
|
import javax.ws.rs.client.ClientResponseContext;
|
||||||
|
import javax.ws.rs.client.ClientResponseFilter;
|
||||||
|
import javax.ws.rs.ext.Provider;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Sebastien Riviere on 31/07/2017.
|
||||||
|
*/
|
||||||
|
@Provider
|
||||||
|
public class MyFilter implements ClientResponseFilter {
|
||||||
|
@Override
|
||||||
|
public void filter(final ClientRequestContext requestContext, final ClientResponseContext responseContext) throws IOException {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
<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>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
|
@ -32,6 +32,12 @@
|
||||||
<version>3.5</version>
|
<version>3.5</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.codehaus.woodstox</groupId>
|
||||||
|
<artifactId>woodstox-core-asl</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>net.sf.saxon</groupId>
|
<groupId>net.sf.saxon</groupId>
|
||||||
<artifactId>Saxon-HE</artifactId>
|
<artifactId>Saxon-HE</artifactId>
|
||||||
|
@ -105,10 +111,12 @@
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!--
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
<artifactId>jcl-over-slf4j</artifactId>
|
<artifactId>jcl-over-slf4j</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
-->
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>ch.qos.logback</groupId>
|
<groupId>ch.qos.logback</groupId>
|
||||||
|
@ -163,7 +171,7 @@
|
||||||
<artifactId>thymeleaf-spring4</artifactId>
|
<artifactId>thymeleaf-spring4</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- For UCUM -->
|
<!-- For UCUM: TODO we should replace this with org.fhir UCUM -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.jscience</groupId>
|
<groupId>org.jscience</groupId>
|
||||||
<artifactId>jscience</artifactId>
|
<artifactId>jscience</artifactId>
|
||||||
|
@ -233,10 +241,12 @@
|
||||||
<!-- <dependency> <groupId>org.hsqldb</groupId> <artifactId>hsqldb</artifactId> <version>2.3.2</version> </dependency> -->
|
<!-- <dependency> <groupId>org.hsqldb</groupId> <artifactId>hsqldb</artifactId> <version>2.3.2</version> </dependency> -->
|
||||||
|
|
||||||
<!-- Spring -->
|
<!-- Spring -->
|
||||||
|
<!--
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>aopalliance</groupId>
|
<groupId>aopalliance</groupId>
|
||||||
<artifactId>aopalliance</artifactId>
|
<artifactId>aopalliance</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework</groupId>
|
<groupId>org.springframework</groupId>
|
||||||
<artifactId>spring-core</artifactId>
|
<artifactId>spring-core</artifactId>
|
||||||
|
@ -358,6 +368,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>javax.mail</groupId>
|
<groupId>javax.mail</groupId>
|
||||||
<artifactId>javax.mail-api</artifactId>
|
<artifactId>javax.mail-api</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.sun.mail</groupId>
|
<groupId>com.sun.mail</groupId>
|
||||||
|
|
|
@ -24,6 +24,7 @@ import ca.uhn.fhir.jpa.graphql.JpaStorageServices;
|
||||||
import ca.uhn.fhir.jpa.search.*;
|
import ca.uhn.fhir.jpa.search.*;
|
||||||
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
|
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
|
||||||
import ca.uhn.fhir.jpa.sp.SearchParamPresenceSvcImpl;
|
import ca.uhn.fhir.jpa.sp.SearchParamPresenceSvcImpl;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.email.SubscriptionEmailInterceptor;
|
||||||
import ca.uhn.fhir.jpa.subscription.resthook.SubscriptionRestHookInterceptor;
|
import ca.uhn.fhir.jpa.subscription.resthook.SubscriptionRestHookInterceptor;
|
||||||
import ca.uhn.fhir.jpa.subscription.websocket.SubscriptionWebsocketInterceptor;
|
import ca.uhn.fhir.jpa.subscription.websocket.SubscriptionWebsocketInterceptor;
|
||||||
import org.hl7.fhir.utilities.graphql.IGraphQLStorageServices;
|
import org.hl7.fhir.utilities.graphql.IGraphQLStorageServices;
|
||||||
|
@ -93,15 +94,6 @@ public class BaseConfig implements SchedulingConfigurer {
|
||||||
return new StaleSearchDeletingSvcImpl();
|
return new StaleSearchDeletingSvcImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
// @PostConstruct
|
|
||||||
// public void wireResourceDaos() {
|
|
||||||
// Map<String, IDao> daoBeans = myAppCtx.getBeansOfType(IDao.class);
|
|
||||||
// List bean = myAppCtx.getBean("myResourceProvidersDstu2", List.class);
|
|
||||||
// for (IDao next : daoBeans.values()) {
|
|
||||||
// next.setResourceDaos(bean);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@Lazy
|
@Lazy
|
||||||
public SubscriptionRestHookInterceptor subscriptionRestHookInterceptor() {
|
public SubscriptionRestHookInterceptor subscriptionRestHookInterceptor() {
|
||||||
|
@ -114,15 +106,23 @@ public class BaseConfig implements SchedulingConfigurer {
|
||||||
return new SubscriptionWebsocketInterceptor();
|
return new SubscriptionWebsocketInterceptor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Note: If you're going to use this, you need to provide a bean
|
||||||
|
* of type {@link ca.uhn.fhir.jpa.subscription.email.IEmailSender}
|
||||||
|
* in your own Spring config
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
@Lazy
|
||||||
|
public SubscriptionEmailInterceptor subscriptionEmailInterceptor() {
|
||||||
|
return new SubscriptionEmailInterceptor();
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public TaskScheduler taskScheduler() {
|
public TaskScheduler taskScheduler() {
|
||||||
ConcurrentTaskScheduler retVal = new ConcurrentTaskScheduler();
|
ConcurrentTaskScheduler retVal = new ConcurrentTaskScheduler();
|
||||||
retVal.setConcurrentExecutor(scheduledExecutorService().getObject());
|
retVal.setConcurrentExecutor(scheduledExecutorService().getObject());
|
||||||
retVal.setScheduledExecutor(scheduledExecutorService().getObject());
|
retVal.setScheduledExecutor(scheduledExecutorService().getObject());
|
||||||
return retVal;
|
return retVal;
|
||||||
// ThreadPoolTaskScheduler retVal = new ThreadPoolTaskScheduler();
|
|
||||||
// retVal.setPoolSize(5);
|
|
||||||
// return retVal;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -29,7 +29,6 @@ import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider;
|
||||||
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
|
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
|
||||||
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
|
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
|
||||||
import ca.uhn.fhir.jpa.util.DeleteConflict;
|
import ca.uhn.fhir.jpa.util.DeleteConflict;
|
||||||
import ca.uhn.fhir.jpa.util.StopWatch;
|
|
||||||
import ca.uhn.fhir.model.api.*;
|
import ca.uhn.fhir.model.api.*;
|
||||||
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
|
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
|
||||||
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
|
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
|
||||||
|
@ -51,10 +50,7 @@ import ca.uhn.fhir.rest.server.exceptions.*;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||||
import ca.uhn.fhir.util.CoverageIgnore;
|
import ca.uhn.fhir.util.*;
|
||||||
import ca.uhn.fhir.util.FhirTerser;
|
|
||||||
import ca.uhn.fhir.util.OperationOutcomeUtil;
|
|
||||||
import ca.uhn.fhir.util.UrlUtil;
|
|
||||||
import com.google.common.base.Charsets;
|
import com.google.common.base.Charsets;
|
||||||
import com.google.common.collect.ArrayListMultimap;
|
import com.google.common.collect.ArrayListMultimap;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
|
@ -2044,7 +2040,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
StringBuilder b = new StringBuilder();
|
StringBuilder b = new StringBuilder();
|
||||||
if (theResource instanceof IResource) {
|
if (theResource instanceof IResource) {
|
||||||
IResource resource = (IResource) theResource;
|
IResource resource = (IResource) theResource;
|
||||||
List<XMLEvent> xmlEvents = resource.getText().getDiv().getValue();
|
List<XMLEvent> xmlEvents = XmlUtil.parse(resource.getText().getDiv().getValue());
|
||||||
if (xmlEvents != null) {
|
if (xmlEvents != null) {
|
||||||
for (XMLEvent next : xmlEvents) {
|
for (XMLEvent next : xmlEvents) {
|
||||||
if (next.isCharacters()) {
|
if (next.isCharacters()) {
|
||||||
|
@ -2057,8 +2053,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
IDomainResource resource = (IDomainResource) theResource;
|
IDomainResource resource = (IDomainResource) theResource;
|
||||||
try {
|
try {
|
||||||
String divAsString = resource.getText().getDivAsString();
|
String divAsString = resource.getText().getDivAsString();
|
||||||
XhtmlDt xhtml = new XhtmlDt(divAsString);
|
List<XMLEvent> xmlEvents = XmlUtil.parse(divAsString);
|
||||||
List<XMLEvent> xmlEvents = xhtml.getValue();
|
|
||||||
if (xmlEvents != null) {
|
if (xmlEvents != null) {
|
||||||
for (XMLEvent next : xmlEvents) {
|
for (XMLEvent next : xmlEvents) {
|
||||||
if (next.isCharacters()) {
|
if (next.isCharacters()) {
|
||||||
|
|
|
@ -30,6 +30,7 @@ import ca.uhn.fhir.jpa.dao.data.IResourceTableDao;
|
||||||
import ca.uhn.fhir.jpa.dao.data.ISearchResultDao;
|
import ca.uhn.fhir.jpa.dao.data.ISearchResultDao;
|
||||||
import ca.uhn.fhir.jpa.entity.*;
|
import ca.uhn.fhir.jpa.entity.*;
|
||||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||||
|
import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider;
|
||||||
import ca.uhn.fhir.jpa.util.DeleteConflict;
|
import ca.uhn.fhir.jpa.util.DeleteConflict;
|
||||||
import ca.uhn.fhir.jpa.util.StopWatch;
|
import ca.uhn.fhir.jpa.util.StopWatch;
|
||||||
import ca.uhn.fhir.jpa.util.jsonpatch.JsonPatchUtils;
|
import ca.uhn.fhir.jpa.util.jsonpatch.JsonPatchUtils;
|
||||||
|
@ -61,6 +62,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import javax.persistence.NoResultException;
|
import javax.persistence.NoResultException;
|
||||||
import javax.persistence.TypedQuery;
|
import javax.persistence.TypedQuery;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
@ -894,6 +896,12 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
@Transactional(propagation = Propagation.SUPPORTS)
|
@Transactional(propagation = Propagation.SUPPORTS)
|
||||||
@Override
|
@Override
|
||||||
public IBundleProvider search(final SearchParameterMap theParams, RequestDetails theRequestDetails) {
|
public IBundleProvider search(final SearchParameterMap theParams, RequestDetails theRequestDetails) {
|
||||||
|
return search(theParams, theRequestDetails, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(propagation = Propagation.SUPPORTS)
|
||||||
|
@Override
|
||||||
|
public IBundleProvider search(final SearchParameterMap theParams, RequestDetails theRequestDetails, HttpServletResponse theServletResponse) {
|
||||||
|
|
||||||
if (myDaoConfig.getIndexMissingFields() == DaoConfig.IndexEnabledEnum.DISABLED) {
|
if (myDaoConfig.getIndexMissingFields() == DaoConfig.IndexEnabledEnum.DISABLED) {
|
||||||
for (List<List<? extends IQueryParameterType>> nextAnds : theParams.values()) {
|
for (List<List<? extends IQueryParameterType>> nextAnds : theParams.values()) {
|
||||||
|
@ -930,7 +938,19 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
cacheControlDirective.parse(theRequestDetails.getHeaders(Constants.HEADER_CACHE_CONTROL));
|
cacheControlDirective.parse(theRequestDetails.getHeaders(Constants.HEADER_CACHE_CONTROL));
|
||||||
}
|
}
|
||||||
|
|
||||||
return mySearchCoordinatorSvc.registerSearch(this, theParams, getResourceName(), cacheControlDirective);
|
IBundleProvider retVal = mySearchCoordinatorSvc.registerSearch(this, theParams, getResourceName(), cacheControlDirective);
|
||||||
|
|
||||||
|
if (retVal instanceof PersistedJpaBundleProvider) {
|
||||||
|
PersistedJpaBundleProvider provider = (PersistedJpaBundleProvider) retVal;
|
||||||
|
if (provider.isCacheHit()) {
|
||||||
|
if (theServletResponse != null && theRequestDetails != null) {
|
||||||
|
String value = "HIT from " + theRequestDetails.getFhirServerBase();
|
||||||
|
theServletResponse.addHeader(Constants.HEADER_X_CACHE, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -108,6 +108,7 @@ public class DaoConfig {
|
||||||
private Set<String> myTreatReferencesAsLogical = new HashSet<String>(DEFAULT_LOGICAL_BASE_URLS);
|
private Set<String> myTreatReferencesAsLogical = new HashSet<String>(DEFAULT_LOGICAL_BASE_URLS);
|
||||||
private boolean myAutoCreatePlaceholderReferenceTargets;
|
private boolean myAutoCreatePlaceholderReferenceTargets;
|
||||||
private Integer myCacheControlNoStoreMaxResultsUpperLimit = 1000;
|
private Integer myCacheControlNoStoreMaxResultsUpperLimit = 1000;
|
||||||
|
private Integer myCountSearchResultsUpTo = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
|
@ -152,6 +153,56 @@ public class DaoConfig {
|
||||||
myCacheControlNoStoreMaxResultsUpperLimit = theCacheControlNoStoreMaxResults;
|
myCacheControlNoStoreMaxResultsUpperLimit = theCacheControlNoStoreMaxResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When searching, if set to a non-null value (default is <code>null</code>) the
|
||||||
|
* search coordinator will attempt to find at least this many results
|
||||||
|
* before returning a response to the client. This parameter mainly affects
|
||||||
|
* whether a "total count" is included in the response bundle for searches that
|
||||||
|
* return large amounts of data.
|
||||||
|
* <p>
|
||||||
|
* For a search that returns 10000 results, if this value is set to
|
||||||
|
* 10000 the search coordinator will find all 10000 results
|
||||||
|
* prior to returning, so the initial response bundle will have the
|
||||||
|
* total set to 10000. If this value is null (or less than 10000)
|
||||||
|
* the response bundle will likely return slightly faster, but will
|
||||||
|
* not include the total. Subsequent page requests will likely
|
||||||
|
* include the total however, if they are performed after the
|
||||||
|
* search coordinator has found all results.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* Set this value to <code>0</code> to always load all
|
||||||
|
* results before returning.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public Integer getCountSearchResultsUpTo() {
|
||||||
|
return myCountSearchResultsUpTo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When searching, if set to a non-null value (default is <code>null</code>) the
|
||||||
|
* search coordinator will attempt to find at least this many results
|
||||||
|
* before returning a response to the client. This parameter mainly affects
|
||||||
|
* whether a "total count" is included in the response bundle for searches that
|
||||||
|
* return large amounts of data.
|
||||||
|
* <p>
|
||||||
|
* For a search that returns 10000 results, if this value is set to
|
||||||
|
* 10000 the search coordinator will find all 10000 results
|
||||||
|
* prior to returning, so the initial response bundle will have the
|
||||||
|
* total set to 10000. If this value is null (or less than 10000)
|
||||||
|
* the response bundle will likely return slightly faster, but will
|
||||||
|
* not include the total. Subsequent page requests will likely
|
||||||
|
* include the total however, if they are performed after the
|
||||||
|
* search coordinator has found all results.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* Set this value to <code>0</code> to always load all
|
||||||
|
* results before returning.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public void setCountSearchResultsUpTo(Integer theCountSearchResultsUpTo) {
|
||||||
|
myCountSearchResultsUpTo = theCountSearchResultsUpTo;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When a code system is added that contains more than this number of codes,
|
* When a code system is added that contains more than this number of codes,
|
||||||
* the code system will be indexed later in an incremental process in order to
|
* the code system will be indexed later in an incremental process in order to
|
||||||
|
@ -357,11 +408,8 @@ public class DaoConfig {
|
||||||
/**
|
/**
|
||||||
* This may be used to optionally register server interceptors directly against the DAOs.
|
* This may be used to optionally register server interceptors directly against the DAOs.
|
||||||
*/
|
*/
|
||||||
public void setInterceptors(IServerInterceptor... theInterceptor) {
|
public void setInterceptors(List<IServerInterceptor> theInterceptors) {
|
||||||
setInterceptors(new ArrayList<IServerInterceptor>());
|
myInterceptors = theInterceptors;
|
||||||
if (theInterceptor != null && theInterceptor.length != 0) {
|
|
||||||
getInterceptors().addAll(Arrays.asList(theInterceptor));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -959,8 +1007,11 @@ public class DaoConfig {
|
||||||
/**
|
/**
|
||||||
* This may be used to optionally register server interceptors directly against the DAOs.
|
* This may be used to optionally register server interceptors directly against the DAOs.
|
||||||
*/
|
*/
|
||||||
public void setInterceptors(List<IServerInterceptor> theInterceptors) {
|
public void setInterceptors(IServerInterceptor... theInterceptor) {
|
||||||
myInterceptors = theInterceptors;
|
setInterceptors(new ArrayList<IServerInterceptor>());
|
||||||
|
if (theInterceptor != null && theInterceptor.length != 0) {
|
||||||
|
getInterceptors().addAll(Arrays.asList(theInterceptor));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -33,6 +33,10 @@ import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||||
|
import org.springframework.transaction.annotation.Propagation;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
public interface IFhirResourceDao<T extends IBaseResource> extends IDao {
|
public interface IFhirResourceDao<T extends IBaseResource> extends IDao {
|
||||||
|
|
||||||
|
@ -179,6 +183,9 @@ public interface IFhirResourceDao<T extends IBaseResource> extends IDao {
|
||||||
|
|
||||||
IBundleProvider search(SearchParameterMap theParams, RequestDetails theRequestDetails);
|
IBundleProvider search(SearchParameterMap theParams, RequestDetails theRequestDetails);
|
||||||
|
|
||||||
|
@Transactional(propagation = Propagation.SUPPORTS)
|
||||||
|
IBundleProvider search(SearchParameterMap theParams, RequestDetails theRequestDetails, HttpServletResponse theServletResponse);
|
||||||
|
|
||||||
Set<Long> searchForIds(SearchParameterMap theParams);
|
Set<Long> searchForIds(SearchParameterMap theParams);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -274,14 +274,6 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated As of HAPI FHIR 2.4 this method no longer does anything
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public void setPersistResults(boolean thePersistResults) {
|
|
||||||
// does nothing as of HAPI FHIR 2.4
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRevIncludes(Set<Include> theRevIncludes) {
|
public void setRevIncludes(Set<Include> theRevIncludes) {
|
||||||
myRevIncludes = theRevIncludes;
|
myRevIncludes = theRevIncludes;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ import java.util.Map.Entry;
|
||||||
import javax.persistence.TypedQuery;
|
import javax.persistence.TypedQuery;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.util.StopWatch;
|
import ca.uhn.fhir.jpa.util.StopWatch;
|
||||||
|
import ca.uhn.fhir.rest.param.ParameterUtil;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.apache.http.NameValuePair;
|
import org.apache.http.NameValuePair;
|
||||||
import org.hibernate.Session;
|
import org.hibernate.Session;
|
||||||
|
@ -438,7 +439,11 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
|
||||||
DaoMethodOutcome outcome;
|
DaoMethodOutcome outcome;
|
||||||
UrlParts parts = UrlUtil.parseUrl(url);
|
UrlParts parts = UrlUtil.parseUrl(url);
|
||||||
if (isNotBlank(parts.getResourceId())) {
|
if (isNotBlank(parts.getResourceId())) {
|
||||||
res.setId(new IdType(parts.getResourceType(), parts.getResourceId()));
|
String version = null;
|
||||||
|
if (isNotBlank(nextReqEntry.getRequest().getIfMatch())) {
|
||||||
|
version = ParameterUtil.parseETagValue(nextReqEntry.getRequest().getIfMatch());
|
||||||
|
}
|
||||||
|
res.setId(new IdType(parts.getResourceType(), parts.getResourceId(), version));
|
||||||
outcome = resourceDao.update(res, null, false, theRequestDetails);
|
outcome = resourceDao.update(res, null, false, theRequestDetails);
|
||||||
} else {
|
} else {
|
||||||
res.setId((String) null);
|
res.setId((String) null);
|
||||||
|
|
|
@ -215,6 +215,9 @@ public class SearchParamRegistryDstu3 extends BaseSearchParamRegistry {
|
||||||
case RETIRED:
|
case RETIRED:
|
||||||
status = RuntimeSearchParamStatusEnum.RETIRED;
|
status = RuntimeSearchParamStatusEnum.RETIRED;
|
||||||
break;
|
break;
|
||||||
|
case UNKNOWN:
|
||||||
|
status = RuntimeSearchParamStatusEnum.UNKNOWN;
|
||||||
|
break;
|
||||||
case NULL:
|
case NULL:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ import java.util.Map.Entry;
|
||||||
|
|
||||||
import javax.persistence.TypedQuery;
|
import javax.persistence.TypedQuery;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.rest.param.ParameterUtil;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.apache.http.NameValuePair;
|
import org.apache.http.NameValuePair;
|
||||||
import org.hl7.fhir.r4.model.*;
|
import org.hl7.fhir.r4.model.*;
|
||||||
|
@ -435,7 +436,11 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
|
||||||
DaoMethodOutcome outcome;
|
DaoMethodOutcome outcome;
|
||||||
UrlParts parts = UrlUtil.parseUrl(url);
|
UrlParts parts = UrlUtil.parseUrl(url);
|
||||||
if (isNotBlank(parts.getResourceId())) {
|
if (isNotBlank(parts.getResourceId())) {
|
||||||
res.setId(new IdType(parts.getResourceType(), parts.getResourceId()));
|
String version = null;
|
||||||
|
if (isNotBlank(nextReqEntry.getRequest().getIfMatch())) {
|
||||||
|
version = ParameterUtil.parseETagValue(nextReqEntry.getRequest().getIfMatch());
|
||||||
|
}
|
||||||
|
res.setId(new IdType(parts.getResourceType(), parts.getResourceId(), version));
|
||||||
outcome = resourceDao.update(res, null, false, theRequestDetails);
|
outcome = resourceDao.update(res, null, false, theRequestDetails);
|
||||||
} else {
|
} else {
|
||||||
res.setId((String) null);
|
res.setId((String) null);
|
||||||
|
|
|
@ -216,6 +216,9 @@ public class SearchParamRegistryR4 extends BaseSearchParamRegistry {
|
||||||
case RETIRED:
|
case RETIRED:
|
||||||
status = RuntimeSearchParamStatusEnum.RETIRED;
|
status = RuntimeSearchParamStatusEnum.RETIRED;
|
||||||
break;
|
break;
|
||||||
|
case UNKNOWN:
|
||||||
|
status = RuntimeSearchParamStatusEnum.UNKNOWN;
|
||||||
|
break;
|
||||||
case NULL:
|
case NULL:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,6 +115,8 @@ public class JpaStorageServices extends BaseHapiFhirDao<IBaseResource> implement
|
||||||
break;
|
break;
|
||||||
case URI:
|
case URI:
|
||||||
break;
|
break;
|
||||||
|
case HAS:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
params.add(nextArgument.getName(), param);
|
params.add(nextArgument.getName(), param);
|
||||||
|
|
|
@ -24,6 +24,7 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
|
||||||
|
@ -47,7 +48,7 @@ public class JpaSystemProviderDstu2 extends BaseJpaSystemProviderDstu2Plus<Bundl
|
||||||
@Qualifier("mySystemDaoDstu2")
|
@Qualifier("mySystemDaoDstu2")
|
||||||
private IFhirSystemDao<Bundle, MetaDt> mySystemDao;
|
private IFhirSystemDao<Bundle, MetaDt> mySystemDao;
|
||||||
|
|
||||||
@Autowired
|
@Autowired(required = false)
|
||||||
private IFulltextSearchSvc mySearchDao;
|
private IFulltextSearchSvc mySearchDao;
|
||||||
|
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
|
@ -178,6 +179,7 @@ public class JpaSystemProviderDstu2 extends BaseJpaSystemProviderDstu2Plus<Bundl
|
||||||
@OperationParam(name="searchParam", min=1, max=1) String theSearchParam,
|
@OperationParam(name="searchParam", min=1, max=1) String theSearchParam,
|
||||||
@OperationParam(name="text", min=1, max=1) String theText
|
@OperationParam(name="text", min=1, max=1) String theText
|
||||||
) {
|
) {
|
||||||
|
JpaSystemProviderDstu3.validateFulltextSearchEnabled(mySearchDao);
|
||||||
|
|
||||||
if (isBlank(theContext)) {
|
if (isBlank(theContext)) {
|
||||||
throw new InvalidRequestException("Parameter 'context' must be provided");
|
throw new InvalidRequestException("Parameter 'context' must be provided");
|
||||||
|
@ -193,11 +195,9 @@ public class JpaSystemProviderDstu2 extends BaseJpaSystemProviderDstu2Plus<Bundl
|
||||||
|
|
||||||
Parameters retVal = new Parameters();
|
Parameters retVal = new Parameters();
|
||||||
for (Suggestion next : keywords) {
|
for (Suggestion next : keywords) {
|
||||||
//@formatter:off
|
|
||||||
retVal.addParameter()
|
retVal.addParameter()
|
||||||
.addPart(new Parameter().setName("keyword").setValue(new StringDt(next.getTerm())))
|
.addPart(new Parameter().setName("keyword").setValue(new StringDt(next.getTerm())))
|
||||||
.addPart(new Parameter().setName("score").setValue(new DecimalDt(next.getScore())));
|
.addPart(new Parameter().setName("score").setValue(new DecimalDt(next.getScore())));
|
||||||
//@formatter:on
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return retVal;
|
return retVal;
|
||||||
|
|
|
@ -1,5 +1,27 @@
|
||||||
package ca.uhn.fhir.jpa.provider.dstu3;
|
package ca.uhn.fhir.jpa.provider.dstu3;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.dao.FulltextSearchSvcImpl.Suggestion;
|
||||||
|
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
||||||
|
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
|
||||||
|
import ca.uhn.fhir.jpa.provider.BaseJpaSystemProviderDstu2Plus;
|
||||||
|
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.annotation.Transaction;
|
||||||
|
import ca.uhn.fhir.rest.annotation.TransactionParam;
|
||||||
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||||
|
import org.hl7.fhir.dstu3.model.*;
|
||||||
|
import org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -21,23 +43,6 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
import java.util.*;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
|
|
||||||
import org.hl7.fhir.dstu3.model.*;
|
|
||||||
import org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.dao.FulltextSearchSvcImpl.Suggestion;
|
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
|
||||||
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
|
|
||||||
import ca.uhn.fhir.jpa.provider.BaseJpaSystemProviderDstu2Plus;
|
|
||||||
import ca.uhn.fhir.model.api.annotation.Description;
|
|
||||||
import ca.uhn.fhir.rest.annotation.*;
|
|
||||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
|
||||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
|
||||||
|
|
||||||
public class JpaSystemProviderDstu3 extends BaseJpaSystemProviderDstu2Plus<Bundle, Meta> {
|
public class JpaSystemProviderDstu3 extends BaseJpaSystemProviderDstu2Plus<Bundle, Meta> {
|
||||||
|
|
||||||
|
@ -45,106 +50,106 @@ public class JpaSystemProviderDstu3 extends BaseJpaSystemProviderDstu2Plus<Bundl
|
||||||
@Qualifier("mySystemDaoDstu3")
|
@Qualifier("mySystemDaoDstu3")
|
||||||
private IFhirSystemDao<Bundle, Meta> mySystemDao;
|
private IFhirSystemDao<Bundle, Meta> mySystemDao;
|
||||||
|
|
||||||
@Autowired
|
@Autowired(required = false)
|
||||||
private IFulltextSearchSvc mySearchDao;
|
private IFulltextSearchSvc mySearchDao;
|
||||||
|
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
// This is generated by hand:
|
// This is generated by hand:
|
||||||
// ls hapi-fhir-structures-dstu2/target/generated-sources/tinder/ca/uhn/fhir/model/dstu2/resource/ | sort | sed "s/.java//" | sed "s/^/@OperationParam(name=\"/" | sed "s/$/\", type=IntegerType.class, min=0, max=1),/"
|
// ls hapi-fhir-structures-dstu2/target/generated-sources/tinder/ca/uhn/fhir/model/dstu2/resource/ | sort | sed "s/.java//" | sed "s/^/@OperationParam(name=\"/" | sed "s/$/\", type=IntegerType.class, min=0, max=1),/"
|
||||||
@Operation(name="$get-resource-counts", idempotent=true, returnParameters= {
|
@Operation(name = "$get-resource-counts", idempotent = true, returnParameters = {
|
||||||
@OperationParam(name="AllergyIntolerance", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "AllergyIntolerance", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="Appointment", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "Appointment", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="AppointmentResponse", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "AppointmentResponse", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="AuditEvent", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "AuditEvent", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="Basic", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "Basic", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="Binary", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "Binary", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="BodySite", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "BodySite", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="Bundle", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "Bundle", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="CarePlan", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "CarePlan", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="CarePlan2", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "CarePlan2", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="Claim", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "Claim", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="ClaimResponse", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "ClaimResponse", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="ClinicalImpression", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "ClinicalImpression", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="Communication", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "Communication", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="CommunicationRequest", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "CommunicationRequest", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="Composition", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "Composition", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="ConceptMap", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "ConceptMap", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="Condition", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "Condition", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="Conformance", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "Conformance", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="Contract", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "Contract", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="Contraindication", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "Contraindication", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="Coverage", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "Coverage", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="DataElement", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "DataElement", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="Device", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "Device", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="DeviceComponent", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "DeviceComponent", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="DeviceMetric", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "DeviceMetric", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="DeviceUseRequest", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "DeviceUseRequest", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="DeviceUseStatement", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "DeviceUseStatement", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="DiagnosticOrder", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "DiagnosticOrder", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="DiagnosticReport", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "DiagnosticReport", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="DocumentManifest", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "DocumentManifest", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="DocumentReference", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "DocumentReference", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="EligibilityRequest", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "EligibilityRequest", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="EligibilityResponse", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "EligibilityResponse", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="Encounter", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "Encounter", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="EnrollmentRequest", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "EnrollmentRequest", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="EnrollmentResponse", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "EnrollmentResponse", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="EpisodeOfCare", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "EpisodeOfCare", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="ExplanationOfBenefit", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "ExplanationOfBenefit", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="FamilyMemberHistory", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "FamilyMemberHistory", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="Flag", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "Flag", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="Goal", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "Goal", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="Group", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "Group", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="HealthcareService", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "HealthcareService", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="ImagingObjectSelection", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "ImagingObjectSelection", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="ImagingStudy", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "ImagingStudy", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="Immunization", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "Immunization", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="ImmunizationRecommendation", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "ImmunizationRecommendation", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="ListResource", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "ListResource", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="Location", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "Location", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="Media", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "Media", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="Medication", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "Medication", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="MedicationAdministration", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "MedicationAdministration", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="MedicationDispense", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "MedicationDispense", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="MedicationPrescription", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "MedicationPrescription", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="MedicationStatement", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "MedicationStatement", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="MessageHeader", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "MessageHeader", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="NamingSystem", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "NamingSystem", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="NutritionOrder", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "NutritionOrder", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="Observation", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "Observation", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="OperationDefinition", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "OperationDefinition", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="OperationOutcome", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "OperationOutcome", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="Order", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "Order", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="OrderResponse", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "OrderResponse", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="Organization", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "Organization", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="Parameters", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "Parameters", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="Patient", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "Patient", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="PaymentNotice", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "PaymentNotice", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="PaymentReconciliation", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "PaymentReconciliation", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="Person", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "Person", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="Practitioner", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "Practitioner", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="Procedure", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "Procedure", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="ProcedureRequest", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "ProcedureRequest", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="ProcessRequest", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "ProcessRequest", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="ProcessResponse", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "ProcessResponse", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="Provenance", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "Provenance", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="Questionnaire", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "Questionnaire", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="QuestionnaireAnswers", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "QuestionnaireAnswers", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="ReferralRequest", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "ReferralRequest", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="RelatedPerson", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "RelatedPerson", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="RiskAssessment", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "RiskAssessment", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="Schedule", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "Schedule", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="SearchParameter", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "SearchParameter", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="Slot", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "Slot", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="Specimen", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "Specimen", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="StructureDefinition", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "StructureDefinition", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="Subscription", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "Subscription", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="Substance", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "Substance", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="Supply", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "Supply", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="ValueSet", type=IntegerType.class, min=0, max=1),
|
@OperationParam(name = "ValueSet", type = IntegerType.class, min = 0, max = 1),
|
||||||
@OperationParam(name="VisionPrescription", type=IntegerType.class, min=0, max=1)
|
@OperationParam(name = "VisionPrescription", type = IntegerType.class, min = 0, max = 1)
|
||||||
})
|
})
|
||||||
@Description(shortDefinition="Provides the number of resources currently stored on the server, broken down by resource type")
|
@Description(shortDefinition = "Provides the number of resources currently stored on the server, broken down by resource type")
|
||||||
//@formatter:on
|
//@formatter:on
|
||||||
public Parameters getResourceCounts() {
|
public Parameters getResourceCounts() {
|
||||||
Parameters retVal = new Parameters();
|
Parameters retVal = new Parameters();
|
||||||
|
@ -159,8 +164,8 @@ public class JpaSystemProviderDstu3 extends BaseJpaSystemProviderDstu2Plus<Bundl
|
||||||
}
|
}
|
||||||
|
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
@Operation(name="$meta", idempotent=true, returnParameters= {
|
@Operation(name = "$meta", idempotent = true, returnParameters = {
|
||||||
@OperationParam(name="return", type=Meta.class)
|
@OperationParam(name = "return", type = Meta.class)
|
||||||
})
|
})
|
||||||
//@formatter:on
|
//@formatter:on
|
||||||
public Parameters meta(RequestDetails theRequestDetails) {
|
public Parameters meta(RequestDetails theRequestDetails) {
|
||||||
|
@ -169,12 +174,13 @@ public class JpaSystemProviderDstu3 extends BaseJpaSystemProviderDstu2Plus<Bundl
|
||||||
return parameters;
|
return parameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(name="$suggest-keywords", idempotent=true)
|
@Operation(name = "$suggest-keywords", idempotent = true)
|
||||||
public Parameters suggestKeywords(
|
public Parameters suggestKeywords(
|
||||||
@OperationParam(name="context", min=1, max=1) String theContext,
|
@OperationParam(name = "context", min = 1, max = 1) String theContext,
|
||||||
@OperationParam(name="searchParam", min=1, max=1) String theSearchParam,
|
@OperationParam(name = "searchParam", min = 1, max = 1) String theSearchParam,
|
||||||
@OperationParam(name="text", min=1, max=1) String theText
|
@OperationParam(name = "text", min = 1, max = 1) String theText
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
||||||
if (isBlank(theContext)) {
|
if (isBlank(theContext)) {
|
||||||
throw new InvalidRequestException("Parameter 'context' must be provided");
|
throw new InvalidRequestException("Parameter 'context' must be provided");
|
||||||
|
@ -192,8 +198,8 @@ public class JpaSystemProviderDstu3 extends BaseJpaSystemProviderDstu2Plus<Bundl
|
||||||
for (Suggestion next : keywords) {
|
for (Suggestion next : keywords) {
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
retVal.addParameter()
|
retVal.addParameter()
|
||||||
.addPart(new ParametersParameterComponent().setName("keyword").setValue(new StringType(next.getTerm())))
|
.addPart(new ParametersParameterComponent().setName("keyword").setValue(new StringType(next.getTerm())))
|
||||||
.addPart(new ParametersParameterComponent().setName("score").setValue(new DecimalType(next.getScore())));
|
.addPart(new ParametersParameterComponent().setName("score").setValue(new DecimalType(next.getScore())));
|
||||||
//@formatter:on
|
//@formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,4 +216,10 @@ public class JpaSystemProviderDstu3 extends BaseJpaSystemProviderDstu2Plus<Bundl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void validateFulltextSearchEnabled(IFulltextSearchSvc theSearchDao) {
|
||||||
|
if (theSearchDao == null || theSearchDao.isDisabled()) {
|
||||||
|
throw new InvalidRequestException("Fulltext searching is disabled on this server");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ public class JpaSystemProviderR4 extends BaseJpaSystemProviderDstu2Plus<Bundle,
|
||||||
@Qualifier("mySystemDaoR4")
|
@Qualifier("mySystemDaoR4")
|
||||||
private IFhirSystemDao<Bundle, Meta> mySystemDao;
|
private IFhirSystemDao<Bundle, Meta> mySystemDao;
|
||||||
|
|
||||||
@Autowired
|
@Autowired(required = false)
|
||||||
private IFulltextSearchSvc mySearchDao;
|
private IFulltextSearchSvc mySearchDao;
|
||||||
|
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
|
@ -175,6 +175,7 @@ public class JpaSystemProviderR4 extends BaseJpaSystemProviderDstu2Plus<Bundle,
|
||||||
@OperationParam(name="searchParam", min=1, max=1) String theSearchParam,
|
@OperationParam(name="searchParam", min=1, max=1) String theSearchParam,
|
||||||
@OperationParam(name="text", min=1, max=1) String theText
|
@OperationParam(name="text", min=1, max=1) String theText
|
||||||
) {
|
) {
|
||||||
|
ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3.validateFulltextSearchEnabled(mySearchDao);
|
||||||
|
|
||||||
if (isBlank(theContext)) {
|
if (isBlank(theContext)) {
|
||||||
throw new InvalidRequestException("Parameter 'context' must be provided");
|
throw new InvalidRequestException("Parameter 'context' must be provided");
|
||||||
|
|
|
@ -19,23 +19,32 @@ package ca.uhn.fhir.jpa.search;
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
import javax.persistence.*;
|
|
||||||
import javax.persistence.criteria.*;
|
|
||||||
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
|
||||||
import org.springframework.transaction.PlatformTransactionManager;
|
|
||||||
import org.springframework.transaction.TransactionStatus;
|
|
||||||
import org.springframework.transaction.support.*;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.jpa.dao.IDao;
|
import ca.uhn.fhir.jpa.dao.IDao;
|
||||||
import ca.uhn.fhir.jpa.dao.ISearchBuilder;
|
import ca.uhn.fhir.jpa.dao.ISearchBuilder;
|
||||||
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
|
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
|
||||||
import ca.uhn.fhir.jpa.entity.*;
|
import ca.uhn.fhir.jpa.entity.BaseHasResource;
|
||||||
|
import ca.uhn.fhir.jpa.entity.ResourceHistoryTable;
|
||||||
|
import ca.uhn.fhir.jpa.entity.Search;
|
||||||
|
import ca.uhn.fhir.jpa.entity.SearchTypeEnum;
|
||||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.springframework.transaction.PlatformTransactionManager;
|
||||||
|
import org.springframework.transaction.TransactionStatus;
|
||||||
|
import org.springframework.transaction.support.TransactionCallback;
|
||||||
|
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
|
||||||
|
import org.springframework.transaction.support.TransactionTemplate;
|
||||||
|
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.NoResultException;
|
||||||
|
import javax.persistence.TypedQuery;
|
||||||
|
import javax.persistence.criteria.CriteriaBuilder;
|
||||||
|
import javax.persistence.criteria.CriteriaQuery;
|
||||||
|
import javax.persistence.criteria.Predicate;
|
||||||
|
import javax.persistence.criteria.Root;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
public class PersistedJpaBundleProvider implements IBundleProvider {
|
public class PersistedJpaBundleProvider implements IBundleProvider {
|
||||||
|
|
||||||
|
@ -47,6 +56,7 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
|
||||||
private ISearchDao mySearchDao;
|
private ISearchDao mySearchDao;
|
||||||
private Search mySearchEntity;
|
private Search mySearchEntity;
|
||||||
private String myUuid;
|
private String myUuid;
|
||||||
|
private boolean myCacheHit;
|
||||||
|
|
||||||
public PersistedJpaBundleProvider(String theSearchUuid, IDao theDao) {
|
public PersistedJpaBundleProvider(String theSearchUuid, IDao theDao) {
|
||||||
myUuid = theSearchUuid;
|
myUuid = theSearchUuid;
|
||||||
|
@ -179,17 +189,17 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
|
||||||
});
|
});
|
||||||
|
|
||||||
switch (mySearchEntity.getSearchType()) {
|
switch (mySearchEntity.getSearchType()) {
|
||||||
case HISTORY:
|
case HISTORY:
|
||||||
return template.execute(new TransactionCallback<List<IBaseResource>>() {
|
return template.execute(new TransactionCallback<List<IBaseResource>>() {
|
||||||
@Override
|
@Override
|
||||||
public List<IBaseResource> doInTransaction(TransactionStatus theStatus) {
|
public List<IBaseResource> doInTransaction(TransactionStatus theStatus) {
|
||||||
return doHistoryInTransaction(theFromIndex, theToIndex);
|
return doHistoryInTransaction(theFromIndex, theToIndex);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
case SEARCH:
|
case SEARCH:
|
||||||
case EVERYTHING:
|
case EVERYTHING:
|
||||||
default:
|
default:
|
||||||
return doSearchOrEverything(theFromIndex, theToIndex);
|
return doSearchOrEverything(theFromIndex, theToIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,6 +207,14 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
|
||||||
return myUuid;
|
return myUuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isCacheHit() {
|
||||||
|
return myCacheHit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCacheHit(boolean theCacheHit) {
|
||||||
|
myCacheHit = theCacheHit;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Integer preferredPageSize() {
|
public Integer preferredPageSize() {
|
||||||
ensureSearchEntityLoaded();
|
ensureSearchEntityLoaded();
|
||||||
|
|
|
@ -67,8 +67,11 @@ public class PersistedJpaSearchFirstPageBundleProvider extends PersistedJpaBundl
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Integer size() {
|
public Integer size() {
|
||||||
mySearchTask.awaitInitialSync();
|
Integer size = mySearchTask.awaitInitialSync();
|
||||||
SearchCoordinatorSvcImpl.verifySearchHasntFailedOrThrowInternalErrorException(mySearch);
|
SearchCoordinatorSvcImpl.verifySearchHasntFailedOrThrowInternalErrorException(mySearch);
|
||||||
|
if (size != null) {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
return super.size();
|
return super.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ package ca.uhn.fhir.jpa.search;
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
* #L%
|
* #L%family
|
||||||
*/
|
*/
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
|
@ -184,6 +184,8 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
||||||
StopWatch w = new StopWatch();
|
StopWatch w = new StopWatch();
|
||||||
final String searchUuid = UUID.randomUUID().toString();
|
final String searchUuid = UUID.randomUUID().toString();
|
||||||
|
|
||||||
|
ourLog.debug("Registering new search {}", searchUuid);
|
||||||
|
|
||||||
Class<? extends IBaseResource> resourceTypeClass = myContext.getResourceDefinition(theResourceType).getImplementingClass();
|
Class<? extends IBaseResource> resourceTypeClass = myContext.getResourceDefinition(theResourceType).getImplementingClass();
|
||||||
final ISearchBuilder sb = theCallingDao.newSearchBuilder();
|
final ISearchBuilder sb = theCallingDao.newSearchBuilder();
|
||||||
sb.setType(resourceTypeClass, theResourceType);
|
sb.setType(resourceTypeClass, theResourceType);
|
||||||
|
@ -204,6 +206,8 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
||||||
|
|
||||||
if (theParams.isLoadSynchronous() || loadSynchronousUpTo != null) {
|
if (theParams.isLoadSynchronous() || loadSynchronousUpTo != null) {
|
||||||
|
|
||||||
|
ourLog.debug("Search {} is loading in synchronous mode", searchUuid);
|
||||||
|
|
||||||
// Execute the query and make sure we return distinct results
|
// Execute the query and make sure we return distinct results
|
||||||
TransactionTemplate txTemplate = new TransactionTemplate(myManagedTxManager);
|
TransactionTemplate txTemplate = new TransactionTemplate(myManagedTxManager);
|
||||||
txTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRED);
|
txTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRED);
|
||||||
|
@ -281,6 +285,8 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
||||||
mySearchDao.updateSearchLastReturned(searchToUse.getId(), new Date());
|
mySearchDao.updateSearchLastReturned(searchToUse.getId(), new Date());
|
||||||
|
|
||||||
retVal = new PersistedJpaBundleProvider(searchToUse.getUuid(), theCallingDao);
|
retVal = new PersistedJpaBundleProvider(searchToUse.getUuid(), theCallingDao);
|
||||||
|
retVal.setCacheHit(true);
|
||||||
|
|
||||||
populateBundleProvider(retVal);
|
populateBundleProvider(retVal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -437,7 +443,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
||||||
mySearchUuid = theSearchUuid;
|
mySearchUuid = theSearchUuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void awaitInitialSync() {
|
public Integer awaitInitialSync() {
|
||||||
ourLog.trace("Awaiting initial sync");
|
ourLog.trace("Awaiting initial sync");
|
||||||
do {
|
do {
|
||||||
try {
|
try {
|
||||||
|
@ -449,6 +455,8 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
||||||
}
|
}
|
||||||
} while (mySearch.getStatus() == SearchStatusEnum.LOADING);
|
} while (mySearch.getStatus() == SearchStatusEnum.LOADING);
|
||||||
ourLog.trace("Initial sync completed");
|
ourLog.trace("Initial sync completed");
|
||||||
|
|
||||||
|
return mySearch.getTotalCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -507,6 +515,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
||||||
}
|
}
|
||||||
|
|
||||||
myIdToSearchTask.remove(mySearch.getUuid());
|
myIdToSearchTask.remove(mySearch.getUuid());
|
||||||
|
myInitialCollectionLatch.countDown();
|
||||||
myCompletionLatch.countDown();
|
myCompletionLatch.countDown();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -552,27 +561,27 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
||||||
public List<Long> getResourcePids(int theFromIndex, int theToIndex) {
|
public List<Long> getResourcePids(int theFromIndex, int theToIndex) {
|
||||||
ourLog.info("Requesting search PIDs from {}-{}", theFromIndex, theToIndex);
|
ourLog.info("Requesting search PIDs from {}-{}", theFromIndex, theToIndex);
|
||||||
|
|
||||||
CountDownLatch latch = null;
|
boolean keepWaiting;
|
||||||
synchronized (mySyncedPids) {
|
do {
|
||||||
if (mySyncedPids.size() < theToIndex && mySearch.getStatus() == SearchStatusEnum.LOADING) {
|
synchronized (mySyncedPids) {
|
||||||
int latchSize = theToIndex - mySyncedPids.size();
|
keepWaiting = false;
|
||||||
ourLog.trace("Registering latch to await {} results (want {} total)", latchSize, theToIndex);
|
if (mySyncedPids.size() < theToIndex && mySearch.getStatus() == SearchStatusEnum.LOADING) {
|
||||||
latch = new CountDownLatch(latchSize);
|
keepWaiting = true;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (latch != null) {
|
|
||||||
while (latch.getCount() > 0 && mySearch.getStatus() == SearchStatusEnum.LOADING) {
|
|
||||||
try {
|
|
||||||
ourLog.trace("Awaiting latch with {}", latch.getCount());
|
|
||||||
latch.await(500, TimeUnit.MILLISECONDS);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
// ok
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
if (keepWaiting) {
|
||||||
|
ourLog.info("Waiting, as we only have {} results", mySyncedPids.size());
|
||||||
|
try {
|
||||||
|
Thread.sleep(500);
|
||||||
|
} catch (InterruptedException theE) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (keepWaiting);
|
||||||
|
|
||||||
ArrayList<Long> retVal = new ArrayList<Long>();
|
ourLog.info("Proceeding, as we have {} results", mySyncedPids.size());
|
||||||
|
|
||||||
|
ArrayList<Long> retVal = new ArrayList<>();
|
||||||
synchronized (mySyncedPids) {
|
synchronized (mySyncedPids) {
|
||||||
verifySearchHasntFailedOrThrowInternalErrorException(mySearch);
|
verifySearchHasntFailedOrThrowInternalErrorException(mySearch);
|
||||||
|
|
||||||
|
@ -585,6 +594,8 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ourLog.info("Done syncing results", mySyncedPids.size());
|
||||||
|
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -633,8 +644,8 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
||||||
myUnsyncedPids.clear();
|
myUnsyncedPids.clear();
|
||||||
|
|
||||||
if (theResultIter.hasNext() == false) {
|
if (theResultIter.hasNext() == false) {
|
||||||
mySearch.setStatus(SearchStatusEnum.FINISHED);
|
|
||||||
mySearch.setTotalCount(myCountSaved);
|
mySearch.setTotalCount(myCountSaved);
|
||||||
|
mySearch.setStatus(SearchStatusEnum.FINISHED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mySearch.setNumFound(myCountSaved);
|
mySearch.setNumFound(myCountSaved);
|
||||||
|
@ -643,7 +654,16 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
myInitialCollectionLatch.countDown();
|
int numSynced;
|
||||||
|
synchronized (mySyncedPids) {
|
||||||
|
numSynced = mySyncedPids.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (myDaoConfig.getCountSearchResultsUpTo() == null ||
|
||||||
|
myDaoConfig.getCountSearchResultsUpTo() <= 0 ||
|
||||||
|
myDaoConfig.getCountSearchResultsUpTo() <= numSynced) {
|
||||||
|
myInitialCollectionLatch.countDown();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,8 +20,12 @@ package ca.uhn.fhir.jpa.search;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.util.Date;
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
|
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
|
||||||
|
import ca.uhn.fhir.jpa.dao.data.ISearchIncludeDao;
|
||||||
|
import ca.uhn.fhir.jpa.dao.data.ISearchResultDao;
|
||||||
|
import ca.uhn.fhir.jpa.entity.Search;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import org.apache.commons.lang3.time.DateUtils;
|
import org.apache.commons.lang3.time.DateUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.domain.PageRequest;
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
@ -34,11 +38,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.transaction.support.TransactionCallback;
|
import org.springframework.transaction.support.TransactionCallback;
|
||||||
import org.springframework.transaction.support.TransactionTemplate;
|
import org.springframework.transaction.support.TransactionTemplate;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import java.util.Date;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
|
||||||
import ca.uhn.fhir.jpa.dao.data.*;
|
|
||||||
import ca.uhn.fhir.jpa.entity.Search;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes old searches
|
* Deletes old searches
|
||||||
|
@ -46,35 +46,32 @@ import ca.uhn.fhir.jpa.entity.Search;
|
||||||
public class StaleSearchDeletingSvcImpl implements IStaleSearchDeletingSvc {
|
public class StaleSearchDeletingSvcImpl implements IStaleSearchDeletingSvc {
|
||||||
public static final long DEFAULT_CUTOFF_SLACK = 10 * DateUtils.MILLIS_PER_SECOND;
|
public static final long DEFAULT_CUTOFF_SLACK = 10 * DateUtils.MILLIS_PER_SECOND;
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(StaleSearchDeletingSvcImpl.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(StaleSearchDeletingSvcImpl.class);
|
||||||
|
private static Long ourNowForUnitTests;
|
||||||
/*
|
/*
|
||||||
* We give a bit of extra leeway just to avoid race conditions where a query result
|
* We give a bit of extra leeway just to avoid race conditions where a query result
|
||||||
* is being reused (because a new client request came in with the same params) right before
|
* is being reused (because a new client request came in with the same params) right before
|
||||||
* the result is to be deleted
|
* the result is to be deleted
|
||||||
*/
|
*/
|
||||||
private long myCutoffSlack = DEFAULT_CUTOFF_SLACK;
|
private long myCutoffSlack = DEFAULT_CUTOFF_SLACK;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private DaoConfig myDaoConfig;
|
private DaoConfig myDaoConfig;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISearchDao mySearchDao;
|
private ISearchDao mySearchDao;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISearchIncludeDao mySearchIncludeDao;
|
private ISearchIncludeDao mySearchIncludeDao;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISearchResultDao mySearchResultDao;
|
private ISearchResultDao mySearchResultDao;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private PlatformTransactionManager myTransactionManager;
|
private PlatformTransactionManager myTransactionManager;
|
||||||
|
|
||||||
private void deleteSearch(final Long theSearchPid) {
|
private void deleteSearch(final Long theSearchPid) {
|
||||||
Search searchToDelete = mySearchDao.findOne(theSearchPid);
|
Search searchToDelete = mySearchDao.findOne(theSearchPid);
|
||||||
ourLog.info("Deleting search {}/{} - Created[{}] -- Last returned[{}]", searchToDelete.getId(), searchToDelete.getUuid(), searchToDelete.getCreated(), searchToDelete.getSearchLastReturned());
|
if (searchToDelete != null) {
|
||||||
mySearchIncludeDao.deleteForSearch(searchToDelete.getId());
|
ourLog.info("Deleting search {}/{} - Created[{}] -- Last returned[{}]", searchToDelete.getId(), searchToDelete.getUuid(), searchToDelete.getCreated(), searchToDelete.getSearchLastReturned());
|
||||||
mySearchResultDao.deleteForSearch(searchToDelete.getId());
|
mySearchIncludeDao.deleteForSearch(searchToDelete.getId());
|
||||||
mySearchDao.delete(searchToDelete);
|
mySearchResultDao.deleteForSearch(searchToDelete.getId());
|
||||||
|
mySearchDao.delete(searchToDelete);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -85,7 +82,7 @@ public class StaleSearchDeletingSvcImpl implements IStaleSearchDeletingSvc {
|
||||||
if (myDaoConfig.getReuseCachedSearchResultsForMillis() != null) {
|
if (myDaoConfig.getReuseCachedSearchResultsForMillis() != null) {
|
||||||
cutoffMillis = Math.max(cutoffMillis, myDaoConfig.getReuseCachedSearchResultsForMillis());
|
cutoffMillis = Math.max(cutoffMillis, myDaoConfig.getReuseCachedSearchResultsForMillis());
|
||||||
}
|
}
|
||||||
final Date cutoff = new Date((System.currentTimeMillis() - cutoffMillis) - myCutoffSlack);
|
final Date cutoff = new Date((now() - cutoffMillis) - myCutoffSlack);
|
||||||
|
|
||||||
ourLog.debug("Searching for searches which are before {}", cutoff);
|
ourLog.debug("Searching for searches which are before {}", cutoff);
|
||||||
|
|
||||||
|
@ -129,4 +126,19 @@ public class StaleSearchDeletingSvcImpl implements IStaleSearchDeletingSvc {
|
||||||
myCutoffSlack = theCutoffSlack;
|
myCutoffSlack = theCutoffSlack;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static long now() {
|
||||||
|
if (ourNowForUnitTests != null) {
|
||||||
|
return ourNowForUnitTests;
|
||||||
|
}
|
||||||
|
return System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is for unit tests only, do not call otherwise
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
public static void setNowForUnitTests(Long theNowForUnitTests) {
|
||||||
|
ourNowForUnitTests = theNowForUnitTests;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -156,13 +156,11 @@ public abstract class BaseSubscriptionInterceptor<S extends IBaseResource> exten
|
||||||
try {
|
try {
|
||||||
from = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_EMAIL_FROM);
|
from = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_EMAIL_FROM);
|
||||||
subjectTemplate = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_SUBJECT_TEMPLATE);
|
subjectTemplate = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_SUBJECT_TEMPLATE);
|
||||||
bodyTemplate = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_BODY_TEMPLATE);
|
|
||||||
} catch (FHIRException theE) {
|
} catch (FHIRException theE) {
|
||||||
throw new ConfigurationException("Failed to extract subscription extension(s): " + theE.getMessage(), theE);
|
throw new ConfigurationException("Failed to extract subscription extension(s): " + theE.getMessage(), theE);
|
||||||
}
|
}
|
||||||
retVal.getEmailDetails().setFrom(from);
|
retVal.getEmailDetails().setFrom(from);
|
||||||
retVal.getEmailDetails().setSubjectTemplate(subjectTemplate);
|
retVal.getEmailDetails().setSubjectTemplate(subjectTemplate);
|
||||||
retVal.getEmailDetails().setBodyTemplate(bodyTemplate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (FHIRException theE) {
|
} catch (FHIRException theE) {
|
||||||
|
@ -191,13 +189,11 @@ public abstract class BaseSubscriptionInterceptor<S extends IBaseResource> exten
|
||||||
try {
|
try {
|
||||||
from = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_EMAIL_FROM);
|
from = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_EMAIL_FROM);
|
||||||
subjectTemplate = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_SUBJECT_TEMPLATE);
|
subjectTemplate = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_SUBJECT_TEMPLATE);
|
||||||
bodyTemplate = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_BODY_TEMPLATE);
|
|
||||||
} catch (FHIRException theE) {
|
} catch (FHIRException theE) {
|
||||||
throw new ConfigurationException("Failed to extract subscription extension(s): " + theE.getMessage(), theE);
|
throw new ConfigurationException("Failed to extract subscription extension(s): " + theE.getMessage(), theE);
|
||||||
}
|
}
|
||||||
retVal.getEmailDetails().setFrom(from);
|
retVal.getEmailDetails().setFrom(from);
|
||||||
retVal.getEmailDetails().setSubjectTemplate(subjectTemplate);
|
retVal.getEmailDetails().setSubjectTemplate(subjectTemplate);
|
||||||
retVal.getEmailDetails().setBodyTemplate(bodyTemplate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<org.hl7.fhir.r4.model.Extension> topicExts = subscription.getExtensionsByUrl("http://hl7.org/fhir/subscription/topics");
|
List<org.hl7.fhir.r4.model.Extension> topicExts = subscription.getExtensionsByUrl("http://hl7.org/fhir/subscription/topics");
|
||||||
|
@ -459,7 +455,7 @@ public abstract class BaseSubscriptionInterceptor<S extends IBaseResource> exten
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mySubscriptionActivatingSubscriber == null) {
|
if (mySubscriptionActivatingSubscriber == null) {
|
||||||
mySubscriptionActivatingSubscriber = new SubscriptionActivatingSubscriber(getSubscriptionDao(), getChannelType(), this);
|
mySubscriptionActivatingSubscriber = new SubscriptionActivatingSubscriber(getSubscriptionDao(), getChannelType(), this, myTxManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
registerSubscriptionCheckingSubscriber();
|
registerSubscriptionCheckingSubscriber();
|
||||||
|
|
|
@ -131,10 +131,12 @@ public class CanonicalSubscription implements Serializable {
|
||||||
return myHeaders;
|
return myHeaders;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setHeaders(String theHeaders) {
|
public void setHeaders(List<? extends IPrimitiveType<String>> theHeader) {
|
||||||
myHeaders = new ArrayList<>();
|
myHeaders = new ArrayList<>();
|
||||||
if (isNotBlank(theHeaders)) {
|
for (IPrimitiveType<String> next : theHeader) {
|
||||||
myHeaders.add(theHeaders);
|
if (isNotBlank(next.getValueAsString())) {
|
||||||
|
myHeaders.add(next.getValueAsString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,12 +191,10 @@ public class CanonicalSubscription implements Serializable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setHeaders(List<? extends IPrimitiveType<String>> theHeader) {
|
public void setHeaders(String theHeaders) {
|
||||||
myHeaders = new ArrayList<>();
|
myHeaders = new ArrayList<>();
|
||||||
for (IPrimitiveType<String> next : theHeader) {
|
if (isNotBlank(theHeaders)) {
|
||||||
if (isNotBlank(next.getValueAsString())) {
|
myHeaders.add(theHeaders);
|
||||||
myHeaders.add(next.getValueAsString());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,16 +212,6 @@ public class CanonicalSubscription implements Serializable {
|
||||||
private String myFrom;
|
private String myFrom;
|
||||||
@JsonProperty("subjectTemplate")
|
@JsonProperty("subjectTemplate")
|
||||||
private String mySubjectTemplate;
|
private String mySubjectTemplate;
|
||||||
@JsonProperty("bodyTemplate")
|
|
||||||
private String myBodyTemplate;
|
|
||||||
|
|
||||||
public String getBodyTemplate() {
|
|
||||||
return myBodyTemplate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBodyTemplate(String theBodyTemplate) {
|
|
||||||
myBodyTemplate = theBodyTemplate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getFrom() {
|
public String getFrom() {
|
||||||
return myFrom;
|
return myFrom;
|
||||||
|
|
|
@ -27,20 +27,21 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
import org.hl7.fhir.r4.model.Subscription;
|
import org.hl7.fhir.r4.model.Subscription;
|
||||||
import org.hl7.fhir.utilities.ucum.Canonical;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.messaging.Message;
|
|
||||||
import org.springframework.messaging.MessagingException;
|
import org.springframework.messaging.MessagingException;
|
||||||
|
import org.springframework.transaction.PlatformTransactionManager;
|
||||||
|
import org.springframework.transaction.TransactionStatus;
|
||||||
|
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
|
||||||
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
|
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
|
||||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||||
|
import org.springframework.transaction.support.TransactionTemplate;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public class SubscriptionActivatingSubscriber {
|
public class SubscriptionActivatingSubscriber {
|
||||||
private final IFhirResourceDao mySubscriptionDao;
|
private final IFhirResourceDao mySubscriptionDao;
|
||||||
private final BaseSubscriptionInterceptor mySubscriptionInterceptor;
|
private final BaseSubscriptionInterceptor mySubscriptionInterceptor;
|
||||||
|
private final PlatformTransactionManager myTransactionManager;
|
||||||
private Logger ourLog = LoggerFactory.getLogger(SubscriptionActivatingSubscriber.class);
|
private Logger ourLog = LoggerFactory.getLogger(SubscriptionActivatingSubscriber.class);
|
||||||
private FhirContext myCtx;
|
private FhirContext myCtx;
|
||||||
private Subscription.SubscriptionChannelType myChannelType;
|
private Subscription.SubscriptionChannelType myChannelType;
|
||||||
|
@ -48,11 +49,12 @@ public class SubscriptionActivatingSubscriber {
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*/
|
*/
|
||||||
public SubscriptionActivatingSubscriber(IFhirResourceDao<? extends IBaseResource> theSubscriptionDao, Subscription.SubscriptionChannelType theChannelType, BaseSubscriptionInterceptor theSubscriptionInterceptor) {
|
public SubscriptionActivatingSubscriber(IFhirResourceDao<? extends IBaseResource> theSubscriptionDao, Subscription.SubscriptionChannelType theChannelType, BaseSubscriptionInterceptor theSubscriptionInterceptor, PlatformTransactionManager theTransactionManager) {
|
||||||
mySubscriptionDao = theSubscriptionDao;
|
mySubscriptionDao = theSubscriptionDao;
|
||||||
mySubscriptionInterceptor = theSubscriptionInterceptor;
|
mySubscriptionInterceptor = theSubscriptionInterceptor;
|
||||||
myChannelType = theChannelType;
|
myChannelType = theChannelType;
|
||||||
myCtx = theSubscriptionDao.getContext();
|
myCtx = theSubscriptionDao.getContext();
|
||||||
|
myTransactionManager = theTransactionManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void activateAndRegisterSubscriptionIfRequired(final IBaseResource theSubscription) {
|
public void activateAndRegisterSubscriptionIfRequired(final IBaseResource theSubscription) {
|
||||||
|
@ -67,15 +69,16 @@ public class SubscriptionActivatingSubscriber {
|
||||||
final String requestedStatus = Subscription.SubscriptionStatus.REQUESTED.toCode();
|
final String requestedStatus = Subscription.SubscriptionStatus.REQUESTED.toCode();
|
||||||
final String activeStatus = Subscription.SubscriptionStatus.ACTIVE.toCode();
|
final String activeStatus = Subscription.SubscriptionStatus.ACTIVE.toCode();
|
||||||
if (requestedStatus.equals(statusString)) {
|
if (requestedStatus.equals(statusString)) {
|
||||||
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
|
if (TransactionSynchronizationManager.isSynchronizationActive()) {
|
||||||
@Override
|
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
|
||||||
public void afterCommit() {
|
@Override
|
||||||
status.setValueAsString(activeStatus);
|
public void afterCommit() {
|
||||||
ourLog.info("Activating and registering subscription {} from status {} to {}", theSubscription.getIdElement().toUnqualified().getValue(), requestedStatus, activeStatus);
|
activateSubscription(status, activeStatus, theSubscription, requestedStatus);
|
||||||
mySubscriptionDao.update(theSubscription);
|
}
|
||||||
mySubscriptionInterceptor.registerSubscription(theSubscription.getIdElement(), theSubscription);
|
});
|
||||||
}
|
} else {
|
||||||
});
|
activateSubscription(status, activeStatus, theSubscription, requestedStatus);
|
||||||
|
}
|
||||||
} else if (activeStatus.equals(statusString)) {
|
} else if (activeStatus.equals(statusString)) {
|
||||||
if (!mySubscriptionInterceptor.hasSubscription(theSubscription.getIdElement())) {
|
if (!mySubscriptionInterceptor.hasSubscription(theSubscription.getIdElement())) {
|
||||||
ourLog.info("Registering active subscription {}", theSubscription.getIdElement().toUnqualified().getValue());
|
ourLog.info("Registering active subscription {}", theSubscription.getIdElement().toUnqualified().getValue());
|
||||||
|
@ -89,8 +92,15 @@ public class SubscriptionActivatingSubscriber {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void activateSubscription(IPrimitiveType<?> theStatus, String theActiveStatus, IBaseResource theSubscription, String theRequestedStatus) {
|
||||||
|
theStatus.setValueAsString(theActiveStatus);
|
||||||
|
ourLog.info("Activating and registering subscription {} from status {} to {}", theSubscription.getIdElement().toUnqualified().getValue(), theRequestedStatus, theActiveStatus);
|
||||||
|
mySubscriptionDao.update(theSubscription);
|
||||||
|
mySubscriptionInterceptor.registerSubscription(theSubscription.getIdElement(), theSubscription);
|
||||||
|
}
|
||||||
|
|
||||||
public void handleMessage(RestOperationTypeEnum theOperationType, IIdType theId, IBaseResource theSubscription) throws MessagingException {
|
|
||||||
|
public void handleMessage(RestOperationTypeEnum theOperationType, IIdType theId, final IBaseResource theSubscription) throws MessagingException {
|
||||||
|
|
||||||
switch (theOperationType) {
|
switch (theOperationType) {
|
||||||
case DELETE:
|
case DELETE:
|
||||||
|
@ -101,7 +111,15 @@ public class SubscriptionActivatingSubscriber {
|
||||||
if (!theId.getResourceType().equals("Subscription")) {
|
if (!theId.getResourceType().equals("Subscription")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
activateAndRegisterSubscriptionIfRequired(theSubscription);
|
TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager);
|
||||||
|
txTemplate.execute(new TransactionCallbackWithoutResult() {
|
||||||
|
@Override
|
||||||
|
protected void doInTransactionWithoutResult(TransactionStatus status) {
|
||||||
|
activateAndRegisterSubscriptionIfRequired(theSubscription);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,8 @@ package ca.uhn.fhir.jpa.subscription.email;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class EmailDetails {
|
public class EmailDetails {
|
||||||
|
@ -27,6 +29,7 @@ public class EmailDetails {
|
||||||
private String myBodyTemplate;
|
private String myBodyTemplate;
|
||||||
private List<String> myTo;
|
private List<String> myTo;
|
||||||
private String myFrom;
|
private String myFrom;
|
||||||
|
private IIdType mySubscription;
|
||||||
|
|
||||||
public String getBodyTemplate() {
|
public String getBodyTemplate() {
|
||||||
return myBodyTemplate;
|
return myBodyTemplate;
|
||||||
|
@ -52,6 +55,14 @@ public class EmailDetails {
|
||||||
mySubjectTemplate = theSubjectTemplate;
|
mySubjectTemplate = theSubjectTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IIdType getSubscription() {
|
||||||
|
return mySubscription;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSubscription(IIdType theSubscription) {
|
||||||
|
mySubscription = theSubscription;
|
||||||
|
}
|
||||||
|
|
||||||
public List<String> getTo() {
|
public List<String> getTo() {
|
||||||
return myTo;
|
return myTo;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,11 +22,11 @@ package ca.uhn.fhir.jpa.subscription.email;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.util.StopWatch;
|
import ca.uhn.fhir.jpa.util.StopWatch;
|
||||||
import ca.uhn.fhir.rest.api.Constants;
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.mail.SimpleMailMessage;
|
|
||||||
import org.springframework.mail.javamail.JavaMailSender;
|
|
||||||
import org.springframework.mail.javamail.JavaMailSenderImpl;
|
import org.springframework.mail.javamail.JavaMailSenderImpl;
|
||||||
import org.thymeleaf.context.Context;
|
import org.thymeleaf.context.Context;
|
||||||
import org.thymeleaf.spring4.SpringTemplateEngine;
|
import org.thymeleaf.spring4.SpringTemplateEngine;
|
||||||
|
@ -35,6 +35,9 @@ import org.thymeleaf.templatemode.TemplateMode;
|
||||||
import org.thymeleaf.templateresolver.StringTemplateResolver;
|
import org.thymeleaf.templateresolver.StringTemplateResolver;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
|
import javax.mail.Message;
|
||||||
|
import javax.mail.MessagingException;
|
||||||
|
import javax.mail.internet.MimeMessage;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -42,26 +45,57 @@ import java.util.List;
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
import static org.apache.commons.lang3.StringUtils.trim;
|
import static org.apache.commons.lang3.StringUtils.trim;
|
||||||
|
|
||||||
public class EmailSender implements IEmailSender {
|
public class JavaMailEmailSender implements IEmailSender {
|
||||||
|
|
||||||
private static final Logger ourLog = LoggerFactory.getLogger(EmailSender.class);
|
private static final Logger ourLog = LoggerFactory.getLogger(JavaMailEmailSender.class);
|
||||||
private String mySmtpServerHost;
|
private String mySmtpServerHostname;
|
||||||
private int mySmtpServerPort = 25;
|
private int mySmtpServerPort = 25;
|
||||||
private JavaMailSenderImpl mySender;
|
private JavaMailSenderImpl mySender;
|
||||||
|
private String mySmtpServerUsername;
|
||||||
|
private String mySmtpServerPassword;
|
||||||
|
|
||||||
@PostConstruct
|
public String getSmtpServerHostname() {
|
||||||
public void start() {
|
return mySmtpServerHostname;
|
||||||
Validate.notBlank(mySmtpServerHost, "No SMTP host defined");
|
}
|
||||||
|
|
||||||
mySender = new JavaMailSenderImpl();
|
/**
|
||||||
mySender.setHost(mySmtpServerHost);
|
* Set the SMTP server host to use for outbound mail
|
||||||
mySender.setPort(mySmtpServerPort);
|
*/
|
||||||
mySender.setDefaultEncoding(Constants.CHARSET_UTF8.name());
|
public void setSmtpServerHostname(String theSmtpServerHostname) {
|
||||||
|
mySmtpServerHostname = theSmtpServerHostname;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSmtpServerPassword() {
|
||||||
|
return mySmtpServerPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSmtpServerPassword(String theSmtpServerPassword) {
|
||||||
|
mySmtpServerPassword = theSmtpServerPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSmtpServerPort() {
|
||||||
|
return mySmtpServerPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the SMTP server port to use for outbound mail
|
||||||
|
*/
|
||||||
|
public void setSmtpServerPort(int theSmtpServerPort) {
|
||||||
|
mySmtpServerPort = theSmtpServerPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSmtpServerUsername() {
|
||||||
|
return mySmtpServerUsername;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSmtpServerUsername(String theSmtpServerUsername) {
|
||||||
|
mySmtpServerUsername = theSmtpServerUsername;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void send(EmailDetails theDetails) {
|
public void send(EmailDetails theDetails) {
|
||||||
ourLog.info("Sending email to recipients: {}", theDetails.getTo());
|
String subscriptionId = theDetails.getSubscription().toUnqualifiedVersionless().getValue();
|
||||||
|
ourLog.info("Sending email for subscription {} to recipients: {}", subscriptionId, theDetails.getTo());
|
||||||
StopWatch sw = new StopWatch();
|
StopWatch sw = new StopWatch();
|
||||||
|
|
||||||
StringTemplateResolver templateResolver = new StringTemplateResolver();
|
StringTemplateResolver templateResolver = new StringTemplateResolver();
|
||||||
|
@ -80,39 +114,44 @@ public class EmailSender implements IEmailSender {
|
||||||
String body = engine.process(theDetails.getBodyTemplate(), context);
|
String body = engine.process(theDetails.getBodyTemplate(), context);
|
||||||
String subject = engine.process(theDetails.getSubjectTemplate(), context);
|
String subject = engine.process(theDetails.getSubjectTemplate(), context);
|
||||||
|
|
||||||
SimpleMailMessage email = new SimpleMailMessage();
|
MimeMessage email = mySender.createMimeMessage();
|
||||||
email.setFrom(trim(theDetails.getFrom()));
|
|
||||||
email.setTo(toTrimmedStringArray(theDetails.getTo()));
|
try {
|
||||||
email.setSubject(subject);
|
email.setFrom(trim(theDetails.getFrom()));
|
||||||
email.setText(body);
|
email.setRecipients(Message.RecipientType.TO, toTrimmedCommaSeparatedString(theDetails.getTo()));
|
||||||
email.setSentDate(new Date());
|
email.setSubject(subject);
|
||||||
|
email.setText(body);
|
||||||
|
email.setSentDate(new Date());
|
||||||
|
email.addHeader("X-FHIR-Subscription", subscriptionId);
|
||||||
|
} catch (MessagingException e) {
|
||||||
|
throw new InternalErrorException("Failed to create email messaage", e);
|
||||||
|
}
|
||||||
|
|
||||||
mySender.send(email);
|
mySender.send(email);
|
||||||
|
|
||||||
ourLog.info("Done sending email (took {}ms)", sw.getMillis());
|
ourLog.info("Done sending email (took {}ms)", sw.getMillis());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@PostConstruct
|
||||||
* Set the SMTP server host to use for outbound mail
|
public void start() {
|
||||||
*/
|
Validate.notBlank(mySmtpServerHostname, "No SMTP host defined");
|
||||||
public void setSmtpServerHost(String theSmtpServerHost) {
|
|
||||||
mySmtpServerHost = theSmtpServerHost;
|
mySender = new JavaMailSenderImpl();
|
||||||
|
mySender.setHost(getSmtpServerHostname());
|
||||||
|
mySender.setPort(getSmtpServerPort());
|
||||||
|
mySender.setUsername(getSmtpServerUsername());
|
||||||
|
mySender.setPassword(getSmtpServerPassword());
|
||||||
|
mySender.setDefaultEncoding(Constants.CHARSET_UTF8.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private static String toTrimmedCommaSeparatedString(List<String> theTo) {
|
||||||
* Set the SMTP server port to use for outbound mail
|
|
||||||
*/
|
|
||||||
public void setSmtpServerPort(int theSmtpServerPort) {
|
|
||||||
mySmtpServerPort = theSmtpServerPort;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String[] toTrimmedStringArray(List<String> theTo) {
|
|
||||||
List<String> to = new ArrayList<>();
|
List<String> to = new ArrayList<>();
|
||||||
for (String next : theTo) {
|
for (String next : theTo) {
|
||||||
if (isNotBlank(next)) {
|
if (isNotBlank(next)) {
|
||||||
to.add(next);
|
to.add(next);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return to.toArray(new String[to.size()]);
|
|
||||||
|
return StringUtils.join(to, ",");
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -54,32 +54,29 @@ public class SubscriptionDeliveringEmailSubscriber extends BaseSubscriptionDeliv
|
||||||
List<String> destinationAddresses = new ArrayList<>();
|
List<String> destinationAddresses = new ArrayList<>();
|
||||||
String[] destinationAddressStrings = StringUtils.split(endpointUrl, ",");
|
String[] destinationAddressStrings = StringUtils.split(endpointUrl, ",");
|
||||||
for (String next : destinationAddressStrings) {
|
for (String next : destinationAddressStrings) {
|
||||||
|
next = trim(defaultString(next));
|
||||||
|
if (next.startsWith("mailto:")) {
|
||||||
|
next = next.substring("mailto:".length());
|
||||||
|
}
|
||||||
if (isNotBlank(next)) {
|
if (isNotBlank(next)) {
|
||||||
destinationAddresses.add(trim(next));
|
destinationAddresses.add(next);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String from = defaultString(subscription.getEmailDetails().getFrom(), provideDefaultFrom());
|
String from = defaultString(subscription.getEmailDetails().getFrom(), mySubscriptionEmailInterceptor.getDefaultFromAddress());
|
||||||
String subjectTemplate = defaultString(subscription.getEmailDetails().getSubjectTemplate(), provideDefaultSubjectTemplate());
|
String subjectTemplate = defaultString(subscription.getEmailDetails().getSubjectTemplate(), provideDefaultSubjectTemplate());
|
||||||
String bodyTemplate = defaultString(subscription.getEmailDetails().getBodyTemplate(), provideDefaultBodyTemplate());
|
|
||||||
|
|
||||||
EmailDetails details = new EmailDetails();
|
EmailDetails details = new EmailDetails();
|
||||||
details.setTo(destinationAddresses);
|
details.setTo(destinationAddresses);
|
||||||
details.setFrom(from);
|
details.setFrom(from);
|
||||||
details.setBodyTemplate(bodyTemplate);
|
details.setBodyTemplate(subscription.getPayloadString());
|
||||||
details.setSubjectTemplate(subjectTemplate);
|
details.setSubjectTemplate(subjectTemplate);
|
||||||
|
details.setSubscription(subscription.getIdElement(getContext()));
|
||||||
|
|
||||||
IEmailSender emailSender = mySubscriptionEmailInterceptor.getEmailSender();
|
IEmailSender emailSender = mySubscriptionEmailInterceptor.getEmailSender();
|
||||||
emailSender.send(details);
|
emailSender.send(details);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String provideDefaultBodyTemplate() {
|
|
||||||
return "A subscription update has been received";
|
|
||||||
}
|
|
||||||
|
|
||||||
private String provideDefaultFrom() {
|
|
||||||
return "unknown@sender.com";
|
|
||||||
}
|
|
||||||
|
|
||||||
private String provideDefaultSubjectTemplate() {
|
private String provideDefaultSubjectTemplate() {
|
||||||
return "HAPI FHIR Subscriptions";
|
return "HAPI FHIR Subscriptions";
|
||||||
|
|
|
@ -20,28 +20,51 @@ package ca.uhn.fhir.jpa.subscription.email;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
|
||||||
import ca.uhn.fhir.jpa.subscription.BaseSubscriptionInterceptor;
|
import ca.uhn.fhir.jpa.subscription.BaseSubscriptionInterceptor;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.springframework.beans.factory.annotation.Required;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class SubscriptionEmailInterceptor extends BaseSubscriptionInterceptor {
|
public class SubscriptionEmailInterceptor extends BaseSubscriptionInterceptor {
|
||||||
private SubscriptionDeliveringEmailSubscriber mySubscriptionDeliverySubscriber;
|
private SubscriptionDeliveringEmailSubscriber mySubscriptionDeliverySubscriber;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is set to autowired=false just so that implementors can supply this
|
||||||
|
* with a mechanism other than autowiring if they want
|
||||||
|
*/
|
||||||
|
@Autowired(required = false)
|
||||||
private IEmailSender myEmailSender;
|
private IEmailSender myEmailSender;
|
||||||
|
private String myDefaultFromAddress = "noreply@unknown.com";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public org.hl7.fhir.r4.model.Subscription.SubscriptionChannelType getChannelType() {
|
public org.hl7.fhir.r4.model.Subscription.SubscriptionChannelType getChannelType() {
|
||||||
return org.hl7.fhir.r4.model.Subscription.SubscriptionChannelType.EMAIL;
|
return org.hl7.fhir.r4.model.Subscription.SubscriptionChannelType.EMAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The "from" address to use for any sent emails that to not explicitly specity a from address
|
||||||
|
*/
|
||||||
|
public String getDefaultFromAddress() {
|
||||||
|
return myDefaultFromAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The "from" address to use for any sent emails that to not explicitly specity a from address
|
||||||
|
*/
|
||||||
|
public void setDefaultFromAddress(String theDefaultFromAddress) {
|
||||||
|
Validate.notBlank(theDefaultFromAddress, "theDefaultFromAddress must not be null or blank");
|
||||||
|
myDefaultFromAddress = theDefaultFromAddress;
|
||||||
|
}
|
||||||
|
|
||||||
public IEmailSender getEmailSender() {
|
public IEmailSender getEmailSender() {
|
||||||
return myEmailSender;
|
return myEmailSender;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Required
|
/**
|
||||||
|
* Set the email sender (this method does not need to be explicitly called if you
|
||||||
|
* are using autowiring to supply the sender)
|
||||||
|
*/
|
||||||
public void setEmailSender(IEmailSender theEmailSender) {
|
public void setEmailSender(IEmailSender theEmailSender) {
|
||||||
myEmailSender = theEmailSender;
|
myEmailSender = theEmailSender;
|
||||||
}
|
}
|
||||||
|
@ -54,12 +77,12 @@ public class SubscriptionEmailInterceptor extends BaseSubscriptionInterceptor {
|
||||||
getDeliveryChannel().subscribe(mySubscriptionDeliverySubscriber);
|
getDeliveryChannel().subscribe(mySubscriptionDeliverySubscriber);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostConstruct
|
// @PostConstruct
|
||||||
public void start() {
|
// public void start() {
|
||||||
Validate.notNull(myEmailSender, "emailSender has not been configured");
|
// Validate.notNull(myEmailSender, "emailSender has not been configured");
|
||||||
|
//
|
||||||
super.start();
|
// super.start();
|
||||||
}
|
// }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void unregisterDeliverySubscriber() {
|
protected void unregisterDeliverySubscriber() {
|
||||||
|
|
|
@ -24,8 +24,20 @@ public class JpaConstants {
|
||||||
|
|
||||||
public static final String EXT_SP_UNIQUE = "http://hapifhir.io/fhir/StructureDefinition/sp-unique";
|
public static final String EXT_SP_UNIQUE = "http://hapifhir.io/fhir/StructureDefinition/sp-unique";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* This extension should be of type <code>string</code> and should be
|
||||||
|
* placed on the <code>Subscription.channel</code> element
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
public static final String EXT_SUBSCRIPTION_EMAIL_FROM = "http://hapifhir.io/fhir/StructureDefinition/subscription-email-from";
|
public static final String EXT_SUBSCRIPTION_EMAIL_FROM = "http://hapifhir.io/fhir/StructureDefinition/subscription-email-from";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* This extension should be of type <code>string</code> and should be
|
||||||
|
* placed on the <code>Subscription.channel</code> element
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
public static final String EXT_SUBSCRIPTION_SUBJECT_TEMPLATE = "http://hapifhir.io/fhir/StructureDefinition/subscription-email-subject-template";
|
public static final String EXT_SUBSCRIPTION_SUBJECT_TEMPLATE = "http://hapifhir.io/fhir/StructureDefinition/subscription-email-subject-template";
|
||||||
public static final String EXT_SUBSCRIPTION_BODY_TEMPLATE = "http://hapifhir.io/fhir/StructureDefinition/subscription-email-body-template";
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
public class SubscriptionsRequireManualActivationInterceptorDstu2 extends ServerOperationInterceptorAdapter {
|
public class SubscriptionsRequireManualActivationInterceptorDstu2 extends ServerOperationInterceptorAdapter {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
@Qualifier("mySubscriptionDaoR4")
|
@Qualifier("mySubscriptionDaoDstu2")
|
||||||
private IFhirResourceDao<Subscription> myDao;
|
private IFhirResourceDao<Subscription> myDao;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -130,4 +130,19 @@ public class TestUtil {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void sleepAtLeast(int theMillis) {
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
while (System.currentTimeMillis() <= start + theMillis) {
|
||||||
|
try {
|
||||||
|
long timeSinceStarted = System.currentTimeMillis() - start;
|
||||||
|
long timeToSleep = Math.max(0, theMillis - timeSinceStarted);
|
||||||
|
ourLog.info("Sleeping for {}ms", timeToSleep);
|
||||||
|
Thread.sleep(timeToSleep);
|
||||||
|
} catch (InterruptedException theE) {
|
||||||
|
theE.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,7 +97,7 @@ public class ConnectionWrapper implements Connection {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getClientInfo(String theName) throws SQLException {
|
public String getClientInfo(String theName) throws SQLException {
|
||||||
return getClientInfo(theName);
|
return myWrap.getClientInfo(theName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -10,19 +10,30 @@ import javax.sql.DataSource;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory;
|
import ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory;
|
||||||
import net.ttddyy.dsproxy.listener.logging.SLF4JLogLevel;
|
import net.ttddyy.dsproxy.listener.logging.SLF4JLogLevel;
|
||||||
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.email.IEmailSender;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.email.JavaMailEmailSender;
|
||||||
|
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
|
||||||
|
import ca.uhn.fhir.validation.ResultSeverityEnum;
|
||||||
|
import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder;
|
||||||
import org.apache.commons.dbcp2.BasicDataSource;
|
import org.apache.commons.dbcp2.BasicDataSource;
|
||||||
import org.hibernate.jpa.HibernatePersistenceProvider;
|
import org.hibernate.jpa.HibernatePersistenceProvider;
|
||||||
import org.springframework.context.annotation.*;
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.context.annotation.Primary;
|
||||||
import org.springframework.orm.jpa.JpaTransactionManager;
|
import org.springframework.orm.jpa.JpaTransactionManager;
|
||||||
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
import javax.persistence.EntityManagerFactory;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
|
import javax.sql.DataSource;
|
||||||
import ca.uhn.fhir.validation.ResultSeverityEnum;
|
import java.sql.Connection;
|
||||||
import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder;
|
import java.sql.SQLException;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableTransactionManagement()
|
@EnableTransactionManagement()
|
||||||
|
@ -31,11 +42,6 @@ public class TestDstu3Config extends BaseJavaConfigDstu3 {
|
||||||
static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TestDstu3Config.class);
|
static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TestDstu3Config.class);
|
||||||
private Exception myLastStackTrace;
|
private Exception myLastStackTrace;
|
||||||
|
|
||||||
@Bean()
|
|
||||||
public DaoConfig daoConfig() {
|
|
||||||
return new DaoConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean()
|
@Bean()
|
||||||
public BasicDataSource basicDataSource() {
|
public BasicDataSource basicDataSource() {
|
||||||
BasicDataSource retVal = new BasicDataSource() {
|
BasicDataSource retVal = new BasicDataSource() {
|
||||||
|
@ -50,7 +56,7 @@ public class TestDstu3Config extends BaseJavaConfigDstu3 {
|
||||||
ourLog.error("Exceeded maximum wait for connection", e);
|
ourLog.error("Exceeded maximum wait for connection", e);
|
||||||
logGetConnectionStackTrace();
|
logGetConnectionStackTrace();
|
||||||
// if ("true".equals(System.getProperty("ci"))) {
|
// if ("true".equals(System.getProperty("ci"))) {
|
||||||
fail("Exceeded maximum wait for connection: "+ e.toString());
|
fail("Exceeded maximum wait for connection: " + e.toString());
|
||||||
// }
|
// }
|
||||||
// System.exit(1);
|
// System.exit(1);
|
||||||
retVal = null;
|
retVal = null;
|
||||||
|
@ -100,20 +106,33 @@ public class TestDstu3Config extends BaseJavaConfigDstu3 {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean()
|
||||||
|
public DaoConfig daoConfig() {
|
||||||
|
return new DaoConfig();
|
||||||
|
}
|
||||||
|
|
||||||
@Bean()
|
@Bean()
|
||||||
@Primary()
|
@Primary()
|
||||||
public DataSource dataSource() {
|
public DataSource dataSource() {
|
||||||
|
|
||||||
DataSource dataSource = ProxyDataSourceBuilder
|
DataSource dataSource = ProxyDataSourceBuilder
|
||||||
.create(basicDataSource())
|
.create(basicDataSource())
|
||||||
// .logQueryBySlf4j(SLF4JLogLevel.INFO, "SQL")
|
// .logQueryBySlf4j(SLF4JLogLevel.INFO, "SQL")
|
||||||
.logSlowQueryBySlf4j(1000, TimeUnit.MILLISECONDS)
|
.logSlowQueryBySlf4j(1000, TimeUnit.MILLISECONDS)
|
||||||
.countQuery()
|
.countQuery()
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
return dataSource;
|
return dataSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public IEmailSender emailSender() {
|
||||||
|
JavaMailEmailSender retVal = new JavaMailEmailSender();
|
||||||
|
retVal.setSmtpServerHostname("localhost");
|
||||||
|
retVal.setSmtpServerPort(3025);
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
@Bean()
|
@Bean()
|
||||||
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
||||||
LocalContainerEntityManagerFactoryBean retVal = new LocalContainerEntityManagerFactoryBean();
|
LocalContainerEntityManagerFactoryBean retVal = new LocalContainerEntityManagerFactoryBean();
|
||||||
|
|
|
@ -118,8 +118,13 @@ public abstract class BaseJpaTest {
|
||||||
|
|
||||||
protected List<String> toUnqualifiedVersionlessIdValues(IBundleProvider theFound) {
|
protected List<String> toUnqualifiedVersionlessIdValues(IBundleProvider theFound) {
|
||||||
List<String> retVal = new ArrayList<String>();
|
List<String> retVal = new ArrayList<String>();
|
||||||
int size = theFound.size();
|
Integer size = theFound.size();
|
||||||
ourLog.info("Found {} results", size);
|
ourLog.info("Found {} results", size);
|
||||||
|
|
||||||
|
if (size == null) {
|
||||||
|
size = 99999;
|
||||||
|
}
|
||||||
|
|
||||||
List<IBaseResource> resources = theFound.getResources(0, size);
|
List<IBaseResource> resources = theFound.getResources(0, size);
|
||||||
for (IBaseResource next : resources) {
|
for (IBaseResource next : resources) {
|
||||||
retVal.add(next.getIdElement().toUnqualifiedVersionless().getValue());
|
retVal.add(next.getIdElement().toUnqualifiedVersionless().getValue());
|
||||||
|
@ -298,7 +303,7 @@ public abstract class BaseJpaTest {
|
||||||
|
|
||||||
public static void waitForSize(int theTarget, List<?> theList) {
|
public static void waitForSize(int theTarget, List<?> theList) {
|
||||||
StopWatch sw = new StopWatch();
|
StopWatch sw = new StopWatch();
|
||||||
while (theList.size() != theTarget && sw.getMillis() < 10000) {
|
while (theList.size() != theTarget && sw.getMillis() <= 15000) {
|
||||||
try {
|
try {
|
||||||
Thread.sleep(50);
|
Thread.sleep(50);
|
||||||
} catch (InterruptedException theE) {
|
} catch (InterruptedException theE) {
|
||||||
|
|
|
@ -120,7 +120,7 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
|
||||||
protected Object myResourceProviders;
|
protected Object myResourceProviders;
|
||||||
@Autowired
|
@Autowired
|
||||||
protected ISearchCoordinatorSvc mySearchCoordinatorSvc;
|
protected ISearchCoordinatorSvc mySearchCoordinatorSvc;
|
||||||
@Autowired
|
@Autowired(required = false)
|
||||||
protected IFulltextSearchSvc mySearchDao;
|
protected IFulltextSearchSvc mySearchDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
protected ISearchParamPresenceSvc mySearchParamPresenceSvc;
|
protected ISearchParamPresenceSvc mySearchParamPresenceSvc;
|
||||||
|
|
|
@ -56,6 +56,30 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
|
||||||
myDaoConfig.setTreatReferencesAsLogical(new DaoConfig().getTreatReferencesAsLogical());
|
myDaoConfig.setTreatReferencesAsLogical(new DaoConfig().getTreatReferencesAsLogical());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See #773
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testDeleteResourceWithOutboundDeletedResources() {
|
||||||
|
myDaoConfig.setEnforceReferentialIntegrityOnDelete(false);
|
||||||
|
|
||||||
|
Organization org = new Organization();
|
||||||
|
org.setId("ORG");
|
||||||
|
org.setName("ORG");
|
||||||
|
myOrganizationDao.update(org);
|
||||||
|
|
||||||
|
Patient pat = new Patient();
|
||||||
|
pat.setId("PAT");
|
||||||
|
pat.setActive(true);
|
||||||
|
pat.setManagingOrganization(new ResourceReferenceDt("Organization/ORG"));
|
||||||
|
myPatientDao.update(pat);
|
||||||
|
|
||||||
|
myOrganizationDao.delete(new IdDt("Organization/ORG"));
|
||||||
|
|
||||||
|
myPatientDao.delete(new IdDt("Patient/PAT"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private void assertGone(IIdType theId) {
|
private void assertGone(IIdType theId) {
|
||||||
try {
|
try {
|
||||||
assertNotGone(theId);
|
assertNotGone(theId);
|
||||||
|
|
|
@ -102,6 +102,7 @@ public class FhirResourceDaoDstu2ValidateTest extends BaseJpaDstu2Test {
|
||||||
} catch (PreconditionFailedException e) {
|
} catch (PreconditionFailedException e) {
|
||||||
return (OperationOutcome) e.getOperationOutcome();
|
return (OperationOutcome) e.getOperationOutcome();
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
case XML:
|
case XML:
|
||||||
encoded = myFhirCtx.newXmlParser().encodeResourceToString(input);
|
encoded = myFhirCtx.newXmlParser().encodeResourceToString(input);
|
||||||
try {
|
try {
|
||||||
|
@ -110,6 +111,7 @@ public class FhirResourceDaoDstu2ValidateTest extends BaseJpaDstu2Test {
|
||||||
} catch (PreconditionFailedException e) {
|
} catch (PreconditionFailedException e) {
|
||||||
return (OperationOutcome) e.getOperationOutcome();
|
return (OperationOutcome) e.getOperationOutcome();
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new IllegalStateException(); // shouldn't get here
|
throw new IllegalStateException(); // shouldn't get here
|
||||||
|
|
|
@ -172,7 +172,7 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
|
||||||
protected IResourceTagDao myResourceTagDao;
|
protected IResourceTagDao myResourceTagDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
protected ISearchCoordinatorSvc mySearchCoordinatorSvc;
|
protected ISearchCoordinatorSvc mySearchCoordinatorSvc;
|
||||||
@Autowired
|
@Autowired(required = false)
|
||||||
protected IFulltextSearchSvc mySearchDao;
|
protected IFulltextSearchSvc mySearchDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
protected ISearchDao mySearchEntityDao;
|
protected ISearchDao mySearchEntityDao;
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
package ca.uhn.fhir.jpa.dao.dstu3;
|
package ca.uhn.fhir.jpa.dao.dstu3;
|
||||||
|
|
||||||
|
import static ca.uhn.fhir.jpa.util.TestUtil.sleepAtLeast;
|
||||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.util.StopWatch;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.hl7.fhir.dstu3.model.Patient;
|
import org.hl7.fhir.dstu3.model.Patient;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.test.util.AopTestUtils;
|
import org.springframework.test.util.AopTestUtils;
|
||||||
import org.springframework.transaction.TransactionStatus;
|
import org.springframework.transaction.TransactionStatus;
|
||||||
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
|
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
|
||||||
|
@ -18,15 +22,13 @@ import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
import ca.uhn.fhir.rest.param.StringParam;
|
import ca.uhn.fhir.rest.param.StringParam;
|
||||||
|
|
||||||
public class FhirResourceDaoDstu3SearchPageExpiryTest extends BaseJpaDstu3Test {
|
public class FhirResourceDaoDstu3SearchPageExpiryTest extends BaseJpaDstu3Test {
|
||||||
@Before
|
private static final Logger ourLog = LoggerFactory.getLogger(FhirResourceDaoDstu3SearchPageExpiryTest.class);
|
||||||
public void beforeDisableResultReuse() {
|
|
||||||
myDaoConfig.setReuseCachedSearchResultsForMillis(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@After()
|
@After()
|
||||||
public void after() {
|
public void after() {
|
||||||
StaleSearchDeletingSvcImpl staleSearchDeletingSvc = AopTestUtils.getTargetObject(myStaleSearchDeletingSvc);
|
StaleSearchDeletingSvcImpl staleSearchDeletingSvc = AopTestUtils.getTargetObject(myStaleSearchDeletingSvc);
|
||||||
staleSearchDeletingSvc.setCutoffSlackForUnitTest(StaleSearchDeletingSvcImpl.DEFAULT_CUTOFF_SLACK);
|
staleSearchDeletingSvc.setCutoffSlackForUnitTest(StaleSearchDeletingSvcImpl.DEFAULT_CUTOFF_SLACK);
|
||||||
|
StaleSearchDeletingSvcImpl.setNowForUnitTests(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
|
@ -35,48 +37,9 @@ public class FhirResourceDaoDstu3SearchPageExpiryTest extends BaseJpaDstu3Test {
|
||||||
staleSearchDeletingSvc.setCutoffSlackForUnitTest(0);
|
staleSearchDeletingSvc.setCutoffSlackForUnitTest(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Before
|
||||||
public void testExpirePagesAfterSingleUse() throws Exception {
|
public void beforeDisableResultReuse() {
|
||||||
IIdType pid1;
|
myDaoConfig.setReuseCachedSearchResultsForMillis(null);
|
||||||
IIdType pid2;
|
|
||||||
{
|
|
||||||
Patient patient = new Patient();
|
|
||||||
patient.addName().setFamily("EXPIRE");
|
|
||||||
pid1 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
|
|
||||||
}
|
|
||||||
Thread.sleep(10);
|
|
||||||
{
|
|
||||||
Patient patient = new Patient();
|
|
||||||
patient.addName().setFamily("EXPIRE");
|
|
||||||
pid2 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
|
|
||||||
}
|
|
||||||
Thread.sleep(10);
|
|
||||||
|
|
||||||
SearchParameterMap params;
|
|
||||||
params = new SearchParameterMap();
|
|
||||||
params.add(Patient.SP_FAMILY, new StringParam("EXPIRE"));
|
|
||||||
final IBundleProvider bundleProvider = myPatientDao.search(params);
|
|
||||||
assertThat(toUnqualifiedVersionlessIds(bundleProvider), containsInAnyOrder(pid1, pid2));
|
|
||||||
assertThat(toUnqualifiedVersionlessIds(bundleProvider), containsInAnyOrder(pid1, pid2));
|
|
||||||
|
|
||||||
myDaoConfig.setExpireSearchResultsAfterMillis(500);
|
|
||||||
myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem();
|
|
||||||
TransactionTemplate txTemplate = new TransactionTemplate(myTxManager);
|
|
||||||
txTemplate.execute(new TransactionCallbackWithoutResult() {
|
|
||||||
@Override
|
|
||||||
protected void doInTransactionWithoutResult(TransactionStatus theArg0) {
|
|
||||||
assertNotNull(mySearchEntityDao.findByUuid(bundleProvider.getUuid()));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Thread.sleep(750);
|
|
||||||
myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem();
|
|
||||||
txTemplate.execute(new TransactionCallbackWithoutResult() {
|
|
||||||
@Override
|
|
||||||
protected void doInTransactionWithoutResult(TransactionStatus theArg0) {
|
|
||||||
assertNull(mySearchEntityDao.findByUuid(bundleProvider.getUuid()));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -98,6 +61,7 @@ public class FhirResourceDaoDstu3SearchPageExpiryTest extends BaseJpaDstu3Test {
|
||||||
|
|
||||||
myDaoConfig.setExpireSearchResultsAfterMillis(1000L);
|
myDaoConfig.setExpireSearchResultsAfterMillis(1000L);
|
||||||
myDaoConfig.setReuseCachedSearchResultsForMillis(500L);
|
myDaoConfig.setReuseCachedSearchResultsForMillis(500L);
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
|
||||||
final String searchUuid1;
|
final String searchUuid1;
|
||||||
{
|
{
|
||||||
|
@ -109,7 +73,7 @@ public class FhirResourceDaoDstu3SearchPageExpiryTest extends BaseJpaDstu3Test {
|
||||||
Validate.notBlank(searchUuid1);
|
Validate.notBlank(searchUuid1);
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread.sleep(250);
|
sleepAtLeast(250);
|
||||||
|
|
||||||
String searchUuid2;
|
String searchUuid2;
|
||||||
{
|
{
|
||||||
|
@ -122,7 +86,7 @@ public class FhirResourceDaoDstu3SearchPageExpiryTest extends BaseJpaDstu3Test {
|
||||||
}
|
}
|
||||||
assertEquals(searchUuid1, searchUuid2);
|
assertEquals(searchUuid1, searchUuid2);
|
||||||
|
|
||||||
Thread.sleep(500);
|
sleepAtLeast(500);
|
||||||
|
|
||||||
// We're now past 500ms so we shouldn't reuse the search
|
// We're now past 500ms so we shouldn't reuse the search
|
||||||
|
|
||||||
|
@ -139,18 +103,31 @@ public class FhirResourceDaoDstu3SearchPageExpiryTest extends BaseJpaDstu3Test {
|
||||||
|
|
||||||
// Search just got used so it shouldn't be deleted
|
// Search just got used so it shouldn't be deleted
|
||||||
|
|
||||||
Thread.sleep(750);
|
myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem();
|
||||||
|
newTxTemplate().execute(new TransactionCallbackWithoutResult() {
|
||||||
|
@Override
|
||||||
|
protected void doInTransactionWithoutResult(TransactionStatus theArg0) {
|
||||||
|
assertNotNull(mySearchEntityDao.findByUuid(searchUuid3));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
StaleSearchDeletingSvcImpl.setNowForUnitTests(start + 1400);
|
||||||
|
|
||||||
myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem();
|
myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem();
|
||||||
newTxTemplate().execute(new TransactionCallbackWithoutResult() {
|
newTxTemplate().execute(new TransactionCallbackWithoutResult() {
|
||||||
@Override
|
@Override
|
||||||
protected void doInTransactionWithoutResult(TransactionStatus theArg0) {
|
protected void doInTransactionWithoutResult(TransactionStatus theArg0) {
|
||||||
assertNull(mySearchEntityDao.findByUuid(searchUuid1));
|
|
||||||
assertNotNull(mySearchEntityDao.findByUuid(searchUuid3));
|
assertNotNull(mySearchEntityDao.findByUuid(searchUuid3));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
newTxTemplate().execute(new TransactionCallbackWithoutResult() {
|
||||||
|
@Override
|
||||||
|
protected void doInTransactionWithoutResult(TransactionStatus theArg0) {
|
||||||
|
assertNull(mySearchEntityDao.findByUuid(searchUuid1));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Thread.sleep(300);
|
StaleSearchDeletingSvcImpl.setNowForUnitTests(start + 2200);
|
||||||
|
|
||||||
myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem();
|
myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem();
|
||||||
newTxTemplate().execute(new TransactionCallbackWithoutResult() {
|
newTxTemplate().execute(new TransactionCallbackWithoutResult() {
|
||||||
|
@ -162,4 +139,63 @@ public class FhirResourceDaoDstu3SearchPageExpiryTest extends BaseJpaDstu3Test {
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExpirePagesAfterSingleUse() throws Exception {
|
||||||
|
IIdType pid1;
|
||||||
|
IIdType pid2;
|
||||||
|
{
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.addName().setFamily("EXPIRE");
|
||||||
|
pid1 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
|
||||||
|
}
|
||||||
|
Thread.sleep(10);
|
||||||
|
{
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.addName().setFamily("EXPIRE");
|
||||||
|
pid2 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
|
||||||
|
}
|
||||||
|
Thread.sleep(10);
|
||||||
|
|
||||||
|
final StopWatch sw = new StopWatch();
|
||||||
|
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
|
||||||
|
SearchParameterMap params;
|
||||||
|
params = new SearchParameterMap();
|
||||||
|
params.add(Patient.SP_FAMILY, new StringParam("EXPIRE"));
|
||||||
|
final IBundleProvider bundleProvider = myPatientDao.search(params);
|
||||||
|
assertThat(toUnqualifiedVersionlessIds(bundleProvider), containsInAnyOrder(pid1, pid2));
|
||||||
|
assertThat(toUnqualifiedVersionlessIds(bundleProvider), containsInAnyOrder(pid1, pid2));
|
||||||
|
|
||||||
|
myDaoConfig.setExpireSearchResultsAfterMillis(500);
|
||||||
|
StaleSearchDeletingSvcImpl.setNowForUnitTests(start);
|
||||||
|
|
||||||
|
myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem();
|
||||||
|
TransactionTemplate txTemplate = new TransactionTemplate(myTxManager);
|
||||||
|
txTemplate.execute(new TransactionCallbackWithoutResult() {
|
||||||
|
@Override
|
||||||
|
protected void doInTransactionWithoutResult(TransactionStatus theArg0) {
|
||||||
|
assertNotNull("Failed after " + sw.toString(), mySearchEntityDao.findByUuid(bundleProvider.getUuid()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
StaleSearchDeletingSvcImpl.setNowForUnitTests(start + 499);
|
||||||
|
myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem();
|
||||||
|
txTemplate.execute(new TransactionCallbackWithoutResult() {
|
||||||
|
@Override
|
||||||
|
protected void doInTransactionWithoutResult(TransactionStatus theArg0) {
|
||||||
|
assertNotNull(mySearchEntityDao.findByUuid(bundleProvider.getUuid()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
StaleSearchDeletingSvcImpl.setNowForUnitTests(start + 600);
|
||||||
|
myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem();
|
||||||
|
txTemplate.execute(new TransactionCallbackWithoutResult() {
|
||||||
|
@Override
|
||||||
|
protected void doInTransactionWithoutResult(TransactionStatus theArg0) {
|
||||||
|
assertNull(mySearchEntityDao.findByUuid(bundleProvider.getUuid()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,6 +133,7 @@ public class FhirResourceDaoDstu3ValidateTest extends BaseJpaDstu3Test {
|
||||||
} catch (PreconditionFailedException e) {
|
} catch (PreconditionFailedException e) {
|
||||||
return (OperationOutcome) e.getOperationOutcome();
|
return (OperationOutcome) e.getOperationOutcome();
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
case XML:
|
case XML:
|
||||||
encoded = myFhirCtx.newXmlParser().encodeResourceToString(input);
|
encoded = myFhirCtx.newXmlParser().encodeResourceToString(input);
|
||||||
try {
|
try {
|
||||||
|
@ -141,6 +142,7 @@ public class FhirResourceDaoDstu3ValidateTest extends BaseJpaDstu3Test {
|
||||||
} catch (PreconditionFailedException e) {
|
} catch (PreconditionFailedException e) {
|
||||||
return (OperationOutcome) e.getOperationOutcome();
|
return (OperationOutcome) e.getOperationOutcome();
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new IllegalStateException(); // shouldn't get here
|
throw new IllegalStateException(); // shouldn't get here
|
||||||
|
|
|
@ -1879,6 +1879,91 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
|
||||||
assertNull(nextEntry.getResource());
|
assertNull(nextEntry.getResource());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTransactionWithIfMatch() {
|
||||||
|
Patient p = new Patient();
|
||||||
|
p.setId("P1");
|
||||||
|
p.setActive(true);
|
||||||
|
myPatientDao.update(p);
|
||||||
|
|
||||||
|
p.setActive(false);
|
||||||
|
Bundle b = new Bundle();
|
||||||
|
b.setType(BundleType.TRANSACTION);
|
||||||
|
b.addEntry()
|
||||||
|
.setFullUrl("Patient/P1")
|
||||||
|
.setResource(p)
|
||||||
|
.getRequest()
|
||||||
|
.setMethod(HTTPVerb.PUT)
|
||||||
|
.setUrl("Patient/P1")
|
||||||
|
.setIfMatch("2");
|
||||||
|
|
||||||
|
try {
|
||||||
|
mySystemDao.transaction(mySrd, b);
|
||||||
|
} catch (ResourceVersionConflictException e) {
|
||||||
|
assertEquals("Trying to update Patient/P1/_history/2 but this is not the current version", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
b = new Bundle();
|
||||||
|
b.setType(BundleType.TRANSACTION);
|
||||||
|
b.addEntry()
|
||||||
|
.setFullUrl("Patient/P1")
|
||||||
|
.setResource(p)
|
||||||
|
.getRequest()
|
||||||
|
.setMethod(HTTPVerb.PUT)
|
||||||
|
.setUrl("Patient/P1")
|
||||||
|
.setIfMatch("1");
|
||||||
|
|
||||||
|
Bundle resp = mySystemDao.transaction(mySrd, b);
|
||||||
|
assertEquals("Patient/P1/_history/2", new IdType(resp.getEntry().get(0).getResponse().getLocation()).toUnqualified().getValue());
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTransactionWithIfNoneExist() {
|
||||||
|
Patient p = new Patient();
|
||||||
|
p.setId("P1");
|
||||||
|
p.setActive(true);
|
||||||
|
myPatientDao.update(p);
|
||||||
|
|
||||||
|
p = new Patient();
|
||||||
|
p.setActive(true);
|
||||||
|
p.addName().setFamily("AAA");
|
||||||
|
|
||||||
|
Bundle b = new Bundle();
|
||||||
|
b.setType(BundleType.TRANSACTION);
|
||||||
|
b.addEntry()
|
||||||
|
.setFullUrl("Patient")
|
||||||
|
.setResource(p)
|
||||||
|
.getRequest()
|
||||||
|
.setMethod(HTTPVerb.POST)
|
||||||
|
.setUrl("Patient/P1")
|
||||||
|
.setIfNoneExist("Patient?active=true");
|
||||||
|
|
||||||
|
Bundle resp = mySystemDao.transaction(mySrd, b);
|
||||||
|
assertEquals("Patient/P1/_history/1", new IdType(resp.getEntry().get(0).getResponse().getLocation()).toUnqualified().getValue());
|
||||||
|
|
||||||
|
p = new Patient();
|
||||||
|
p.setActive(true);
|
||||||
|
p.addName().setFamily("AAA");
|
||||||
|
|
||||||
|
b = new Bundle();
|
||||||
|
b.setType(BundleType.TRANSACTION);
|
||||||
|
b.addEntry()
|
||||||
|
.setFullUrl("Patient")
|
||||||
|
.setResource(p)
|
||||||
|
.getRequest()
|
||||||
|
.setMethod(HTTPVerb.POST)
|
||||||
|
.setUrl("Patient/P1")
|
||||||
|
.setIfNoneExist("Patient?active=false");
|
||||||
|
|
||||||
|
resp = mySystemDao.transaction(mySrd, b);
|
||||||
|
assertThat( new IdType(resp.getEntry().get(0).getResponse().getLocation()).toUnqualified().getValue(), matchesPattern("Patient/[0-9]+/_history/1"));
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTransactionSearchWithCount() {
|
public void testTransactionSearchWithCount() {
|
||||||
String methodName = "testTransactionSearchWithCount";
|
String methodName = "testTransactionSearchWithCount";
|
||||||
|
|
|
@ -181,7 +181,7 @@ public abstract class BaseJpaR4Test extends BaseJpaTest {
|
||||||
protected IResourceTagDao myResourceTagDao;
|
protected IResourceTagDao myResourceTagDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
protected ISearchCoordinatorSvc mySearchCoordinatorSvc;
|
protected ISearchCoordinatorSvc mySearchCoordinatorSvc;
|
||||||
@Autowired
|
@Autowired(required = false)
|
||||||
protected IFulltextSearchSvc mySearchDao;
|
protected IFulltextSearchSvc mySearchDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
protected ISearchDao mySearchEntityDao;
|
protected ISearchDao mySearchEntityDao;
|
||||||
|
|
|
@ -1,33 +1,35 @@
|
||||||
package ca.uhn.fhir.jpa.dao.r4;
|
package ca.uhn.fhir.jpa.dao.r4;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
|
||||||
import static org.junit.Assert.*;
|
import ca.uhn.fhir.jpa.search.StaleSearchDeletingSvcImpl;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.util.StopWatch;
|
import ca.uhn.fhir.jpa.util.StopWatch;
|
||||||
|
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
|
import ca.uhn.fhir.rest.param.StringParam;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.hl7.fhir.r4.model.Patient;
|
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.junit.*;
|
import org.hl7.fhir.r4.model.Patient;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.test.util.AopTestUtils;
|
import org.springframework.test.util.AopTestUtils;
|
||||||
import org.springframework.transaction.TransactionStatus;
|
import org.springframework.transaction.TransactionStatus;
|
||||||
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
|
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
|
||||||
import org.springframework.transaction.support.TransactionTemplate;
|
import org.springframework.transaction.support.TransactionTemplate;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
|
import static ca.uhn.fhir.jpa.util.TestUtil.sleepAtLeast;
|
||||||
import ca.uhn.fhir.jpa.search.StaleSearchDeletingSvcImpl;
|
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
import static org.junit.Assert.*;
|
||||||
import ca.uhn.fhir.rest.param.StringParam;
|
|
||||||
|
|
||||||
public class FhirResourceDaoR4SearchPageExpiryTest extends BaseJpaR4Test {
|
public class FhirResourceDaoR4SearchPageExpiryTest extends BaseJpaR4Test {
|
||||||
@Before
|
private static final Logger ourLog = LoggerFactory.getLogger(FhirResourceDaoR4SearchPageExpiryTest.class);
|
||||||
public void beforeDisableResultReuse() {
|
|
||||||
myDaoConfig.setReuseCachedSearchResultsForMillis(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@After()
|
@After()
|
||||||
public void after() {
|
public void after() {
|
||||||
StaleSearchDeletingSvcImpl staleSearchDeletingSvc = AopTestUtils.getTargetObject(myStaleSearchDeletingSvc);
|
StaleSearchDeletingSvcImpl staleSearchDeletingSvc = AopTestUtils.getTargetObject(myStaleSearchDeletingSvc);
|
||||||
staleSearchDeletingSvc.setCutoffSlackForUnitTest(StaleSearchDeletingSvcImpl.DEFAULT_CUTOFF_SLACK);
|
staleSearchDeletingSvc.setCutoffSlackForUnitTest(StaleSearchDeletingSvcImpl.DEFAULT_CUTOFF_SLACK);
|
||||||
|
StaleSearchDeletingSvcImpl.setNowForUnitTests(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
|
@ -36,50 +38,9 @@ public class FhirResourceDaoR4SearchPageExpiryTest extends BaseJpaR4Test {
|
||||||
staleSearchDeletingSvc.setCutoffSlackForUnitTest(0);
|
staleSearchDeletingSvc.setCutoffSlackForUnitTest(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Before
|
||||||
public void testExpirePagesAfterSingleUse() throws Exception {
|
public void beforeDisableResultReuse() {
|
||||||
IIdType pid1;
|
myDaoConfig.setReuseCachedSearchResultsForMillis(null);
|
||||||
IIdType pid2;
|
|
||||||
{
|
|
||||||
Patient patient = new Patient();
|
|
||||||
patient.addName().setFamily("EXPIRE");
|
|
||||||
pid1 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
|
|
||||||
}
|
|
||||||
Thread.sleep(10);
|
|
||||||
{
|
|
||||||
Patient patient = new Patient();
|
|
||||||
patient.addName().setFamily("EXPIRE");
|
|
||||||
pid2 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
|
|
||||||
}
|
|
||||||
Thread.sleep(10);
|
|
||||||
|
|
||||||
final StopWatch sw = new StopWatch();
|
|
||||||
|
|
||||||
SearchParameterMap params;
|
|
||||||
params = new SearchParameterMap();
|
|
||||||
params.add(Patient.SP_FAMILY, new StringParam("EXPIRE"));
|
|
||||||
final IBundleProvider bundleProvider = myPatientDao.search(params);
|
|
||||||
assertThat(toUnqualifiedVersionlessIds(bundleProvider), containsInAnyOrder(pid1, pid2));
|
|
||||||
assertThat(toUnqualifiedVersionlessIds(bundleProvider), containsInAnyOrder(pid1, pid2));
|
|
||||||
|
|
||||||
myDaoConfig.setExpireSearchResultsAfterMillis(500);
|
|
||||||
myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem();
|
|
||||||
TransactionTemplate txTemplate = new TransactionTemplate(myTxManager);
|
|
||||||
txTemplate.execute(new TransactionCallbackWithoutResult() {
|
|
||||||
@Override
|
|
||||||
protected void doInTransactionWithoutResult(TransactionStatus theArg0) {
|
|
||||||
assertNotNull("Failed after " + sw.toString(), mySearchEntityDao.findByUuid(bundleProvider.getUuid()));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Thread.sleep(750);
|
|
||||||
myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem();
|
|
||||||
txTemplate.execute(new TransactionCallbackWithoutResult() {
|
|
||||||
@Override
|
|
||||||
protected void doInTransactionWithoutResult(TransactionStatus theArg0) {
|
|
||||||
assertNull(mySearchEntityDao.findByUuid(bundleProvider.getUuid()));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -101,6 +62,7 @@ public class FhirResourceDaoR4SearchPageExpiryTest extends BaseJpaR4Test {
|
||||||
|
|
||||||
myDaoConfig.setExpireSearchResultsAfterMillis(1000L);
|
myDaoConfig.setExpireSearchResultsAfterMillis(1000L);
|
||||||
myDaoConfig.setReuseCachedSearchResultsForMillis(500L);
|
myDaoConfig.setReuseCachedSearchResultsForMillis(500L);
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
|
||||||
final String searchUuid1;
|
final String searchUuid1;
|
||||||
{
|
{
|
||||||
|
@ -112,7 +74,7 @@ public class FhirResourceDaoR4SearchPageExpiryTest extends BaseJpaR4Test {
|
||||||
Validate.notBlank(searchUuid1);
|
Validate.notBlank(searchUuid1);
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread.sleep(250);
|
sleepAtLeast(250);
|
||||||
|
|
||||||
String searchUuid2;
|
String searchUuid2;
|
||||||
{
|
{
|
||||||
|
@ -125,7 +87,7 @@ public class FhirResourceDaoR4SearchPageExpiryTest extends BaseJpaR4Test {
|
||||||
}
|
}
|
||||||
assertEquals(searchUuid1, searchUuid2);
|
assertEquals(searchUuid1, searchUuid2);
|
||||||
|
|
||||||
Thread.sleep(500);
|
sleepAtLeast(500);
|
||||||
|
|
||||||
// We're now past 500ms so we shouldn't reuse the search
|
// We're now past 500ms so we shouldn't reuse the search
|
||||||
|
|
||||||
|
@ -150,7 +112,7 @@ public class FhirResourceDaoR4SearchPageExpiryTest extends BaseJpaR4Test {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Thread.sleep(750);
|
StaleSearchDeletingSvcImpl.setNowForUnitTests(start + 1400);
|
||||||
|
|
||||||
myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem();
|
myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem();
|
||||||
newTxTemplate().execute(new TransactionCallbackWithoutResult() {
|
newTxTemplate().execute(new TransactionCallbackWithoutResult() {
|
||||||
|
@ -166,7 +128,7 @@ public class FhirResourceDaoR4SearchPageExpiryTest extends BaseJpaR4Test {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Thread.sleep(300);
|
StaleSearchDeletingSvcImpl.setNowForUnitTests(start + 2200);
|
||||||
|
|
||||||
myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem();
|
myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem();
|
||||||
newTxTemplate().execute(new TransactionCallbackWithoutResult() {
|
newTxTemplate().execute(new TransactionCallbackWithoutResult() {
|
||||||
|
@ -178,4 +140,63 @@ public class FhirResourceDaoR4SearchPageExpiryTest extends BaseJpaR4Test {
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExpirePagesAfterSingleUse() throws Exception {
|
||||||
|
IIdType pid1;
|
||||||
|
IIdType pid2;
|
||||||
|
{
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.addName().setFamily("EXPIRE");
|
||||||
|
pid1 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
|
||||||
|
}
|
||||||
|
Thread.sleep(10);
|
||||||
|
{
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.addName().setFamily("EXPIRE");
|
||||||
|
pid2 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
|
||||||
|
}
|
||||||
|
Thread.sleep(10);
|
||||||
|
|
||||||
|
final StopWatch sw = new StopWatch();
|
||||||
|
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
|
||||||
|
SearchParameterMap params;
|
||||||
|
params = new SearchParameterMap();
|
||||||
|
params.add(Patient.SP_FAMILY, new StringParam("EXPIRE"));
|
||||||
|
final IBundleProvider bundleProvider = myPatientDao.search(params);
|
||||||
|
assertThat(toUnqualifiedVersionlessIds(bundleProvider), containsInAnyOrder(pid1, pid2));
|
||||||
|
assertThat(toUnqualifiedVersionlessIds(bundleProvider), containsInAnyOrder(pid1, pid2));
|
||||||
|
|
||||||
|
myDaoConfig.setExpireSearchResultsAfterMillis(500);
|
||||||
|
StaleSearchDeletingSvcImpl.setNowForUnitTests(start);
|
||||||
|
|
||||||
|
myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem();
|
||||||
|
TransactionTemplate txTemplate = new TransactionTemplate(myTxManager);
|
||||||
|
txTemplate.execute(new TransactionCallbackWithoutResult() {
|
||||||
|
@Override
|
||||||
|
protected void doInTransactionWithoutResult(TransactionStatus theArg0) {
|
||||||
|
assertNotNull("Failed after " + sw.toString(), mySearchEntityDao.findByUuid(bundleProvider.getUuid()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
StaleSearchDeletingSvcImpl.setNowForUnitTests(start + 499);
|
||||||
|
myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem();
|
||||||
|
txTemplate.execute(new TransactionCallbackWithoutResult() {
|
||||||
|
@Override
|
||||||
|
protected void doInTransactionWithoutResult(TransactionStatus theArg0) {
|
||||||
|
assertNotNull(mySearchEntityDao.findByUuid(bundleProvider.getUuid()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
StaleSearchDeletingSvcImpl.setNowForUnitTests(start + 600);
|
||||||
|
myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem();
|
||||||
|
txTemplate.execute(new TransactionCallbackWithoutResult() {
|
||||||
|
@Override
|
||||||
|
protected void doInTransactionWithoutResult(TransactionStatus theArg0) {
|
||||||
|
assertNull(mySearchEntityDao.findByUuid(bundleProvider.getUuid()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,7 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
|
||||||
public final void after() {
|
public final void after() {
|
||||||
myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences());
|
myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences());
|
||||||
myDaoConfig.setTreatReferencesAsLogical(new DaoConfig().getTreatReferencesAsLogical());
|
myDaoConfig.setTreatReferencesAsLogical(new DaoConfig().getTreatReferencesAsLogical());
|
||||||
|
myDaoConfig.setEnforceReferentialIntegrityOnDelete(new DaoConfig().isEnforceReferentialIntegrityOnDelete());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertGone(IIdType theId) {
|
private void assertGone(IIdType theId) {
|
||||||
|
@ -98,6 +99,29 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See #773
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testDeleteResourceWithOutboundDeletedResources() {
|
||||||
|
myDaoConfig.setEnforceReferentialIntegrityOnDelete(false);
|
||||||
|
|
||||||
|
Organization org = new Organization();
|
||||||
|
org.setId("ORG");
|
||||||
|
org.setName("ORG");
|
||||||
|
myOrganizationDao.update(org);
|
||||||
|
|
||||||
|
Patient pat = new Patient();
|
||||||
|
pat.setId("PAT");
|
||||||
|
pat.setActive(true);
|
||||||
|
pat.setManagingOrganization(new Reference("Organization/ORG"));
|
||||||
|
myPatientDao.update(pat);
|
||||||
|
|
||||||
|
myOrganizationDao.delete(new IdType("Organization/ORG"));
|
||||||
|
|
||||||
|
myPatientDao.delete(new IdType("Patient/PAT"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void beforeDisableResultReuse() {
|
public void beforeDisableResultReuse() {
|
||||||
|
|
|
@ -133,6 +133,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
||||||
} catch (PreconditionFailedException e) {
|
} catch (PreconditionFailedException e) {
|
||||||
return (OperationOutcome) e.getOperationOutcome();
|
return (OperationOutcome) e.getOperationOutcome();
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
case XML:
|
case XML:
|
||||||
encoded = myFhirCtx.newXmlParser().encodeResourceToString(input);
|
encoded = myFhirCtx.newXmlParser().encodeResourceToString(input);
|
||||||
try {
|
try {
|
||||||
|
@ -141,6 +142,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
||||||
} catch (PreconditionFailedException e) {
|
} catch (PreconditionFailedException e) {
|
||||||
return (OperationOutcome) e.getOperationOutcome();
|
return (OperationOutcome) e.getOperationOutcome();
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new IllegalStateException(); // shouldn't get here
|
throw new IllegalStateException(); // shouldn't get here
|
||||||
|
|
|
@ -587,6 +587,90 @@ public class FhirSystemDaoR4Test extends BaseJpaR4SystemTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTransactionWithIfMatch() {
|
||||||
|
Patient p = new Patient();
|
||||||
|
p.setId("P1");
|
||||||
|
p.setActive(true);
|
||||||
|
myPatientDao.update(p);
|
||||||
|
|
||||||
|
p.setActive(false);
|
||||||
|
Bundle b = new Bundle();
|
||||||
|
b.setType(BundleType.TRANSACTION);
|
||||||
|
b.addEntry()
|
||||||
|
.setFullUrl("Patient/P1")
|
||||||
|
.setResource(p)
|
||||||
|
.getRequest()
|
||||||
|
.setMethod(HTTPVerb.PUT)
|
||||||
|
.setUrl("Patient/P1")
|
||||||
|
.setIfMatch("2");
|
||||||
|
|
||||||
|
try {
|
||||||
|
mySystemDao.transaction(mySrd, b);
|
||||||
|
} catch (ResourceVersionConflictException e) {
|
||||||
|
assertEquals("Trying to update Patient/P1/_history/2 but this is not the current version", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
b = new Bundle();
|
||||||
|
b.setType(BundleType.TRANSACTION);
|
||||||
|
b.addEntry()
|
||||||
|
.setFullUrl("Patient/P1")
|
||||||
|
.setResource(p)
|
||||||
|
.getRequest()
|
||||||
|
.setMethod(HTTPVerb.PUT)
|
||||||
|
.setUrl("Patient/P1")
|
||||||
|
.setIfMatch("1");
|
||||||
|
|
||||||
|
Bundle resp = mySystemDao.transaction(mySrd, b);
|
||||||
|
assertEquals("Patient/P1/_history/2", new IdType(resp.getEntry().get(0).getResponse().getLocation()).toUnqualified().getValue());
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTransactionWithIfNoneExist() {
|
||||||
|
Patient p = new Patient();
|
||||||
|
p.setId("P1");
|
||||||
|
p.setActive(true);
|
||||||
|
myPatientDao.update(p);
|
||||||
|
|
||||||
|
p = new Patient();
|
||||||
|
p.setActive(true);
|
||||||
|
p.addName().setFamily("AAA");
|
||||||
|
|
||||||
|
Bundle b = new Bundle();
|
||||||
|
b.setType(BundleType.TRANSACTION);
|
||||||
|
b.addEntry()
|
||||||
|
.setFullUrl("Patient")
|
||||||
|
.setResource(p)
|
||||||
|
.getRequest()
|
||||||
|
.setMethod(HTTPVerb.POST)
|
||||||
|
.setUrl("Patient/P1")
|
||||||
|
.setIfNoneExist("Patient?active=true");
|
||||||
|
|
||||||
|
Bundle resp = mySystemDao.transaction(mySrd, b);
|
||||||
|
assertEquals("Patient/P1/_history/1", new IdType(resp.getEntry().get(0).getResponse().getLocation()).toUnqualified().getValue());
|
||||||
|
|
||||||
|
p = new Patient();
|
||||||
|
p.setActive(true);
|
||||||
|
p.addName().setFamily("AAA");
|
||||||
|
|
||||||
|
b = new Bundle();
|
||||||
|
b.setType(BundleType.TRANSACTION);
|
||||||
|
b.addEntry()
|
||||||
|
.setFullUrl("Patient")
|
||||||
|
.setResource(p)
|
||||||
|
.getRequest()
|
||||||
|
.setMethod(HTTPVerb.POST)
|
||||||
|
.setUrl("Patient/P1")
|
||||||
|
.setIfNoneExist("Patient?active=false");
|
||||||
|
|
||||||
|
resp = mySystemDao.transaction(mySrd, b);
|
||||||
|
assertThat( new IdType(resp.getEntry().get(0).getResponse().getLocation()).toUnqualified().getValue(), matchesPattern("Patient/[0-9]+/_history/1"));
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTransaction1() throws IOException {
|
public void testTransaction1() throws IOException {
|
||||||
String inputBundleString = loadClasspath("/david-bundle-error.json");
|
String inputBundleString = loadClasspath("/david-bundle-error.json");
|
||||||
|
|
|
@ -6,6 +6,7 @@ import ca.uhn.fhir.jpa.dao.dstu3.BaseJpaDstu3Test;
|
||||||
import ca.uhn.fhir.jpa.dao.dstu3.SearchParamRegistryDstu3;
|
import ca.uhn.fhir.jpa.dao.dstu3.SearchParamRegistryDstu3;
|
||||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||||
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
|
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.email.SubscriptionEmailInterceptor;
|
||||||
import ca.uhn.fhir.jpa.subscription.resthook.SubscriptionRestHookInterceptor;
|
import ca.uhn.fhir.jpa.subscription.resthook.SubscriptionRestHookInterceptor;
|
||||||
import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainDstu3;
|
import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainDstu3;
|
||||||
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||||
|
@ -58,6 +59,7 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test {
|
||||||
protected static SearchParamRegistryDstu3 ourSearchParamRegistry;
|
protected static SearchParamRegistryDstu3 ourSearchParamRegistry;
|
||||||
protected static DatabaseBackedPagingProvider ourPagingProvider;
|
protected static DatabaseBackedPagingProvider ourPagingProvider;
|
||||||
protected static SubscriptionRestHookInterceptor ourRestHookSubscriptionInterceptor;
|
protected static SubscriptionRestHookInterceptor ourRestHookSubscriptionInterceptor;
|
||||||
|
protected static SubscriptionEmailInterceptor ourEmailSubscriptionInterceptor;
|
||||||
protected static ISearchDao mySearchEntityDao;
|
protected static ISearchDao mySearchEntityDao;
|
||||||
protected static ISearchCoordinatorSvc mySearchCoordinatorSvc;
|
protected static ISearchCoordinatorSvc mySearchCoordinatorSvc;
|
||||||
|
|
||||||
|
@ -111,13 +113,9 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test {
|
||||||
ourWebApplicationContext = new GenericWebApplicationContext();
|
ourWebApplicationContext = new GenericWebApplicationContext();
|
||||||
ourWebApplicationContext.setParent(myAppCtx);
|
ourWebApplicationContext.setParent(myAppCtx);
|
||||||
ourWebApplicationContext.refresh();
|
ourWebApplicationContext.refresh();
|
||||||
// ContextLoaderListener loaderListener = new ContextLoaderListener(webApplicationContext);
|
|
||||||
// loaderListener.initWebApplicationContext(mock(ServletContext.class));
|
|
||||||
//
|
|
||||||
proxyHandler.getServletContext().setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ourWebApplicationContext);
|
proxyHandler.getServletContext().setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ourWebApplicationContext);
|
||||||
|
|
||||||
DispatcherServlet dispatcherServlet = new DispatcherServlet();
|
DispatcherServlet dispatcherServlet = new DispatcherServlet();
|
||||||
// dispatcherServlet.setApplicationContext(webApplicationContext);
|
|
||||||
dispatcherServlet.setContextClass(AnnotationConfigWebApplicationContext.class);
|
dispatcherServlet.setContextClass(AnnotationConfigWebApplicationContext.class);
|
||||||
ServletHolder subsServletHolder = new ServletHolder();
|
ServletHolder subsServletHolder = new ServletHolder();
|
||||||
subsServletHolder.setServlet(dispatcherServlet);
|
subsServletHolder.setServlet(dispatcherServlet);
|
||||||
|
@ -150,6 +148,7 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test {
|
||||||
mySearchCoordinatorSvc = wac.getBean(ISearchCoordinatorSvc.class);
|
mySearchCoordinatorSvc = wac.getBean(ISearchCoordinatorSvc.class);
|
||||||
mySearchEntityDao = wac.getBean(ISearchDao.class);
|
mySearchEntityDao = wac.getBean(ISearchDao.class);
|
||||||
ourRestHookSubscriptionInterceptor = wac.getBean(SubscriptionRestHookInterceptor.class);
|
ourRestHookSubscriptionInterceptor = wac.getBean(SubscriptionRestHookInterceptor.class);
|
||||||
|
ourEmailSubscriptionInterceptor = wac.getBean(SubscriptionEmailInterceptor.class);
|
||||||
ourSearchParamRegistry = wac.getBean(SearchParamRegistryDstu3.class);
|
ourSearchParamRegistry = wac.getBean(SearchParamRegistryDstu3.class);
|
||||||
|
|
||||||
myFhirCtx.getRestfulClientFactory().setSocketTimeout(5000000);
|
myFhirCtx.getRestfulClientFactory().setSocketTimeout(5000000);
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package ca.uhn.fhir.jpa.provider.dstu3;
|
package ca.uhn.fhir.jpa.provider.dstu3;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.provider.r4.ResourceProviderInterceptorR4Test;
|
import ca.uhn.fhir.jpa.provider.r4.ResourceProviderInterceptorR4Test;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Conformance;
|
|
||||||
import ca.uhn.fhir.parser.IParser;
|
import ca.uhn.fhir.parser.IParser;
|
||||||
import ca.uhn.fhir.rest.api.Constants;
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||||
|
@ -61,8 +60,8 @@ public class ResourceProviderInterceptorDstu3Test extends BaseResourceProviderDs
|
||||||
public void before() throws Exception {
|
public void before() throws Exception {
|
||||||
super.before();
|
super.before();
|
||||||
|
|
||||||
myServerInterceptor = mock(IServerInterceptor.class);
|
myServerInterceptor = mock(IServerInterceptor.class, withSettings().verboseLogging());
|
||||||
myDaoInterceptor = mock(IServerInterceptor.class);
|
myDaoInterceptor = mock(IServerInterceptor.class, withSettings().verboseLogging());
|
||||||
|
|
||||||
resetServerInterceptor();
|
resetServerInterceptor();
|
||||||
|
|
||||||
|
@ -125,6 +124,8 @@ public class ResourceProviderInterceptorDstu3Test extends BaseResourceProviderDs
|
||||||
public void testCreateResourceInTransaction() throws IOException, ServletException {
|
public void testCreateResourceInTransaction() throws IOException, ServletException {
|
||||||
String methodName = "testCreateResourceInTransaction";
|
String methodName = "testCreateResourceInTransaction";
|
||||||
|
|
||||||
|
ourLog.info("** Starting {}", methodName);
|
||||||
|
|
||||||
Patient pt = new Patient();
|
Patient pt = new Patient();
|
||||||
pt.addName().setFamily(methodName);
|
pt.addName().setFamily(methodName);
|
||||||
|
|
||||||
|
@ -140,6 +141,11 @@ public class ResourceProviderInterceptorDstu3Test extends BaseResourceProviderDs
|
||||||
|
|
||||||
resetServerInterceptor();
|
resetServerInterceptor();
|
||||||
|
|
||||||
|
ArgumentCaptor<ActionRequestDetails> ardCaptor = ArgumentCaptor.forClass(ActionRequestDetails.class);
|
||||||
|
ArgumentCaptor<RestOperationTypeEnum> opTypeCaptor = ArgumentCaptor.forClass(RestOperationTypeEnum.class);
|
||||||
|
verify(myDaoInterceptor, times(0)).incomingRequestPreHandled(opTypeCaptor.capture(), ardCaptor.capture());
|
||||||
|
verify(myServerInterceptor, times(0)).incomingRequestPreHandled(opTypeCaptor.capture(), ardCaptor.capture());
|
||||||
|
|
||||||
HttpPost post = new HttpPost(ourServerBase + "/");
|
HttpPost post = new HttpPost(ourServerBase + "/");
|
||||||
post.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
post.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||||
CloseableHttpResponse response = ourHttpClient.execute(post);
|
CloseableHttpResponse response = ourHttpClient.execute(post);
|
||||||
|
@ -153,8 +159,8 @@ public class ResourceProviderInterceptorDstu3Test extends BaseResourceProviderDs
|
||||||
* Server Interceptor
|
* Server Interceptor
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ArgumentCaptor<ActionRequestDetails> ardCaptor = ArgumentCaptor.forClass(ActionRequestDetails.class);
|
ardCaptor = ArgumentCaptor.forClass(ActionRequestDetails.class);
|
||||||
ArgumentCaptor<RestOperationTypeEnum> opTypeCaptor = ArgumentCaptor.forClass(RestOperationTypeEnum.class);
|
opTypeCaptor = ArgumentCaptor.forClass(RestOperationTypeEnum.class);
|
||||||
verify(myServerInterceptor, times(2)).incomingRequestPreHandled(opTypeCaptor.capture(), ardCaptor.capture());
|
verify(myServerInterceptor, times(2)).incomingRequestPreHandled(opTypeCaptor.capture(), ardCaptor.capture());
|
||||||
assertEquals(RestOperationTypeEnum.TRANSACTION, opTypeCaptor.getAllValues().get(0));
|
assertEquals(RestOperationTypeEnum.TRANSACTION, opTypeCaptor.getAllValues().get(0));
|
||||||
assertEquals(null, ardCaptor.getAllValues().get(0).getResourceType());
|
assertEquals(null, ardCaptor.getAllValues().get(0).getResourceType());
|
||||||
|
|
|
@ -9,6 +9,8 @@ import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
|
||||||
import org.apache.http.client.methods.*;
|
import org.apache.http.client.methods.*;
|
||||||
import org.apache.http.entity.ContentType;
|
import org.apache.http.entity.ContentType;
|
||||||
import org.apache.http.entity.StringEntity;
|
import org.apache.http.entity.StringEntity;
|
||||||
|
@ -69,19 +71,68 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource
|
||||||
Patient patient = new Patient();
|
Patient patient = new Patient();
|
||||||
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
|
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
|
||||||
patient.addName().setFamily("Tester").addGiven("Raghad");
|
patient.addName().setFamily("Tester").addGiven("Raghad");
|
||||||
IIdType id = ourClient.create().resource(patient).execute().getId();
|
IIdType id = myClient.create().resource(patient).execute().getId();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ourClient.delete().resourceById(id.toUnqualifiedVersionless()).execute();
|
myClient.delete().resourceById(id.toUnqualifiedVersionless()).execute();
|
||||||
fail();
|
fail();
|
||||||
} catch (ForbiddenOperationException e) {
|
} catch (ForbiddenOperationException e) {
|
||||||
// good
|
// good
|
||||||
}
|
}
|
||||||
|
|
||||||
patient = ourClient.read().resource(Patient.class).withId(id.toUnqualifiedVersionless()).execute();
|
patient = myClient.read().resource(Patient.class).withId(id.toUnqualifiedVersionless()).execute();
|
||||||
assertEquals(id.getValue(), patient.getId());
|
assertEquals(id.getValue(), patient.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See #751
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testDeleteInCompartmentIsBlocked() {
|
||||||
|
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.setId("Patient/A");
|
||||||
|
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
|
||||||
|
patient.addName().setFamily("Tester").addGiven("Raghad");
|
||||||
|
IIdType id = myClient.update().resource(patient).execute().getId();
|
||||||
|
|
||||||
|
Observation obs = new Observation();
|
||||||
|
obs.setId("Observation/B");
|
||||||
|
obs.getSubject().setReference("Patient/A");
|
||||||
|
myClient.update().resource(obs).execute();
|
||||||
|
|
||||||
|
obs = new Observation();
|
||||||
|
obs.setId("Observation/C");
|
||||||
|
obs.setStatus(ObservationStatus.FINAL);
|
||||||
|
myClient.update().resource(obs).execute();
|
||||||
|
|
||||||
|
ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
|
||||||
|
@Override
|
||||||
|
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
|
||||||
|
return new RuleBuilder()
|
||||||
|
.allow().delete().allResources().inCompartment("Patient", new IdType("Patient/A")).andThen()
|
||||||
|
.allow().read().allResources().withAnyId().andThen()
|
||||||
|
.denyAll()
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
myClient.delete().resourceById(new IdType("Observation/B")).execute();
|
||||||
|
|
||||||
|
try {
|
||||||
|
myClient.read().resource(Observation.class).withId("Observation/B").execute();
|
||||||
|
fail();
|
||||||
|
} catch (ResourceGoneException e) {
|
||||||
|
// good
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
myClient.delete().resourceById(new IdType("Observation/C")).execute();
|
||||||
|
fail();
|
||||||
|
} catch (ForbiddenOperationException e) {
|
||||||
|
// good
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See #503
|
* See #503
|
||||||
|
@ -92,16 +143,16 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource
|
||||||
Patient patient = new Patient();
|
Patient patient = new Patient();
|
||||||
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
|
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
|
||||||
patient.addName().setFamily("Tester").addGiven("Raghad");
|
patient.addName().setFamily("Tester").addGiven("Raghad");
|
||||||
final IIdType id = ourClient.create().resource(patient).execute().getId();
|
final IIdType id = myClient.create().resource(patient).execute().getId();
|
||||||
|
|
||||||
Observation obsInCompartment = new Observation();
|
Observation obsInCompartment = new Observation();
|
||||||
obsInCompartment.setStatus(ObservationStatus.FINAL);
|
obsInCompartment.setStatus(ObservationStatus.FINAL);
|
||||||
obsInCompartment.getSubject().setReferenceElement(id.toUnqualifiedVersionless());
|
obsInCompartment.getSubject().setReferenceElement(id.toUnqualifiedVersionless());
|
||||||
IIdType obsInCompartmentId = ourClient.create().resource(obsInCompartment).execute().getId().toUnqualifiedVersionless();
|
IIdType obsInCompartmentId = myClient.create().resource(obsInCompartment).execute().getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
Observation obsNotInCompartment = new Observation();
|
Observation obsNotInCompartment = new Observation();
|
||||||
obsNotInCompartment.setStatus(ObservationStatus.FINAL);
|
obsNotInCompartment.setStatus(ObservationStatus.FINAL);
|
||||||
IIdType obsNotInCompartmentId = ourClient.create().resource(obsNotInCompartment).execute().getId().toUnqualifiedVersionless();
|
IIdType obsNotInCompartmentId = myClient.create().resource(obsNotInCompartment).execute().getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
|
ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
|
||||||
@Override
|
@Override
|
||||||
|
@ -114,10 +165,10 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ourClient.delete().resourceById(obsInCompartmentId.toUnqualifiedVersionless()).execute();
|
myClient.delete().resourceById(obsInCompartmentId.toUnqualifiedVersionless()).execute();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ourClient.delete().resourceById(obsNotInCompartmentId.toUnqualifiedVersionless()).execute();
|
myClient.delete().resourceById(obsNotInCompartmentId.toUnqualifiedVersionless()).execute();
|
||||||
fail();
|
fail();
|
||||||
} catch (ForbiddenOperationException e) {
|
} catch (ForbiddenOperationException e) {
|
||||||
// good
|
// good
|
||||||
|
@ -130,7 +181,7 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource
|
||||||
Patient patient = new Patient();
|
Patient patient = new Patient();
|
||||||
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
|
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
|
||||||
patient.addName().setFamily("Tester").addGiven("Raghad");
|
patient.addName().setFamily("Tester").addGiven("Raghad");
|
||||||
final MethodOutcome output1 = ourClient.update().resource(patient).conditionalByUrl("Patient?identifier=http://uhn.ca/mrns|100").execute();
|
final MethodOutcome output1 = myClient.update().resource(patient).conditionalByUrl("Patient?identifier=http://uhn.ca/mrns|100").execute();
|
||||||
|
|
||||||
ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
|
ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
|
||||||
@Override
|
@Override
|
||||||
|
@ -148,7 +199,7 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource
|
||||||
patient.setId(output1.getId().toUnqualifiedVersionless());
|
patient.setId(output1.getId().toUnqualifiedVersionless());
|
||||||
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
|
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
|
||||||
patient.addName().setFamily("Tester").addGiven("Raghad");
|
patient.addName().setFamily("Tester").addGiven("Raghad");
|
||||||
MethodOutcome output2 = ourClient.update().resource(patient).conditionalByUrl("Patient?identifier=http://uhn.ca/mrns|100").execute();
|
MethodOutcome output2 = myClient.update().resource(patient).conditionalByUrl("Patient?identifier=http://uhn.ca/mrns|100").execute();
|
||||||
|
|
||||||
assertEquals(output1.getId().getIdPart(), output2.getId().getIdPart());
|
assertEquals(output1.getId().getIdPart(), output2.getId().getIdPart());
|
||||||
|
|
||||||
|
@ -156,7 +207,7 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource
|
||||||
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
|
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
|
||||||
patient.addName().setFamily("Tester").addGiven("Raghad");
|
patient.addName().setFamily("Tester").addGiven("Raghad");
|
||||||
try {
|
try {
|
||||||
ourClient.update().resource(patient).conditionalByUrl("Patient?identifier=http://uhn.ca/mrns|101").execute();
|
myClient.update().resource(patient).conditionalByUrl("Patient?identifier=http://uhn.ca/mrns|101").execute();
|
||||||
fail();
|
fail();
|
||||||
} catch (ForbiddenOperationException e) {
|
} catch (ForbiddenOperationException e) {
|
||||||
assertEquals("HTTP 403 Forbidden: Access denied by default policy (no applicable rules)", e.getMessage());
|
assertEquals("HTTP 403 Forbidden: Access denied by default policy (no applicable rules)", e.getMessage());
|
||||||
|
@ -167,7 +218,7 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource
|
||||||
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
|
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
|
||||||
patient.addName().setFamily("Tester").addGiven("Raghad");
|
patient.addName().setFamily("Tester").addGiven("Raghad");
|
||||||
try {
|
try {
|
||||||
ourClient.update().resource(patient).execute();
|
myClient.update().resource(patient).execute();
|
||||||
fail();
|
fail();
|
||||||
} catch (ForbiddenOperationException e) {
|
} catch (ForbiddenOperationException e) {
|
||||||
assertEquals("HTTP 403 Forbidden: Access denied by default policy (no applicable rules)", e.getMessage());
|
assertEquals("HTTP 403 Forbidden: Access denied by default policy (no applicable rules)", e.getMessage());
|
||||||
|
@ -182,11 +233,11 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource
|
||||||
public void testBlockUpdatingPatientUserDoesnNotHaveAccessTo() throws IOException {
|
public void testBlockUpdatingPatientUserDoesnNotHaveAccessTo() throws IOException {
|
||||||
Patient pt1 = new Patient();
|
Patient pt1 = new Patient();
|
||||||
pt1.setActive(true);
|
pt1.setActive(true);
|
||||||
final IIdType pid1 = ourClient.create().resource(pt1).execute().getId().toUnqualifiedVersionless();
|
final IIdType pid1 = myClient.create().resource(pt1).execute().getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
Patient pt2 = new Patient();
|
Patient pt2 = new Patient();
|
||||||
pt2.setActive(false);
|
pt2.setActive(false);
|
||||||
final IIdType pid2 = ourClient.create().resource(pt2).execute().getId().toUnqualifiedVersionless();
|
final IIdType pid2 = myClient.create().resource(pt2).execute().getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
|
ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
|
||||||
@Override
|
@Override
|
||||||
|
@ -200,7 +251,7 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource
|
||||||
Observation obs = new Observation();
|
Observation obs = new Observation();
|
||||||
obs.setStatus(ObservationStatus.FINAL);
|
obs.setStatus(ObservationStatus.FINAL);
|
||||||
obs.setSubject(new Reference(pid1));
|
obs.setSubject(new Reference(pid1));
|
||||||
IIdType oid = ourClient.create().resource(obs).execute().getId().toUnqualified();
|
IIdType oid = myClient.create().resource(obs).execute().getId().toUnqualified();
|
||||||
|
|
||||||
|
|
||||||
unregisterInterceptors();
|
unregisterInterceptors();
|
||||||
|
@ -224,7 +275,7 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource
|
||||||
obs.setSubject(new Reference(pid2));
|
obs.setSubject(new Reference(pid2));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ourClient.update().resource(obs).execute();
|
myClient.update().resource(obs).execute();
|
||||||
fail();
|
fail();
|
||||||
} catch (ForbiddenOperationException e) {
|
} catch (ForbiddenOperationException e) {
|
||||||
// good
|
// good
|
||||||
|
|
|
@ -47,7 +47,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
|
public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
|
||||||
|
|
||||||
protected static JpaValidationSupportChainR4 myValidationSupport;
|
protected static JpaValidationSupportChainR4 myValidationSupport;
|
||||||
protected static IGenericClient ourClient;
|
protected IGenericClient myClient;
|
||||||
protected static CloseableHttpClient ourHttpClient;
|
protected static CloseableHttpClient ourHttpClient;
|
||||||
protected static int ourPort;
|
protected static int ourPort;
|
||||||
protected static RestfulServer ourRestServer;
|
protected static RestfulServer ourRestServer;
|
||||||
|
@ -150,10 +150,6 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
|
||||||
ourSearchParamRegistry = wac.getBean(SearchParamRegistryR4.class);
|
ourSearchParamRegistry = wac.getBean(SearchParamRegistryR4.class);
|
||||||
|
|
||||||
myFhirCtx.getRestfulClientFactory().setSocketTimeout(5000000);
|
myFhirCtx.getRestfulClientFactory().setSocketTimeout(5000000);
|
||||||
ourClient = myFhirCtx.newRestfulGenericClient(ourServerBase);
|
|
||||||
if (shouldLogClient()) {
|
|
||||||
ourClient.registerInterceptor(new LoggingInterceptor());
|
|
||||||
}
|
|
||||||
|
|
||||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||||
HttpClientBuilder builder = HttpClientBuilder.create();
|
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||||
|
@ -165,6 +161,11 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
ourRestServer.setPagingProvider(ourPagingProvider);
|
ourRestServer.setPagingProvider(ourPagingProvider);
|
||||||
|
|
||||||
|
myClient = myFhirCtx.newRestfulGenericClient(ourServerBase);
|
||||||
|
if (shouldLogClient()) {
|
||||||
|
myClient.registerInterceptor(new LoggingInterceptor());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
package ca.uhn.fhir.jpa.provider.r4;
|
package ca.uhn.fhir.jpa.provider.r4;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.util.TestUtil;
|
||||||
import ca.uhn.fhir.util.UrlUtil;
|
import ca.uhn.fhir.util.UrlUtil;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
import org.apache.http.client.methods.HttpGet;
|
import org.apache.http.client.methods.HttpGet;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.hl7.fhir.r4.model.Bundle;
|
|
||||||
import org.hl7.fhir.r4.model.Patient;
|
import org.hl7.fhir.r4.model.Patient;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -31,14 +31,14 @@ public class GraphQLProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
try {
|
try {
|
||||||
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||||
ourLog.info(resp);
|
ourLog.info(resp);
|
||||||
assertEquals(resp, "{\n" +
|
assertEquals(TestUtil.stripReturns(resp), TestUtil.stripReturns("{\n" +
|
||||||
" \"name\":[{\n" +
|
" \"name\":[{\n" +
|
||||||
" \"family\":\"FAM\",\n" +
|
" \"family\":\"FAM\",\n" +
|
||||||
" \"given\":[\"GIVEN1\",\"GIVEN2\"]\n" +
|
" \"given\":[\"GIVEN1\",\"GIVEN2\"]\n" +
|
||||||
" },{\n" +
|
" },{\n" +
|
||||||
" \"given\":[\"GivenOnly1\",\"GivenOnly2\"]\n" +
|
" \"given\":[\"GivenOnly1\",\"GivenOnly2\"]\n" +
|
||||||
" }]\n" +
|
" }]\n" +
|
||||||
"}");
|
"}"));
|
||||||
} finally {
|
} finally {
|
||||||
IOUtils.closeQuietly(response);
|
IOUtils.closeQuietly(response);
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ public class GraphQLProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
try {
|
try {
|
||||||
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||||
ourLog.info(resp);
|
ourLog.info(resp);
|
||||||
assertEquals(resp, "{\n" +
|
assertEquals(TestUtil.stripReturns(resp), TestUtil.stripReturns("{\n" +
|
||||||
" \"PatientList\":[{\n" +
|
" \"PatientList\":[{\n" +
|
||||||
" \"name\":[{\n" +
|
" \"name\":[{\n" +
|
||||||
" \"family\":\"FAM\",\n" +
|
" \"family\":\"FAM\",\n" +
|
||||||
|
@ -69,7 +69,7 @@ public class GraphQLProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
" \"given\":[\"GivenOnlyB1\",\"GivenOnlyB2\"]\n" +
|
" \"given\":[\"GivenOnlyB1\",\"GivenOnlyB2\"]\n" +
|
||||||
" }]\n" +
|
" }]\n" +
|
||||||
" }]\n" +
|
" }]\n" +
|
||||||
"}");
|
"}"));
|
||||||
} finally {
|
} finally {
|
||||||
IOUtils.closeQuietly(response);
|
IOUtils.closeQuietly(response);
|
||||||
}
|
}
|
||||||
|
@ -85,13 +85,13 @@ public class GraphQLProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
p.addName()
|
p.addName()
|
||||||
.addGiven("GivenOnly1")
|
.addGiven("GivenOnly1")
|
||||||
.addGiven("GivenOnly2");
|
.addGiven("GivenOnly2");
|
||||||
myPatientId0 = ourClient.create().resource(p).execute().getId().toUnqualifiedVersionless();
|
myPatientId0 = myClient.create().resource(p).execute().getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
p = new Patient();
|
p = new Patient();
|
||||||
p.addName()
|
p.addName()
|
||||||
.addGiven("GivenOnlyB1")
|
.addGiven("GivenOnlyB1")
|
||||||
.addGiven("GivenOnlyB2");
|
.addGiven("GivenOnlyB2");
|
||||||
ourClient.create().resource(p).execute();
|
myClient.create().resource(p).execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -57,41 +57,41 @@ public class PatientEverythingR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
Organization org = new Organization();
|
Organization org = new Organization();
|
||||||
org.setName("an org");
|
org.setName("an org");
|
||||||
orgId = ourClient.create().resource(org).execute().getId().toUnqualifiedVersionless().getValue();
|
orgId = myClient.create().resource(org).execute().getId().toUnqualifiedVersionless().getValue();
|
||||||
ourLog.info("OrgId: {}", orgId);
|
ourLog.info("OrgId: {}", orgId);
|
||||||
|
|
||||||
Patient patient = new Patient();
|
Patient patient = new Patient();
|
||||||
patient.getManagingOrganization().setReference(orgId);
|
patient.getManagingOrganization().setReference(orgId);
|
||||||
patId = ourClient.create().resource(patient).execute().getId().toUnqualifiedVersionless().getValue();
|
patId = myClient.create().resource(patient).execute().getId().toUnqualifiedVersionless().getValue();
|
||||||
|
|
||||||
Patient patient2 = new Patient();
|
Patient patient2 = new Patient();
|
||||||
patient2.getManagingOrganization().setReference(orgId);
|
patient2.getManagingOrganization().setReference(orgId);
|
||||||
myWrongPatId = ourClient.create().resource(patient2).execute().getId().toUnqualifiedVersionless().getValue();
|
myWrongPatId = myClient.create().resource(patient2).execute().getId().toUnqualifiedVersionless().getValue();
|
||||||
|
|
||||||
Encounter enc1 = new Encounter();
|
Encounter enc1 = new Encounter();
|
||||||
enc1.setStatus(EncounterStatus.CANCELLED);
|
enc1.setStatus(EncounterStatus.CANCELLED);
|
||||||
enc1.getSubject().setReference(patId);
|
enc1.getSubject().setReference(patId);
|
||||||
enc1.getServiceProvider().setReference(orgId);
|
enc1.getServiceProvider().setReference(orgId);
|
||||||
encId1 = ourClient.create().resource(enc1).execute().getId().toUnqualifiedVersionless().getValue();
|
encId1 = myClient.create().resource(enc1).execute().getId().toUnqualifiedVersionless().getValue();
|
||||||
|
|
||||||
Encounter enc2 = new Encounter();
|
Encounter enc2 = new Encounter();
|
||||||
enc2.setStatus(EncounterStatus.ARRIVED);
|
enc2.setStatus(EncounterStatus.ARRIVED);
|
||||||
enc2.getSubject().setReference(patId);
|
enc2.getSubject().setReference(patId);
|
||||||
enc2.getServiceProvider().setReference(orgId);
|
enc2.getServiceProvider().setReference(orgId);
|
||||||
encId2 = ourClient.create().resource(enc2).execute().getId().toUnqualifiedVersionless().getValue();
|
encId2 = myClient.create().resource(enc2).execute().getId().toUnqualifiedVersionless().getValue();
|
||||||
|
|
||||||
Encounter wrongEnc1 = new Encounter();
|
Encounter wrongEnc1 = new Encounter();
|
||||||
wrongEnc1.setStatus(EncounterStatus.ARRIVED);
|
wrongEnc1.setStatus(EncounterStatus.ARRIVED);
|
||||||
wrongEnc1.getSubject().setReference(myWrongPatId);
|
wrongEnc1.getSubject().setReference(myWrongPatId);
|
||||||
wrongEnc1.getServiceProvider().setReference(orgId);
|
wrongEnc1.getServiceProvider().setReference(orgId);
|
||||||
myWrongEnc1 = ourClient.create().resource(wrongEnc1).execute().getId().toUnqualifiedVersionless().getValue();
|
myWrongEnc1 = myClient.create().resource(wrongEnc1).execute().getId().toUnqualifiedVersionless().getValue();
|
||||||
|
|
||||||
myObsIds = new ArrayList<String>();
|
myObsIds = new ArrayList<String>();
|
||||||
for (int i = 0; i < 20; i++) {
|
for (int i = 0; i < 20; i++) {
|
||||||
Observation obs = new Observation();
|
Observation obs = new Observation();
|
||||||
obs.getSubject().setReference(patId);
|
obs.getSubject().setReference(patId);
|
||||||
obs.setStatus(ObservationStatus.FINAL);
|
obs.setStatus(ObservationStatus.FINAL);
|
||||||
String obsId = ourClient.create().resource(obs).execute().getId().toUnqualifiedVersionless().getValue();
|
String obsId = myClient.create().resource(obs).execute().getId().toUnqualifiedVersionless().getValue();
|
||||||
myObsIds.add(obsId);
|
myObsIds.add(obsId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,7 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide
|
||||||
sp.setTitle("Foo Param");
|
sp.setTitle("Foo Param");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ourClient.create().resource(sp).execute();
|
myClient.create().resource(sp).execute();
|
||||||
fail();
|
fail();
|
||||||
} catch (UnprocessableEntityException e) {
|
} catch (UnprocessableEntityException e) {
|
||||||
assertEquals("HTTP 422 Unprocessable Entity: SearchParameter.status is missing or invalid: null", e.getMessage());
|
assertEquals("HTTP 422 Unprocessable Entity: SearchParameter.status is missing or invalid: null", e.getMessage());
|
||||||
|
@ -120,7 +120,7 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide
|
||||||
|
|
||||||
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(eyeColourSp));
|
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(eyeColourSp));
|
||||||
|
|
||||||
ourClient
|
myClient
|
||||||
.create()
|
.create()
|
||||||
.resource(eyeColourSp)
|
.resource(eyeColourSp)
|
||||||
.execute();
|
.execute();
|
||||||
|
@ -139,7 +139,7 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide
|
||||||
p2.addExtension().setUrl("http://acme.org/eyecolour").setValue(new CodeType("green"));
|
p2.addExtension().setUrl("http://acme.org/eyecolour").setValue(new CodeType("green"));
|
||||||
IIdType p2id = myPatientDao.create(p2).getId().toUnqualifiedVersionless();
|
IIdType p2id = myPatientDao.create(p2).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
Bundle bundle = ourClient
|
Bundle bundle = myClient
|
||||||
.search()
|
.search()
|
||||||
.forResource(Patient.class)
|
.forResource(Patient.class)
|
||||||
.where(new TokenClientParam("eyecolour").exactly().code("blue"))
|
.where(new TokenClientParam("eyecolour").exactly().code("blue"))
|
||||||
|
@ -168,7 +168,7 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide
|
||||||
public void testConformanceOverrideAllowed() {
|
public void testConformanceOverrideAllowed() {
|
||||||
myDaoConfig.setDefaultSearchParamsCanBeOverridden(true);
|
myDaoConfig.setDefaultSearchParamsCanBeOverridden(true);
|
||||||
|
|
||||||
CapabilityStatement conformance = ourClient
|
CapabilityStatement conformance = myClient
|
||||||
.fetchConformance()
|
.fetchConformance()
|
||||||
.ofType(CapabilityStatement.class)
|
.ofType(CapabilityStatement.class)
|
||||||
.execute();
|
.execute();
|
||||||
|
@ -220,7 +220,7 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
conformance = ourClient
|
conformance = myClient
|
||||||
.fetchConformance()
|
.fetchConformance()
|
||||||
.ofType(CapabilityStatement.class)
|
.ofType(CapabilityStatement.class)
|
||||||
.execute();
|
.execute();
|
||||||
|
@ -238,7 +238,7 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide
|
||||||
public void testConformanceOverrideNotAllowed() {
|
public void testConformanceOverrideNotAllowed() {
|
||||||
myDaoConfig.setDefaultSearchParamsCanBeOverridden(false);
|
myDaoConfig.setDefaultSearchParamsCanBeOverridden(false);
|
||||||
|
|
||||||
CapabilityStatement conformance = ourClient
|
CapabilityStatement conformance = myClient
|
||||||
.fetchConformance()
|
.fetchConformance()
|
||||||
.ofType(CapabilityStatement.class)
|
.ofType(CapabilityStatement.class)
|
||||||
.execute();
|
.execute();
|
||||||
|
@ -274,7 +274,7 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide
|
||||||
|
|
||||||
mySearchParamRegsitry.forceRefresh();
|
mySearchParamRegsitry.forceRefresh();
|
||||||
|
|
||||||
conformance = ourClient
|
conformance = myClient
|
||||||
.fetchConformance()
|
.fetchConformance()
|
||||||
.ofType(CapabilityStatement.class)
|
.ofType(CapabilityStatement.class)
|
||||||
.execute();
|
.execute();
|
||||||
|
@ -332,7 +332,7 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide
|
||||||
List<String> foundResources;
|
List<String> foundResources;
|
||||||
Bundle result;
|
Bundle result;
|
||||||
|
|
||||||
result = ourClient
|
result = myClient
|
||||||
.search()
|
.search()
|
||||||
.forResource(Patient.class)
|
.forResource(Patient.class)
|
||||||
.where(new TokenClientParam("foo").exactly().code("male"))
|
.where(new TokenClientParam("foo").exactly().code("male"))
|
||||||
|
@ -409,7 +409,7 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide
|
||||||
List<String> foundResources;
|
List<String> foundResources;
|
||||||
Bundle result;
|
Bundle result;
|
||||||
|
|
||||||
result = ourClient
|
result = myClient
|
||||||
.search()
|
.search()
|
||||||
.forResource(Observation.class)
|
.forResource(Observation.class)
|
||||||
.where(new ReferenceClientParam("foo").hasChainedProperty(Patient.GENDER.exactly().code("male")))
|
.where(new ReferenceClientParam("foo").hasChainedProperty(Patient.GENDER.exactly().code("male")))
|
||||||
|
|
|
@ -198,7 +198,7 @@ public class ResourceProviderInterceptorR4Test extends BaseResourceProviderR4Tes
|
||||||
|
|
||||||
Organization org = new Organization();
|
Organization org = new Organization();
|
||||||
org.setName("orgName");
|
org.setName("orgName");
|
||||||
IIdType orgId = ourClient.create().resource(org).execute().getId().toUnqualified();
|
IIdType orgId = myClient.create().resource(org).execute().getId().toUnqualified();
|
||||||
assertNotNull(orgId.getVersionIdPartAsLong());
|
assertNotNull(orgId.getVersionIdPartAsLong());
|
||||||
|
|
||||||
resetServerInterceptor();
|
resetServerInterceptor();
|
||||||
|
|
|
@ -72,7 +72,7 @@ public class ResourceProviderQuestionnaireResponseR4Test extends BaseResourcePro
|
||||||
qr1.setStatus(QuestionnaireResponseStatus.COMPLETED);
|
qr1.setStatus(QuestionnaireResponseStatus.COMPLETED);
|
||||||
qr1.addItem().setLinkId("link1").addAnswer().setValue(new DecimalType(123));
|
qr1.addItem().setLinkId("link1").addAnswer().setValue(new DecimalType(123));
|
||||||
try {
|
try {
|
||||||
ourClient.create().resource(qr1).execute();
|
myClient.create().resource(qr1).execute();
|
||||||
fail();
|
fail();
|
||||||
} catch (UnprocessableEntityException e) {
|
} catch (UnprocessableEntityException e) {
|
||||||
assertThat(e.toString(), containsString("Answer value must be of type string"));
|
assertThat(e.toString(), containsString("Answer value must be of type string"));
|
||||||
|
@ -95,7 +95,7 @@ public class ResourceProviderQuestionnaireResponseR4Test extends BaseResourcePro
|
||||||
qr1.setStatus(QuestionnaireResponseStatus.COMPLETED);
|
qr1.setStatus(QuestionnaireResponseStatus.COMPLETED);
|
||||||
qr1.addItem().setLinkId("link1").addAnswer().setValue(new DecimalType(123));
|
qr1.addItem().setLinkId("link1").addAnswer().setValue(new DecimalType(123));
|
||||||
try {
|
try {
|
||||||
ourClient.create().resource(qr1).execute();
|
myClient.create().resource(qr1).execute();
|
||||||
fail();
|
fail();
|
||||||
} catch (UnprocessableEntityException e) {
|
} catch (UnprocessableEntityException e) {
|
||||||
assertThat(e.toString(), containsString("Answer value must be of type string"));
|
assertThat(e.toString(), containsString("Answer value must be of type string"));
|
||||||
|
|
|
@ -34,9 +34,9 @@ public class ResourceProviderR4BundleTest extends BaseResourceProviderR4Test {
|
||||||
composition.setTitle("Visit Summary");
|
composition.setTitle("Visit Summary");
|
||||||
bundle.addEntry().setFullUrl("http://foo").setResource(composition);
|
bundle.addEntry().setFullUrl("http://foo").setResource(composition);
|
||||||
|
|
||||||
IIdType id = ourClient.create().resource(bundle).execute().getId();
|
IIdType id = myClient.create().resource(bundle).execute().getId();
|
||||||
|
|
||||||
Bundle retBundle = ourClient.read().resource(Bundle.class).withId(id).execute();
|
Bundle retBundle = myClient.read().resource(Bundle.class).withId(id).execute();
|
||||||
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(retBundle));
|
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(retBundle));
|
||||||
|
|
||||||
assertEquals("http://foo", bundle.getEntry().get(0).getFullUrl());
|
assertEquals("http://foo", bundle.getEntry().get(0).getFullUrl());
|
||||||
|
|
|
@ -4,6 +4,8 @@ import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.search.SearchCoordinatorSvcImpl;
|
import ca.uhn.fhir.jpa.search.SearchCoordinatorSvcImpl;
|
||||||
import ca.uhn.fhir.parser.StrictErrorHandler;
|
import ca.uhn.fhir.parser.StrictErrorHandler;
|
||||||
import ca.uhn.fhir.rest.api.CacheControlDirective;
|
import ca.uhn.fhir.rest.api.CacheControlDirective;
|
||||||
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
|
import ca.uhn.fhir.rest.client.interceptor.CapturingInterceptor;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
import ca.uhn.fhir.util.TestUtil;
|
import ca.uhn.fhir.util.TestUtil;
|
||||||
import org.hl7.fhir.r4.model.Bundle;
|
import org.hl7.fhir.r4.model.Bundle;
|
||||||
|
@ -14,13 +16,17 @@ import org.junit.Test;
|
||||||
import org.springframework.test.util.AopTestUtils;
|
import org.springframework.test.util.AopTestUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.*;
|
||||||
|
import static org.hamcrest.core.IsNot.not;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
public class ResourceProviderR4CacheTest extends BaseResourceProviderR4Test {
|
public class ResourceProviderR4CacheTest extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderR4CacheTest.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderR4CacheTest.class);
|
||||||
private SearchCoordinatorSvcImpl mySearchCoordinatorSvcRaw;
|
private SearchCoordinatorSvcImpl mySearchCoordinatorSvcRaw;
|
||||||
|
private CapturingInterceptor myCapturingInterceptor;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@After
|
@After
|
||||||
|
@ -28,6 +34,8 @@ public class ResourceProviderR4CacheTest extends BaseResourceProviderR4Test {
|
||||||
super.after();
|
super.after();
|
||||||
myDaoConfig.setReuseCachedSearchResultsForMillis(new DaoConfig().getReuseCachedSearchResultsForMillis());
|
myDaoConfig.setReuseCachedSearchResultsForMillis(new DaoConfig().getReuseCachedSearchResultsForMillis());
|
||||||
myDaoConfig.setCacheControlNoStoreMaxResultsUpperLimit(new DaoConfig().getCacheControlNoStoreMaxResultsUpperLimit());
|
myDaoConfig.setCacheControlNoStoreMaxResultsUpperLimit(new DaoConfig().getCacheControlNoStoreMaxResultsUpperLimit());
|
||||||
|
|
||||||
|
myClient.unregisterInterceptor(myCapturingInterceptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -35,6 +43,9 @@ public class ResourceProviderR4CacheTest extends BaseResourceProviderR4Test {
|
||||||
super.before();
|
super.before();
|
||||||
myFhirCtx.setParserErrorHandler(new StrictErrorHandler());
|
myFhirCtx.setParserErrorHandler(new StrictErrorHandler());
|
||||||
mySearchCoordinatorSvcRaw = AopTestUtils.getTargetObject(mySearchCoordinatorSvc);
|
mySearchCoordinatorSvcRaw = AopTestUtils.getTargetObject(mySearchCoordinatorSvc);
|
||||||
|
|
||||||
|
myCapturingInterceptor = new CapturingInterceptor();
|
||||||
|
myClient.registerInterceptor(myCapturingInterceptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -42,9 +53,9 @@ public class ResourceProviderR4CacheTest extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
Patient pt1 = new Patient();
|
Patient pt1 = new Patient();
|
||||||
pt1.addName().setFamily("FAM");
|
pt1.addName().setFamily("FAM");
|
||||||
ourClient.create().resource(pt1).execute();
|
myClient.create().resource(pt1).execute();
|
||||||
|
|
||||||
Bundle results = ourClient
|
Bundle results = myClient
|
||||||
.search()
|
.search()
|
||||||
.forResource("Patient")
|
.forResource("Patient")
|
||||||
.where(Patient.FAMILY.matches().value("FAM"))
|
.where(Patient.FAMILY.matches().value("FAM"))
|
||||||
|
@ -53,12 +64,13 @@ public class ResourceProviderR4CacheTest extends BaseResourceProviderR4Test {
|
||||||
.execute();
|
.execute();
|
||||||
assertEquals(1, results.getEntry().size());
|
assertEquals(1, results.getEntry().size());
|
||||||
assertEquals(0, mySearchEntityDao.count());
|
assertEquals(0, mySearchEntityDao.count());
|
||||||
|
assertThat(myCapturingInterceptor.getLastResponse().getHeaders(Constants.HEADER_X_CACHE), empty());
|
||||||
|
|
||||||
Patient pt2 = new Patient();
|
Patient pt2 = new Patient();
|
||||||
pt2.addName().setFamily("FAM");
|
pt2.addName().setFamily("FAM");
|
||||||
ourClient.create().resource(pt2).execute();
|
myClient.create().resource(pt2).execute();
|
||||||
|
|
||||||
results = ourClient
|
results = myClient
|
||||||
.search()
|
.search()
|
||||||
.forResource("Patient")
|
.forResource("Patient")
|
||||||
.where(Patient.FAMILY.matches().value("FAM"))
|
.where(Patient.FAMILY.matches().value("FAM"))
|
||||||
|
@ -67,6 +79,7 @@ public class ResourceProviderR4CacheTest extends BaseResourceProviderR4Test {
|
||||||
.execute();
|
.execute();
|
||||||
assertEquals(2, results.getEntry().size());
|
assertEquals(2, results.getEntry().size());
|
||||||
assertEquals(0, mySearchEntityDao.count());
|
assertEquals(0, mySearchEntityDao.count());
|
||||||
|
assertThat(myCapturingInterceptor.getLastResponse().getHeaders(Constants.HEADER_X_CACHE), empty());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,10 +89,10 @@ public class ResourceProviderR4CacheTest extends BaseResourceProviderR4Test {
|
||||||
for (int i = 0; i < 10; i++) {
|
for (int i = 0; i < 10; i++) {
|
||||||
Patient pt1 = new Patient();
|
Patient pt1 = new Patient();
|
||||||
pt1.addName().setFamily("FAM" + i);
|
pt1.addName().setFamily("FAM" + i);
|
||||||
ourClient.create().resource(pt1).execute();
|
myClient.create().resource(pt1).execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
Bundle results = ourClient
|
Bundle results = myClient
|
||||||
.search()
|
.search()
|
||||||
.forResource("Patient")
|
.forResource("Patient")
|
||||||
.where(Patient.FAMILY.matches().value("FAM"))
|
.where(Patient.FAMILY.matches().value("FAM"))
|
||||||
|
@ -88,6 +101,7 @@ public class ResourceProviderR4CacheTest extends BaseResourceProviderR4Test {
|
||||||
.execute();
|
.execute();
|
||||||
assertEquals(5, results.getEntry().size());
|
assertEquals(5, results.getEntry().size());
|
||||||
assertEquals(0, mySearchEntityDao.count());
|
assertEquals(0, mySearchEntityDao.count());
|
||||||
|
assertThat(myCapturingInterceptor.getLastResponse().getHeaders(Constants.HEADER_X_CACHE), empty());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +109,7 @@ public class ResourceProviderR4CacheTest extends BaseResourceProviderR4Test {
|
||||||
public void testCacheNoStoreMaxResultsWithIllegalValue() throws IOException {
|
public void testCacheNoStoreMaxResultsWithIllegalValue() throws IOException {
|
||||||
myDaoConfig.setCacheControlNoStoreMaxResultsUpperLimit(123);
|
myDaoConfig.setCacheControlNoStoreMaxResultsUpperLimit(123);
|
||||||
try {
|
try {
|
||||||
ourClient
|
myClient
|
||||||
.search()
|
.search()
|
||||||
.forResource("Patient")
|
.forResource("Patient")
|
||||||
.where(Patient.FAMILY.matches().value("FAM"))
|
.where(Patient.FAMILY.matches().value("FAM"))
|
||||||
|
@ -113,17 +127,18 @@ public class ResourceProviderR4CacheTest extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
Patient pt1 = new Patient();
|
Patient pt1 = new Patient();
|
||||||
pt1.addName().setFamily("FAM");
|
pt1.addName().setFamily("FAM");
|
||||||
ourClient.create().resource(pt1).execute();
|
myClient.create().resource(pt1).execute();
|
||||||
|
|
||||||
Bundle results = ourClient.search().forResource("Patient").where(Patient.FAMILY.matches().value("FAM")).returnBundle(Bundle.class).execute();
|
Bundle results = myClient.search().forResource("Patient").where(Patient.FAMILY.matches().value("FAM")).returnBundle(Bundle.class).execute();
|
||||||
assertEquals(1, results.getEntry().size());
|
assertEquals(1, results.getEntry().size());
|
||||||
assertEquals(1, mySearchEntityDao.count());
|
assertEquals(1, mySearchEntityDao.count());
|
||||||
|
assertThat(myCapturingInterceptor.getLastResponse().getHeaders(Constants.HEADER_X_CACHE), empty());
|
||||||
|
|
||||||
Patient pt2 = new Patient();
|
Patient pt2 = new Patient();
|
||||||
pt2.addName().setFamily("FAM");
|
pt2.addName().setFamily("FAM");
|
||||||
ourClient.create().resource(pt2).execute();
|
myClient.create().resource(pt2).execute();
|
||||||
|
|
||||||
results = ourClient
|
results = myClient
|
||||||
.search()
|
.search()
|
||||||
.forResource("Patient")
|
.forResource("Patient")
|
||||||
.where(Patient.FAMILY.matches().value("FAM"))
|
.where(Patient.FAMILY.matches().value("FAM"))
|
||||||
|
@ -132,6 +147,7 @@ public class ResourceProviderR4CacheTest extends BaseResourceProviderR4Test {
|
||||||
.execute();
|
.execute();
|
||||||
assertEquals(2, results.getEntry().size());
|
assertEquals(2, results.getEntry().size());
|
||||||
assertEquals(2, mySearchEntityDao.count());
|
assertEquals(2, mySearchEntityDao.count());
|
||||||
|
assertThat(myCapturingInterceptor.getLastResponse().getHeaders(Constants.HEADER_X_CACHE), empty());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,20 +156,28 @@ public class ResourceProviderR4CacheTest extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
Patient pt1 = new Patient();
|
Patient pt1 = new Patient();
|
||||||
pt1.addName().setFamily("FAM");
|
pt1.addName().setFamily("FAM");
|
||||||
ourClient.create().resource(pt1).execute();
|
myClient.create().resource(pt1).execute();
|
||||||
|
|
||||||
Bundle results = ourClient.search().forResource("Patient").where(Patient.FAMILY.matches().value("FAM")).returnBundle(Bundle.class).execute();
|
Date beforeFirst = new Date();
|
||||||
assertEquals(1, results.getEntry().size());
|
|
||||||
|
Bundle results1 = myClient.search().forResource("Patient").where(Patient.FAMILY.matches().value("FAM")).returnBundle(Bundle.class).execute();
|
||||||
|
assertEquals(1, results1.getEntry().size());
|
||||||
assertEquals(1, mySearchEntityDao.count());
|
assertEquals(1, mySearchEntityDao.count());
|
||||||
|
assertThat(myCapturingInterceptor.getLastResponse().getHeaders(Constants.HEADER_X_CACHE), empty());
|
||||||
|
assertThat(results1.getMeta().getLastUpdated(), greaterThan(beforeFirst));
|
||||||
|
assertThat(results1.getMeta().getLastUpdated(), lessThan(new Date()));
|
||||||
|
assertThat(results1.getId(), not(blankOrNullString()));
|
||||||
|
|
||||||
Patient pt2 = new Patient();
|
Patient pt2 = new Patient();
|
||||||
pt2.addName().setFamily("FAM");
|
pt2.addName().setFamily("FAM");
|
||||||
ourClient.create().resource(pt2).execute();
|
myClient.create().resource(pt2).execute();
|
||||||
|
|
||||||
results = ourClient.search().forResource("Patient").where(Patient.FAMILY.matches().value("FAM")).returnBundle(Bundle.class).execute();
|
Bundle results2 = myClient.search().forResource("Patient").where(Patient.FAMILY.matches().value("FAM")).returnBundle(Bundle.class).execute();
|
||||||
assertEquals(1, results.getEntry().size());
|
assertEquals(1, results2.getEntry().size());
|
||||||
assertEquals(1, mySearchEntityDao.count());
|
assertEquals(1, mySearchEntityDao.count());
|
||||||
|
assertEquals("HIT from " + ourServerBase, myCapturingInterceptor.getLastResponse().getHeaders(Constants.HEADER_X_CACHE).get(0));
|
||||||
|
assertEquals(results1.getMeta().getLastUpdated(), results2.getMeta().getLastUpdated());
|
||||||
|
assertEquals(results1.getId(), results2.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue