Merge branch 'master' into analyzer-def-using-search-mapping
This commit is contained in:
commit
49f4ac4fc5
|
@ -141,6 +141,8 @@ local.properties
|
|||
**/.target
|
||||
**/.project
|
||||
**/.classpath
|
||||
**/.factorypath
|
||||
**/.springBeans
|
||||
|
||||
|
||||
# PDT-specific
|
||||
|
|
|
@ -22,4 +22,4 @@ before_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 -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>
|
||||
</dependencies>
|
||||
</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>
|
||||
</build>
|
||||
|
||||
|
|
|
@ -35,6 +35,25 @@
|
|||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
</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>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
@ -92,52 +111,14 @@
|
|||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>dstu2_shade</id>
|
||||
<id>it</id>
|
||||
<goals>
|
||||
<goal>integration-test</goal>
|
||||
<goal>verify</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<includes>
|
||||
<include>**/*Dstu2ShadeIT.java</include>
|
||||
<include>**/*IT.java</include>
|
||||
</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>
|
||||
</execution>
|
||||
</executions>
|
||||
|
|
|
@ -9,6 +9,7 @@ import java.util.zip.ZipFile;
|
|||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.filefilter.WildcardFileFilter;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
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);
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testParserXml() throws Exception {
|
||||
|
||||
FhirContext ctx = FhirContext.forDstu2();
|
||||
|
|
|
@ -7,7 +7,6 @@ import static org.mockito.Mockito.when;
|
|||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.hl7.fhir.dstu3.model.*;
|
||||
import org.junit.*;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
@ -82,6 +81,7 @@ public class GenericClientDstu3IT {
|
|||
* TODO: narratives don't work without stax
|
||||
*/
|
||||
@Test
|
||||
@Ignore
|
||||
public void testBinaryCreateWithFhirContentType() throws Exception {
|
||||
IParser p = ourCtx.newXmlParser();
|
||||
|
||||
|
@ -142,7 +142,7 @@ public class GenericClientDstu3IT {
|
|||
.returnBundle(Bundle.class)
|
||||
.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++;
|
||||
|
||||
}
|
||||
|
@ -177,12 +177,12 @@ public class GenericClientDstu3IT {
|
|||
Request request = capt.getAllValues().get(0);
|
||||
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);
|
||||
|
||||
assertEquals(Constants.CT_FHIR_XML_NEW + ";charset=utf-8", request.body().contentType().toString().toLowerCase().replace(" ", ""));
|
||||
assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_NON_LEGACY, request.header("Accept"));
|
||||
assertArrayEquals(new byte[] { 0, 1, 2, 3, 4 }, ourCtx.newXmlParser().parseResource(Binary.class, extractBodyAsString(capt)).getContent());
|
||||
assertEquals(Constants.CT_FHIR_JSON_NEW + ";charset=utf-8", request.body().contentType().toString().toLowerCase().replace(" ", ""));
|
||||
assertEquals(Constants.HEADER_ACCEPT_VALUE_JSON_NON_LEGACY, request.header("Accept"));
|
||||
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());
|
||||
|
||||
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\"}]}}]}";
|
||||
myHttpResponse = new Response.Builder()
|
||||
.request(myRequest)
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
<dependency>
|
||||
<groupId>org.codehaus.woodstox</groupId>
|
||||
<artifactId>woodstox-core-asl</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Only required for narrative generator support -->
|
||||
|
@ -124,6 +125,7 @@
|
|||
<argLine>${argLine} -Dfile.encoding=UTF-8 -Xmx712m</argLine>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<!--
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
|
@ -136,6 +138,7 @@
|
|||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
-->
|
||||
</plugins>
|
||||
<resources>
|
||||
<resource>
|
||||
|
|
|
@ -78,6 +78,10 @@ public enum FhirVersionEnum {
|
|||
return myVersionImplementation;
|
||||
}
|
||||
|
||||
public boolean isEqualOrNewerThan(FhirVersionEnum theVersion) {
|
||||
return ordinal() >= theVersion.ordinal();
|
||||
}
|
||||
|
||||
public boolean isEquivalentTo(FhirVersionEnum theVersion) {
|
||||
if (this.equals(theVersion)) {
|
||||
return true;
|
||||
|
|
|
@ -152,7 +152,8 @@ public class RuntimeSearchParam {
|
|||
public enum RuntimeSearchParamStatusEnum {
|
||||
ACTIVE,
|
||||
DRAFT,
|
||||
RETIRED
|
||||
RETIRED,
|
||||
UNKNOWN
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,29 +20,29 @@ package ca.uhn.fhir.model.primitive;
|
|||
* #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.annotation.DatatypeDef;
|
||||
import ca.uhn.fhir.model.api.annotation.SimpleSetter;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.util.XmlDetectionUtil;
|
||||
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")
|
||||
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 DIV_OPEN_FIRST = "<div" + DECL_XMLNS + ">";
|
||||
public static final String DIV_OPEN_FIRST = "<div" + DECL_XMLNS + ">";
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
|
@ -54,7 +54,7 @@ public class XhtmlDt extends BasePrimitive<List<XMLEvent>> {
|
|||
|
||||
/**
|
||||
* Constructor which accepts a string code
|
||||
*
|
||||
*
|
||||
* @see #setValueAsString(String) for a description of how this value is applied
|
||||
*/
|
||||
@SimpleSetter()
|
||||
|
@ -63,29 +63,12 @@ public class XhtmlDt extends BasePrimitive<List<XMLEvent>> {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected String encode(List<XMLEvent> theValue) {
|
||||
try {
|
||||
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);
|
||||
}
|
||||
protected String encode(String theValue) {
|
||||
return theValue;
|
||||
}
|
||||
|
||||
public boolean hasContent() {
|
||||
return getValue() != null && getValue().size() > 0;
|
||||
return isNotBlank(getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -94,40 +77,37 @@ public class XhtmlDt extends BasePrimitive<List<XMLEvent>> {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected List<XMLEvent> parse(String theValue) {
|
||||
String val = theValue.trim();
|
||||
if (!val.startsWith("<")) {
|
||||
val = DIV_OPEN_FIRST + val + "</div>";
|
||||
}
|
||||
boolean hasProcessingInstruction = val.startsWith("<?");
|
||||
if (hasProcessingInstruction && val.endsWith("?>")) {
|
||||
return null;
|
||||
protected String parse(String theValue) {
|
||||
if (XmlDetectionUtil.isStaxPresent()) {
|
||||
// for validation
|
||||
XmlUtil.parse(theValue);
|
||||
}
|
||||
return theValue;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
ArrayList<XMLEvent> value = new ArrayList<XMLEvent>();
|
||||
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);
|
||||
}
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@Override
|
||||
public String getValue() {
|
||||
return super.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@Override
|
||||
public BasePrimitive<String> setValue(String theValue) throws DataFormatException {
|
||||
return super.setValue(theValue);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -157,7 +137,7 @@ public class XhtmlDt extends BasePrimitive<List<XMLEvent>> {
|
|||
if (value.charAt(0) != '<') {
|
||||
value = DIV_OPEN_FIRST + value + "</div>";
|
||||
}
|
||||
|
||||
|
||||
boolean hasProcessingInstruction = value.startsWith("<?");
|
||||
int firstTagIndex = value.indexOf("<", hasProcessingInstruction ? 1 : 0);
|
||||
if (firstTagIndex != -1) {
|
||||
|
|
|
@ -43,7 +43,7 @@ public abstract class BaseParser implements IParser {
|
|||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseParser.class);
|
||||
|
||||
private ContainedResources myContainedResources;
|
||||
|
||||
private boolean myEncodeElementsAppliesToChildResourcesOnly;
|
||||
private FhirContext myContext;
|
||||
private Set<String> myDontEncodeElements;
|
||||
private boolean myDontEncodeElementsIncludesStars;
|
||||
|
@ -556,6 +556,16 @@ public abstract class BaseParser implements IParser {
|
|||
&& theIncludedResource == false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEncodeElementsAppliesToChildResourcesOnly() {
|
||||
return myEncodeElementsAppliesToChildResourcesOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEncodeElementsAppliesToChildResourcesOnly(boolean theEncodeElementsAppliesToChildResourcesOnly) {
|
||||
myEncodeElementsAppliesToChildResourcesOnly = theEncodeElementsAppliesToChildResourcesOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOmitResourceId() {
|
||||
return myOmitResourceId;
|
||||
|
@ -1039,7 +1049,13 @@ public abstract class BaseParser implements IParser {
|
|||
}
|
||||
|
||||
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) {
|
||||
|
@ -1058,6 +1074,9 @@ public abstract class BaseParser implements IParser {
|
|||
} else {
|
||||
thePathBuilder.append(myResDef.getName());
|
||||
}
|
||||
if (theElements == null) {
|
||||
return true;
|
||||
}
|
||||
if (theElements.contains(thePathBuilder.toString())) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -206,6 +206,22 @@ public interface IParser {
|
|||
*/
|
||||
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
|
||||
* 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.
|
||||
* #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.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.ValueType;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.util.BinaryUtil;
|
||||
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.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
|
||||
|
@ -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,
|
||||
CompositeChildElement theParent) {
|
||||
CompositeChildElement theParent) {
|
||||
if (ext.size() > 0) {
|
||||
list.ensureCapacity(valueIdx);
|
||||
while (list.size() <= valueIdx) {
|
||||
|
@ -139,12 +144,6 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
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 {
|
||||
if (myPrettyPrint) {
|
||||
theEventWriter.setPrettyPrint(myPrettyPrint);
|
||||
|
@ -156,6 +155,12 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
theEventWriter.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doEncodeResourceToWriter(IBaseResource theResource, Writer theWriter) throws IOException {
|
||||
JsonLikeWriter eventWriter = createJsonWriter(theWriter);
|
||||
doEncodeResourceToJsonLikeWriter(theResource, eventWriter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends IBaseResource> T doParseResource(Class<T> theResourceType, Reader theReader) {
|
||||
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,
|
||||
BaseRuntimeElementDefinition<?> theChildDef, String theChildName, boolean theContainedResource, boolean theSubResource, CompositeChildElement theChildElem,
|
||||
boolean theForceEmpty) throws IOException {
|
||||
BaseRuntimeElementDefinition<?> theChildDef, String theChildName, boolean theContainedResource, boolean theSubResource, CompositeChildElement theChildElem,
|
||||
boolean theForceEmpty) throws IOException {
|
||||
|
||||
switch (theChildDef.getChildType()) {
|
||||
case ID_DATATYPE: {
|
||||
IIdType value = (IIdType) theNextValue;
|
||||
String encodedValue = "id".equals(theChildName) ? value.getIdPart() : value.getValue();
|
||||
if (isBlank(encodedValue)) {
|
||||
break;
|
||||
}
|
||||
if (theChildName != null) {
|
||||
write(theEventWriter, theChildName, encodedValue);
|
||||
} else {
|
||||
theEventWriter.write(encodedValue);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PRIMITIVE_DATATYPE: {
|
||||
final IPrimitiveType<?> value = (IPrimitiveType<?>) theNextValue;
|
||||
if (isBlank(value.getValueAsString())) {
|
||||
if (theForceEmpty) {
|
||||
theEventWriter.writeNull();
|
||||
case ID_DATATYPE: {
|
||||
IIdType value = (IIdType) theNextValue;
|
||||
String encodedValue = "id".equals(theChildName) ? value.getIdPart() : value.getValue();
|
||||
if (isBlank(encodedValue)) {
|
||||
break;
|
||||
}
|
||||
if (theChildName != null) {
|
||||
write(theEventWriter, theChildName, encodedValue);
|
||||
} else {
|
||||
theEventWriter.write(encodedValue);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (value instanceof IBaseIntegerDatatype) {
|
||||
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();
|
||||
case PRIMITIVE_DATATYPE: {
|
||||
final IPrimitiveType<?> value = (IPrimitiveType<?>) theNextValue;
|
||||
if (isBlank(value.getValueAsString())) {
|
||||
if (theForceEmpty) {
|
||||
theEventWriter.writeNull();
|
||||
}
|
||||
};
|
||||
if (theChildName != null) {
|
||||
write(theEventWriter, theChildName, decimalValue);
|
||||
} else {
|
||||
theEventWriter.write(decimalValue);
|
||||
break;
|
||||
}
|
||||
} else if (value instanceof IBaseBooleanDatatype) {
|
||||
if (theChildName != null) {
|
||||
write(theEventWriter, theChildName, ((IBaseBooleanDatatype) value).getValue());
|
||||
|
||||
if (value instanceof IBaseIntegerDatatype) {
|
||||
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 {
|
||||
Boolean booleanValue = ((IBaseBooleanDatatype) value).getValue();
|
||||
if (booleanValue != null) {
|
||||
theEventWriter.write(booleanValue.booleanValue());
|
||||
String valueStr = value.getValueAsString();
|
||||
if (theChildName != null) {
|
||||
write(theEventWriter, theChildName, valueStr);
|
||||
} else {
|
||||
theEventWriter.write(valueStr);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
String valueStr = value.getValueAsString();
|
||||
break;
|
||||
}
|
||||
case RESOURCE_BLOCK:
|
||||
case COMPOSITE_DATATYPE: {
|
||||
if (theChildName != null) {
|
||||
write(theEventWriter, theChildName, valueStr);
|
||||
theEventWriter.beginObject(theChildName);
|
||||
} else {
|
||||
theEventWriter.write(valueStr);
|
||||
theEventWriter.beginObject();
|
||||
}
|
||||
encodeCompositeElementToStreamWriter(theResDef, theResource, theNextValue, theEventWriter, theContainedResource, theSubResource, theChildElem);
|
||||
theEventWriter.endObject();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
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: {
|
||||
case CONTAINED_RESOURCE_LIST:
|
||||
case CONTAINED_RESOURCES: {
|
||||
/*
|
||||
* Disabled per #103 ContainedDt value = (ContainedDt) theNextValue; for (IResource next :
|
||||
* value.getContainedResources()) { if (getContainedResources().getResourceId(next) != null) { continue; }
|
||||
* encodeResourceToJsonStreamWriter(theResDef, next, theWriter, null, true,
|
||||
* fixContainedResourceId(next.getId().getValue())); }
|
||||
*/
|
||||
List<IBaseResource> containedResources = getContainedResources().getContainedResources();
|
||||
if (containedResources.size() > 0) {
|
||||
beginArray(theEventWriter, theChildName);
|
||||
List<IBaseResource> containedResources = getContainedResources().getContainedResources();
|
||||
if (containedResources.size() > 0) {
|
||||
beginArray(theEventWriter, theChildName);
|
||||
|
||||
for (IBaseResource next : containedResources) {
|
||||
IIdType resourceId = getContainedResources().getResourceId(next);
|
||||
encodeResourceToJsonStreamWriter(theResDef, next, theEventWriter, null, true, false, fixContainedResourceId(resourceId.getValue()));
|
||||
}
|
||||
for (IBaseResource next : containedResources) {
|
||||
IIdType resourceId = getContainedResources().getResourceId(next);
|
||||
encodeResourceToJsonStreamWriter(theResDef, next, theEventWriter, null, true, false, fixContainedResourceId(resourceId.getValue()));
|
||||
}
|
||||
|
||||
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();
|
||||
theEventWriter.endArray();
|
||||
}
|
||||
break;
|
||||
}
|
||||
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());
|
||||
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;
|
||||
}
|
||||
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,
|
||||
boolean theContainedResource, boolean theSubResource, CompositeChildElement theParent) throws IOException {
|
||||
boolean theContainedResource, boolean theSubResource, CompositeChildElement theParent) throws IOException {
|
||||
|
||||
{
|
||||
String elementId = getCompositeElementId(theElement);
|
||||
|
@ -335,7 +340,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
BaseRuntimeChildDefinition nextChild = nextChildElem.getDef();
|
||||
|
||||
if (nextChildElem.getDef().getElementName().equals("extension") || nextChildElem.getDef().getElementName().equals("modifierExtension")
|
||||
|| nextChild instanceof RuntimeChildDeclaredExtensionDefinition) {
|
||||
|| nextChild instanceof RuntimeChildDeclaredExtensionDefinition) {
|
||||
if (!haveWrittenExtensions) {
|
||||
extractAndWriteExtensionsAsDirectChild(theElement, theEventWriter, myContext.getElementDefinition(theElement.getClass()), theResDef, theResource, nextChildElem, theParent);
|
||||
haveWrittenExtensions = true;
|
||||
|
@ -451,15 +456,15 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
if (nextChild.getMax() > 1 || nextChild.getMax() == Child.MAX_UNLIMITED) {
|
||||
beginArray(theEventWriter, childName);
|
||||
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) {
|
||||
// suppress narratives from contained resources
|
||||
} 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;
|
||||
} else {
|
||||
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childDef, null, theContainedResource,theSubResource, nextChildElem, force);
|
||||
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childDef, null, theContainedResource, theSubResource, nextChildElem, force);
|
||||
}
|
||||
|
||||
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,
|
||||
CompositeChildElement theParent) throws IOException, DataFormatException {
|
||||
CompositeChildElement theParent) throws IOException, DataFormatException {
|
||||
|
||||
writeCommentsPreAndPost(theNextValue, theEventWriter);
|
||||
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()) {
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
// if (theResource instanceof IResource) {
|
||||
// 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,
|
||||
boolean theContainedResource, boolean theSubResource, IIdType theResourceId) throws IOException {
|
||||
boolean theContainedResource, boolean theSubResource, IIdType theResourceId) throws IOException {
|
||||
if (!theContainedResource) {
|
||||
super.containResourcesForEncoding(theResource);
|
||||
}
|
||||
|
@ -612,28 +617,28 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
}
|
||||
|
||||
write(theEventWriter, "resourceType", resDef.getName());
|
||||
if (theResourceId != null && theResourceId.hasIdPart()) {
|
||||
write(theEventWriter, "id", theResourceId.getIdPart());
|
||||
final List<HeldExtension> extensions = new ArrayList<HeldExtension>(0);
|
||||
final List<HeldExtension> modifierExtensions = new ArrayList<HeldExtension>(0);
|
||||
// Undeclared extensions
|
||||
extractUndeclaredExtensions(theResourceId, extensions, modifierExtensions, null, null);
|
||||
boolean haveExtension = false;
|
||||
if (!extensions.isEmpty()) {
|
||||
haveExtension = true;
|
||||
}
|
||||
if (theResourceId != null && theResourceId.hasIdPart()) {
|
||||
write(theEventWriter, "id", theResourceId.getIdPart());
|
||||
final List<HeldExtension> extensions = new ArrayList<HeldExtension>(0);
|
||||
final List<HeldExtension> modifierExtensions = new ArrayList<HeldExtension>(0);
|
||||
// Undeclared extensions
|
||||
extractUndeclaredExtensions(theResourceId, extensions, modifierExtensions, null, null);
|
||||
boolean haveExtension = false;
|
||||
if (!extensions.isEmpty()) {
|
||||
haveExtension = true;
|
||||
}
|
||||
|
||||
if (theResourceId.hasFormatComment() || haveExtension) {
|
||||
beginObject(theEventWriter, "_id");
|
||||
if (theResourceId.hasFormatComment()) {
|
||||
writeCommentsPreAndPost(theResourceId, theEventWriter);
|
||||
}
|
||||
if (haveExtension) {
|
||||
writeExtensionsAsDirectChild(theResource, theEventWriter, theResDef, extensions, modifierExtensions);
|
||||
}
|
||||
theEventWriter.endObject();
|
||||
}
|
||||
}
|
||||
if (theResourceId.hasFormatComment() || haveExtension) {
|
||||
beginObject(theEventWriter, "_id");
|
||||
if (theResourceId.hasFormatComment()) {
|
||||
writeCommentsPreAndPost(theResourceId, theEventWriter);
|
||||
}
|
||||
if (haveExtension) {
|
||||
writeExtensionsAsDirectChild(theResource, theEventWriter, theResDef, extensions, modifierExtensions);
|
||||
}
|
||||
theEventWriter.endObject();
|
||||
}
|
||||
}
|
||||
|
||||
if (theResource instanceof IResource) {
|
||||
IResource resource = (IResource) theResource;
|
||||
|
@ -695,19 +700,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
}
|
||||
}
|
||||
|
||||
if (theResource instanceof IBaseBinary) {
|
||||
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));
|
||||
}
|
||||
encodeCompositeElementToStreamWriter(theResDef, theResource, theResource, theEventWriter, theContainedResource, theSubResource, new CompositeChildElement(resDef, theSubResource));
|
||||
|
||||
theEventWriter.endObject();
|
||||
}
|
||||
|
@ -715,12 +708,12 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
/**
|
||||
* This is useful only for the two cases where extensions are encoded as direct children (e.g. not in some object
|
||||
* called _name): resource extensions, and extension extensions
|
||||
*
|
||||
*
|
||||
* @param theChildElem
|
||||
* @param theParent
|
||||
*/
|
||||
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> 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,
|
||||
CompositeChildElement theChildElem) {
|
||||
CompositeChildElement theChildElem) {
|
||||
for (RuntimeChildDeclaredExtensionDefinition nextDef : resDef.getExtensionsNonModifier()) {
|
||||
for (IBase nextValue : nextDef.getAccessor().getValues(theResource)) {
|
||||
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,
|
||||
CompositeChildElement theParent) {
|
||||
CompositeChildElement theParent) {
|
||||
if (theElement instanceof ISupportsUndeclaredExtensions) {
|
||||
ISupportsUndeclaredExtensions element = (ISupportsUndeclaredExtensions) theElement;
|
||||
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) {
|
||||
int allUnderscoreNames = 0;
|
||||
int handledUnderscoreNames = 0;
|
||||
private void parseExtension(ParserState<?> theState, JsonLikeArray theValues, boolean theIsModifier) {
|
||||
int allUnderscoreNames = 0;
|
||||
int handledUnderscoreNames = 0;
|
||||
|
||||
for (int i = 0; i < theValues.size(); i++) {
|
||||
JsonLikeObject nextExtObj = JsonLikeValue.asObject(theValues.get(i));
|
||||
JsonLikeValue jsonElement = nextExtObj.get("url");
|
||||
String url;
|
||||
if (null == jsonElement || !(jsonElement.isScalar())) {
|
||||
String parentElementName;
|
||||
if (theIsModifier) {
|
||||
parentElementName = "modifierExtension";
|
||||
} else {
|
||||
parentElementName = "extension";
|
||||
}
|
||||
getErrorHandler().missingRequiredElement(new ParseLocation(parentElementName), "url");
|
||||
url = null;
|
||||
} else {
|
||||
url = getExtensionUrl(jsonElement.getAsString());
|
||||
}
|
||||
theState.enteringNewElementExtension(null, url, theIsModifier, getServerBaseUrl());
|
||||
for (String next : nextExtObj.keySet()) {
|
||||
if ("url".equals(next)) {
|
||||
continue;
|
||||
} else if ("extension".equals(next)) {
|
||||
JsonLikeArray jsonVal = JsonLikeValue.asArray(nextExtObj.get(next));
|
||||
parseExtension(theState, jsonVal, false);
|
||||
} else if ("modifierExtension".equals(next)) {
|
||||
JsonLikeArray jsonVal = JsonLikeValue.asArray(nextExtObj.get(next));
|
||||
parseExtension(theState, jsonVal, true);
|
||||
} else if (next.charAt(0) == '_') {
|
||||
allUnderscoreNames++;
|
||||
continue;
|
||||
} else {
|
||||
JsonLikeValue jsonVal = nextExtObj.get(next);
|
||||
String alternateName = '_' + next;
|
||||
JsonLikeValue alternateVal = nextExtObj.get(alternateName);
|
||||
if (alternateVal != null) {
|
||||
handledUnderscoreNames++;
|
||||
}
|
||||
parseChildren(theState, next, jsonVal, alternateVal, alternateName, false);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < theValues.size(); i++) {
|
||||
JsonLikeObject nextExtObj = JsonLikeValue.asObject(theValues.get(i));
|
||||
JsonLikeValue jsonElement = nextExtObj.get("url");
|
||||
String url;
|
||||
if (null == jsonElement || !(jsonElement.isScalar())) {
|
||||
String parentElementName;
|
||||
if (theIsModifier) {
|
||||
parentElementName = "modifierExtension";
|
||||
} else {
|
||||
parentElementName = "extension";
|
||||
}
|
||||
getErrorHandler().missingRequiredElement(new ParseLocation(parentElementName), "url");
|
||||
url = null;
|
||||
} else {
|
||||
url = getExtensionUrl(jsonElement.getAsString());
|
||||
}
|
||||
theState.enteringNewElementExtension(null, url, theIsModifier, getServerBaseUrl());
|
||||
for (String next : nextExtObj.keySet()) {
|
||||
if ("url".equals(next)) {
|
||||
continue;
|
||||
} else if ("extension".equals(next)) {
|
||||
JsonLikeArray jsonVal = JsonLikeValue.asArray(nextExtObj.get(next));
|
||||
parseExtension(theState, jsonVal, false);
|
||||
} else if ("modifierExtension".equals(next)) {
|
||||
JsonLikeArray jsonVal = JsonLikeValue.asArray(nextExtObj.get(next));
|
||||
parseExtension(theState, jsonVal, true);
|
||||
} else if (next.charAt(0) == '_') {
|
||||
allUnderscoreNames++;
|
||||
continue;
|
||||
} else {
|
||||
JsonLikeValue jsonVal = nextExtObj.get(next);
|
||||
String alternateName = '_' + next;
|
||||
JsonLikeValue alternateVal = nextExtObj.get(alternateName);
|
||||
if (alternateVal != null) {
|
||||
handledUnderscoreNames++;
|
||||
}
|
||||
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"
|
||||
* element. This could be used to handle a null value with an extension
|
||||
* for example.
|
||||
*/
|
||||
if (allUnderscoreNames > handledUnderscoreNames) {
|
||||
for (String alternateName : nextExtObj.keySet()) {
|
||||
if (alternateName.startsWith("_") && alternateName.length() > 1) {
|
||||
JsonLikeValue nextValue = nextExtObj.get(alternateName);
|
||||
if (nextValue != null) {
|
||||
if (nextValue.isObject()) {
|
||||
String nextName = alternateName.substring(1);
|
||||
if (nextExtObj.get(nextName) == null) {
|
||||
theState.enteringNewElement(null, nextName);
|
||||
parseAlternates(nextValue, theState, alternateName, alternateName);
|
||||
theState.endingElement();
|
||||
}
|
||||
} else {
|
||||
getErrorHandler().incorrectJsonType(null, alternateName, ValueType.OBJECT, null, nextValue.getJsonType(), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
theState.endingElement();
|
||||
}
|
||||
}
|
||||
if (allUnderscoreNames > handledUnderscoreNames) {
|
||||
for (String alternateName : nextExtObj.keySet()) {
|
||||
if (alternateName.startsWith("_") && alternateName.length() > 1) {
|
||||
JsonLikeValue nextValue = nextExtObj.get(alternateName);
|
||||
if (nextValue != null) {
|
||||
if (nextValue.isObject()) {
|
||||
String nextName = alternateName.substring(1);
|
||||
if (nextExtObj.get(nextName) == null) {
|
||||
theState.enteringNewElement(null, nextName);
|
||||
parseAlternates(nextValue, theState, alternateName, alternateName);
|
||||
theState.endingElement();
|
||||
}
|
||||
} else {
|
||||
getErrorHandler().incorrectJsonType(null, alternateName, ValueType.OBJECT, null, nextValue.getJsonType(), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
theState.endingElement();
|
||||
}
|
||||
}
|
||||
|
||||
private void parseFhirComments(JsonLikeValue theObject, ParserState<?> theState) {
|
||||
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,
|
||||
List<HeldExtension> modifierExtensions) throws IOException {
|
||||
List<HeldExtension> modifierExtensions) throws IOException {
|
||||
if (extensions.isEmpty() == false) {
|
||||
beginArray(theEventWriter, "extension");
|
||||
for (HeldExtension next : extensions) {
|
||||
|
@ -1328,6 +1321,28 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
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 {
|
||||
if (myUndeclaredExtension != null) {
|
||||
writeUndeclaredExtension(theResDef, theResource, theEventWriter, myUndeclaredExtension);
|
||||
|
@ -1341,7 +1356,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
/*
|
||||
* This makes sure that even if the extension contains a reference to a contained
|
||||
* resource which has a HAPI-assigned ID we'll still encode that ID.
|
||||
*
|
||||
*
|
||||
* See #327
|
||||
*/
|
||||
List<? extends IBase> preProcessedValue = preProcessValues(myDef, theResource, Collections.singletonList(myValue), myChildElem);
|
||||
|
@ -1367,7 +1382,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
} else {
|
||||
String childName = myDef.getChildNameByDatatype(myValue.getClass());
|
||||
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();
|
||||
|
@ -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());
|
||||
}
|
||||
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.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;
|
||||
}
|
||||
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);
|
||||
push(state);
|
||||
} else {
|
||||
|
@ -1559,7 +1559,8 @@ class ParserState<T> {
|
|||
|
||||
if (theEvent.isEndElement()) {
|
||||
if (myDepth == 0) {
|
||||
myDt.setValue(myEvents);
|
||||
String eventsAsString = XmlUtil.encode(myEvents);
|
||||
myDt.setValue(eventsAsString);
|
||||
doPop();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
|
|||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
import java.io.Writer;
|
||||
import java.util.*;
|
||||
|
||||
|
@ -605,13 +606,16 @@ public class XmlParser extends BaseParser /* implements IParser */ {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private void encodeXhtml(XhtmlDt theDt, XMLStreamWriter theEventWriter) throws XMLStreamException {
|
||||
if (theDt == null || theDt.getValue() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<XMLEvent> events = XmlUtil.parse(theDt.getValue());
|
||||
boolean firstElement = true;
|
||||
for (XMLEvent event : theDt.getValue()) {
|
||||
|
||||
for (XMLEvent event : events) {
|
||||
switch (event.getEventType()) {
|
||||
case XMLStreamConstants.ATTRIBUTE:
|
||||
Attribute attr = (Attribute) event;
|
||||
|
|
|
@ -31,6 +31,16 @@ public class Constants {
|
|||
public static final String CHARSET_NAME_UTF8 = "UTF-8";
|
||||
public static final Charset CHARSET_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_NEW = "application/fhir+json";
|
||||
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 OO_INFOSTATUS_PROCESSING = "processing";
|
||||
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 {
|
||||
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(201, "Created");
|
||||
statusNames.put(202, "Accepted");
|
||||
|
@ -246,11 +257,31 @@ public class Constants {
|
|||
statusNames.put(511, "Network Authentication Required");
|
||||
HTTP_STATUS_NAMES = Collections.unmodifiableMap(statusNames);
|
||||
|
||||
Set<String> formatsHtml = new HashSet<String>();
|
||||
Set<String> formatsHtml = new HashSet<>();
|
||||
formatsHtml.add(CT_HTML);
|
||||
formatsHtml.add(FORMAT_HTML);
|
||||
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 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();
|
||||
|
||||
|
|
|
@ -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.util.LinkedHashSet;
|
||||
|
||||
/**
|
||||
* Provides server ports
|
||||
*/
|
||||
@CoverageIgnore
|
||||
public class PortUtil {
|
||||
private static LinkedHashSet<Integer> ourPorts = new LinkedHashSet<>();
|
||||
|
||||
/*
|
||||
* Non instantiable
|
||||
|
@ -41,9 +43,13 @@ public class PortUtil {
|
|||
public static int findFreePort() {
|
||||
ServerSocket server;
|
||||
try {
|
||||
server = new ServerSocket(0);
|
||||
int port = server.getLocalPort();
|
||||
server.close();
|
||||
int port;
|
||||
do {
|
||||
server = new ServerSocket(0);
|
||||
port = server.getLocalPort();
|
||||
server.close();
|
||||
} while (!ourPorts.add(port));
|
||||
|
||||
Thread.sleep(500);
|
||||
return port;
|
||||
} catch (Exception e) {
|
||||
|
|
|
@ -20,33 +20,34 @@ package ca.uhn.fhir.util;
|
|||
* #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.Modifier;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
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 static org.apache.commons.lang3.StringUtils.defaultString;
|
||||
|
||||
public class TestUtil {
|
||||
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>
|
||||
*
|
||||
* <p>
|
||||
* 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
|
||||
* static fields seems to solve this.
|
||||
*/
|
||||
public static void clearAllStaticFieldsForUnitTest() {
|
||||
HapiLocalizer.setOurFailOnMissingMessage(true);
|
||||
|
||||
|
||||
Class<?> theType;
|
||||
try {
|
||||
throw new Exception();
|
||||
|
@ -104,7 +105,7 @@ public class TestUtil {
|
|||
* environment
|
||||
*/
|
||||
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)]);
|
||||
ourLog.info("Tests are running in locale: " + Locale.getDefault().getDisplayName());
|
||||
if (Math.random() < 0.5) {
|
||||
|
@ -116,10 +117,19 @@ public class TestUtil {
|
|||
System.setProperty("file.encoding", "UTF-8");
|
||||
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)];
|
||||
TimeZone.setDefault(TimeZone.getTimeZone(timeZone));
|
||||
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%
|
||||
*/
|
||||
import java.io.*;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
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.codehaus.stax2.XMLOutputFactory2;
|
||||
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.IDependencyLog;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
|
||||
/**
|
||||
* Utility methods for working with the StAX API.
|
||||
*
|
||||
* This class contains code adapted from the Apache Axiom project.
|
||||
*/
|
||||
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 volatile boolean ourHaveLoggedStaxImplementation;
|
||||
private static volatile XMLInputFactory ourInputFactory;
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(XmlUtil.class);
|
||||
private static Throwable ourNextException;
|
||||
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 {
|
||||
HashMap<String, Integer> validEntityNames = new HashMap<String, Integer>(1448);
|
||||
|
@ -1517,7 +1519,7 @@ public class XmlUtil {
|
|||
}
|
||||
|
||||
XMLOutputFactory outputFactory = newOutputFactory();
|
||||
|
||||
|
||||
if (!ourHaveLoggedStaxImplementation) {
|
||||
logStaxImplementation(outputFactory.getClass());
|
||||
}
|
||||
|
@ -1545,7 +1547,7 @@ public class XmlUtil {
|
|||
|
||||
public static XMLEventReader createXmlReader(Reader reader) throws FactoryConfigurationError, XMLStreamException {
|
||||
throwUnitTestExceptionIfConfiguredToDoSo();
|
||||
|
||||
|
||||
XMLInputFactory inputFactory = getOrCreateInputFactory();
|
||||
|
||||
// Now.. create the reader and return it
|
||||
|
@ -1555,7 +1557,7 @@ public class XmlUtil {
|
|||
|
||||
public static XMLStreamWriter createXmlStreamWriter(Writer theWriter) throws FactoryConfigurationError, XMLStreamException {
|
||||
throwUnitTestExceptionIfConfiguredToDoSo();
|
||||
|
||||
|
||||
XMLOutputFactory outputFactory = getOrCreateOutputFactory();
|
||||
XMLStreamWriter retVal = outputFactory.createXMLStreamWriter(theWriter);
|
||||
return retVal;
|
||||
|
@ -1567,6 +1569,30 @@ public class XmlUtil {
|
|||
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 {
|
||||
XMLOutputFactory retVal = ourFragmentOutputFactory;
|
||||
if (retVal == null) {
|
||||
|
@ -1588,7 +1614,7 @@ public class XmlUtil {
|
|||
} catch (ClassNotFoundException e) {
|
||||
// ok
|
||||
}
|
||||
|
||||
|
||||
XMLInputFactory inputFactory = newInputFactory();
|
||||
|
||||
if (!ourHaveLoggedStaxImplementation) {
|
||||
|
@ -1596,20 +1622,20 @@ public class XmlUtil {
|
|||
}
|
||||
|
||||
/*
|
||||
* These two properties disable external entity processing, which can
|
||||
* These two properties disable external entity processing, which can
|
||||
* be a security vulnerability.
|
||||
*
|
||||
*
|
||||
* See https://github.com/jamesagnew/hapi-fhir/issues/339
|
||||
* https://www.owasp.org/index.php/XML_External_Entity_%28XXE%29_Processing
|
||||
*/
|
||||
inputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false); // This disables DTDs entirely for that factory
|
||||
inputFactory.setProperty("javax.xml.stream.isSupportingExternalEntities", false); // disable external entities
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* In the following few lines, you can uncomment the first and comment the second to disable automatic
|
||||
* parsing of extended entities, e.g. §
|
||||
*
|
||||
*
|
||||
* Note that these properties are Woodstox specific and they cause a crash in environments where SJSXP is
|
||||
* being used (e.g. glassfish) so we don't set them there.
|
||||
*/
|
||||
|
@ -1652,6 +1678,7 @@ public class XmlUtil {
|
|||
return ourOutputFactory;
|
||||
}
|
||||
|
||||
|
||||
private static void logStaxImplementation(Class<?> theClass) {
|
||||
IDependencyLog logger = DependencyLogFactory.createJarLogger();
|
||||
if (logger != null) {
|
||||
|
@ -1683,6 +1710,49 @@ public class XmlUtil {
|
|||
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
|
||||
*/
|
||||
|
@ -1711,26 +1781,6 @@ public class XmlUtil {
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
|
||||
|
|
|
@ -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))
|
||||
.execute();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new ParseException("This command does not support FHIR version " + ctx.getVersion().getVersion());
|
||||
}
|
||||
|
|
|
@ -1,28 +1,36 @@
|
|||
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.FhirVersionEnum;
|
||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
||||
import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2;
|
||||
import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2;
|
||||
import ca.uhn.fhir.jpa.provider.dstu3.*;
|
||||
import ca.uhn.fhir.jpa.provider.r4.*;
|
||||
import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3;
|
||||
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.resource.Bundle;
|
||||
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||
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.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 {
|
||||
|
||||
|
@ -134,19 +142,7 @@ public class JpaServerDemo extends RestfulServer {
|
|||
setPagingProvider(new FifoMemoryPagingProvider(10));
|
||||
|
||||
// Register a CORS filter
|
||||
CorsConfiguration config = new CorsConfiguration();
|
||||
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"));
|
||||
CorsInterceptor corsInterceptor = new CorsInterceptor();
|
||||
registerInterceptor(corsInterceptor);
|
||||
|
||||
/*
|
||||
|
|
|
@ -59,6 +59,11 @@
|
|||
</dependency>
|
||||
|
||||
<!-- Unit test dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.codehaus.woodstox</groupId>
|
||||
<artifactId>woodstox-core-asl</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
|
@ -125,4 +130,4 @@
|
|||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
</project>
|
||||
|
|
|
@ -98,24 +98,23 @@ public class ApacheHttpResponse implements IHttpResponse {
|
|||
}
|
||||
if (charset == null) {
|
||||
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");
|
||||
}
|
||||
|
||||
Reader reader = new InputStreamReader(readEntity(), charset);
|
||||
return reader;
|
||||
return new InputStreamReader(readEntity(), charset);
|
||||
}
|
||||
|
||||
@Override
|
||||
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) {
|
||||
for (Header next : myResponse.getAllHeaders()) {
|
||||
String name = next.getName().toLowerCase();
|
||||
List<String> list = headers.get(name);
|
||||
if (list == null) {
|
||||
list = new ArrayList<String>();
|
||||
list = new ArrayList<>();
|
||||
headers.put(name, list);
|
||||
}
|
||||
list.add(next.getValue());
|
||||
|
@ -131,7 +130,7 @@ public class ApacheHttpResponse implements IHttpResponse {
|
|||
if (headers == null) {
|
||||
headers = new Header[0];
|
||||
}
|
||||
List<String> retVal = new ArrayList<String>();
|
||||
List<String> retVal = new ArrayList<>();
|
||||
for (Header next : headers) {
|
||||
retVal.add(next.getValue());
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ import java.util.Map;
|
|||
import java.util.Set;
|
||||
|
||||
import ca.uhn.fhir.rest.api.CacheControlDirective;
|
||||
import ca.uhn.fhir.util.XmlDetectionUtil;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
@ -110,7 +111,7 @@ public abstract class BaseClient implements IRestfulClient {
|
|||
setKeepResponses(true);
|
||||
}
|
||||
|
||||
if (XmlUtil.isStaxPresent() == false) {
|
||||
if (XmlDetectionUtil.isStaxPresent() == false) {
|
||||
myEncoding = EncodingEnum.JSON;
|
||||
}
|
||||
|
||||
|
|
|
@ -330,6 +330,8 @@ public abstract class RestfulClientFactory implements IRestfulClientFactory {
|
|||
serverFhirVersionEnum = FhirVersionEnum.DSTU2_1;
|
||||
} else if (serverFhirVersionString.equals(FhirVersionEnum.DSTU3.getFhirVersionString())) {
|
||||
serverFhirVersionEnum = FhirVersionEnum.DSTU3;
|
||||
} else if (serverFhirVersionString.equals(FhirVersionEnum.R4.getFhirVersionString())) {
|
||||
serverFhirVersionEnum = FhirVersionEnum.R4;
|
||||
} else {
|
||||
// we'll be lenient and accept this
|
||||
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();
|
||||
copyElement(src, tgt);
|
||||
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())
|
||||
tgt.setDescription(src.getDescription());
|
||||
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>
|
||||
|
||||
<!-- 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>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
|
@ -139,7 +144,7 @@
|
|||
<artifactId>jetty-util</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>net.sf.json-lib</groupId>
|
||||
<artifactId>json-lib</artifactId>
|
||||
|
@ -225,14 +230,14 @@
|
|||
<artifactId>spring-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--
|
||||
<!--
|
||||
For some reason JavaDoc crashed during site generation unless we have this dependency
|
||||
-->
|
||||
<dependency>
|
||||
<groupId>javax.interceptor</groupId>
|
||||
<artifactId>javax.interceptor-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.interceptor</groupId>
|
||||
<artifactId>javax.interceptor-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
|
@ -253,6 +258,13 @@
|
|||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.basepom.maven</groupId>
|
||||
<artifactId>duplicate-finder-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
|
@ -285,19 +297,17 @@
|
|||
</fileSets>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>post-integration-test</id>
|
||||
<phase>install</phase>
|
||||
<goals>
|
||||
<goal>report</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<dataFile>${project.build.directory}/jacoco.exec</dataFile>
|
||||
<outputDirectory>${project.reporting.outputDirectory}/jacoco-report</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
||||
|
||||
<execution>
|
||||
<id>post-integration-test</id>
|
||||
<phase>install</phase>
|
||||
<goals>
|
||||
<goal>report</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<dataFile>${project.build.directory}/jacoco.exec</dataFile>
|
||||
<outputDirectory>${project.reporting.outputDirectory}/jacoco-report</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
|
|
|
@ -74,6 +74,11 @@
|
|||
</dependency>
|
||||
|
||||
<!-- Unit test dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.codehaus.woodstox</groupId>
|
||||
<artifactId>woodstox-core-asl</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
|
|
|
@ -34,13 +34,15 @@ import ca.uhn.fhir.rest.client.impl.RestfulClientFactory;
|
|||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public class JaxRsRestfulClientFactory extends RestfulClientFactory {
|
||||
|
||||
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!
|
||||
*/
|
||||
|
@ -64,6 +66,12 @@ public class JaxRsRestfulClientFactory extends RestfulClientFactory {
|
|||
myNativeClient = builder.build();
|
||||
}
|
||||
|
||||
if (registeredComponents != null && !registeredComponents.isEmpty()) {
|
||||
for (Class<?> c : registeredComponents) {
|
||||
myNativeClient = myNativeClient.register(c);
|
||||
}
|
||||
}
|
||||
|
||||
return myNativeClient;
|
||||
}
|
||||
|
||||
|
@ -73,29 +81,46 @@ public class JaxRsRestfulClientFactory extends RestfulClientFactory {
|
|||
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
|
||||
public void setProxy(String theHost, Integer thePort) {
|
||||
throw new UnsupportedOperationException("Proxies are not supported yet in JAX-RS client");
|
||||
}
|
||||
|
||||
/**
|
||||
* Only accept clients of type javax.ws.rs.client.Client
|
||||
*
|
||||
* @param theHttpClient
|
||||
*/
|
||||
|
||||
/**
|
||||
* Only accept clients of type javax.ws.rs.client.Client
|
||||
* Can be used to set a specific Client implementation
|
||||
* @param theHttpClient
|
||||
*/
|
||||
@Override
|
||||
public synchronized void setHttpClient(Object 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
|
||||
protected JaxRsHttpClient getHttpClient(String theServerBase) {
|
||||
return new JaxRsHttpClient(getNativeClientClient(), new StringBuilder(theServerBase), null, null, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resetHttpClient() {
|
||||
this.myNativeClient = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resetHttpClient() {
|
||||
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>
|
||||
|
||||
<parent>
|
||||
|
@ -32,6 +32,12 @@
|
|||
<version>3.5</version>
|
||||
</dependency>
|
||||
-->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.codehaus.woodstox</groupId>
|
||||
<artifactId>woodstox-core-asl</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>net.sf.saxon</groupId>
|
||||
<artifactId>Saxon-HE</artifactId>
|
||||
|
@ -105,10 +111,12 @@
|
|||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!--
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jcl-over-slf4j</artifactId>
|
||||
</dependency>
|
||||
-->
|
||||
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
|
@ -163,7 +171,7 @@
|
|||
<artifactId>thymeleaf-spring4</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- For UCUM -->
|
||||
<!-- For UCUM: TODO we should replace this with org.fhir UCUM -->
|
||||
<dependency>
|
||||
<groupId>org.jscience</groupId>
|
||||
<artifactId>jscience</artifactId>
|
||||
|
@ -233,10 +241,12 @@
|
|||
<!-- <dependency> <groupId>org.hsqldb</groupId> <artifactId>hsqldb</artifactId> <version>2.3.2</version> </dependency> -->
|
||||
|
||||
<!-- Spring -->
|
||||
<!--
|
||||
<dependency>
|
||||
<groupId>aopalliance</groupId>
|
||||
<artifactId>aopalliance</artifactId>
|
||||
</dependency>
|
||||
-->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
|
@ -358,6 +368,7 @@
|
|||
<dependency>
|
||||
<groupId>javax.mail</groupId>
|
||||
<artifactId>javax.mail-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<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.sp.ISearchParamPresenceSvc;
|
||||
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.websocket.SubscriptionWebsocketInterceptor;
|
||||
import org.hl7.fhir.utilities.graphql.IGraphQLStorageServices;
|
||||
|
@ -93,15 +94,6 @@ public class BaseConfig implements SchedulingConfigurer {
|
|||
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
|
||||
@Lazy
|
||||
public SubscriptionRestHookInterceptor subscriptionRestHookInterceptor() {
|
||||
|
@ -114,15 +106,23 @@ public class BaseConfig implements SchedulingConfigurer {
|
|||
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
|
||||
public TaskScheduler taskScheduler() {
|
||||
ConcurrentTaskScheduler retVal = new ConcurrentTaskScheduler();
|
||||
retVal.setConcurrentExecutor(scheduledExecutorService().getObject());
|
||||
retVal.setScheduledExecutor(scheduledExecutorService().getObject());
|
||||
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.term.IHapiTerminologySvc;
|
||||
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.base.composite.BaseCodingDt;
|
||||
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.ActionRequestDetails;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import ca.uhn.fhir.util.CoverageIgnore;
|
||||
import ca.uhn.fhir.util.FhirTerser;
|
||||
import ca.uhn.fhir.util.OperationOutcomeUtil;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
import ca.uhn.fhir.util.*;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.Sets;
|
||||
|
@ -2044,7 +2040,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
StringBuilder b = new StringBuilder();
|
||||
if (theResource instanceof IResource) {
|
||||
IResource resource = (IResource) theResource;
|
||||
List<XMLEvent> xmlEvents = resource.getText().getDiv().getValue();
|
||||
List<XMLEvent> xmlEvents = XmlUtil.parse(resource.getText().getDiv().getValue());
|
||||
if (xmlEvents != null) {
|
||||
for (XMLEvent next : xmlEvents) {
|
||||
if (next.isCharacters()) {
|
||||
|
@ -2057,8 +2053,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
IDomainResource resource = (IDomainResource) theResource;
|
||||
try {
|
||||
String divAsString = resource.getText().getDivAsString();
|
||||
XhtmlDt xhtml = new XhtmlDt(divAsString);
|
||||
List<XMLEvent> xmlEvents = xhtml.getValue();
|
||||
List<XMLEvent> xmlEvents = XmlUtil.parse(divAsString);
|
||||
if (xmlEvents != null) {
|
||||
for (XMLEvent next : xmlEvents) {
|
||||
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.entity.*;
|
||||
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.StopWatch;
|
||||
import ca.uhn.fhir.jpa.util.jsonpatch.JsonPatchUtils;
|
||||
|
@ -61,6 +62,7 @@ import org.springframework.transaction.annotation.Transactional;
|
|||
import javax.annotation.PostConstruct;
|
||||
import javax.persistence.NoResultException;
|
||||
import javax.persistence.TypedQuery;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.*;
|
||||
|
||||
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)
|
||||
@Override
|
||||
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) {
|
||||
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));
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
@ -108,6 +108,7 @@ public class DaoConfig {
|
|||
private Set<String> myTreatReferencesAsLogical = new HashSet<String>(DEFAULT_LOGICAL_BASE_URLS);
|
||||
private boolean myAutoCreatePlaceholderReferenceTargets;
|
||||
private Integer myCacheControlNoStoreMaxResultsUpperLimit = 1000;
|
||||
private Integer myCountSearchResultsUpTo = null;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -152,6 +153,56 @@ public class DaoConfig {
|
|||
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,
|
||||
* 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.
|
||||
*/
|
||||
public void setInterceptors(IServerInterceptor... theInterceptor) {
|
||||
setInterceptors(new ArrayList<IServerInterceptor>());
|
||||
if (theInterceptor != null && theInterceptor.length != 0) {
|
||||
getInterceptors().addAll(Arrays.asList(theInterceptor));
|
||||
}
|
||||
public void setInterceptors(List<IServerInterceptor> theInterceptors) {
|
||||
myInterceptors = theInterceptors;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -959,8 +1007,11 @@ public class DaoConfig {
|
|||
/**
|
||||
* This may be used to optionally register server interceptors directly against the DAOs.
|
||||
*/
|
||||
public void setInterceptors(List<IServerInterceptor> theInterceptors) {
|
||||
myInterceptors = theInterceptors;
|
||||
public void setInterceptors(IServerInterceptor... theInterceptor) {
|
||||
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.server.exceptions.InvalidRequestException;
|
||||
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 {
|
||||
|
||||
|
@ -179,6 +183,9 @@ public interface IFhirResourceDao<T extends IBaseResource> extends IDao {
|
|||
|
||||
IBundleProvider search(SearchParameterMap theParams, RequestDetails theRequestDetails);
|
||||
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
IBundleProvider search(SearchParameterMap theParams, RequestDetails theRequestDetails, HttpServletResponse theServletResponse);
|
||||
|
||||
Set<Long> searchForIds(SearchParameterMap theParams);
|
||||
|
||||
/**
|
||||
|
|
|
@ -274,14 +274,6 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
|
|||
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) {
|
||||
myRevIncludes = theRevIncludes;
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import java.util.Map.Entry;
|
|||
import javax.persistence.TypedQuery;
|
||||
|
||||
import ca.uhn.fhir.jpa.util.StopWatch;
|
||||
import ca.uhn.fhir.rest.param.ParameterUtil;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.hibernate.Session;
|
||||
|
@ -438,7 +439,11 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
|
|||
DaoMethodOutcome outcome;
|
||||
UrlParts parts = UrlUtil.parseUrl(url);
|
||||
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);
|
||||
} else {
|
||||
res.setId((String) null);
|
||||
|
|
|
@ -215,6 +215,9 @@ public class SearchParamRegistryDstu3 extends BaseSearchParamRegistry {
|
|||
case RETIRED:
|
||||
status = RuntimeSearchParamStatusEnum.RETIRED;
|
||||
break;
|
||||
case UNKNOWN:
|
||||
status = RuntimeSearchParamStatusEnum.UNKNOWN;
|
||||
break;
|
||||
case NULL:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ import java.util.Map.Entry;
|
|||
|
||||
import javax.persistence.TypedQuery;
|
||||
|
||||
import ca.uhn.fhir.rest.param.ParameterUtil;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.hl7.fhir.r4.model.*;
|
||||
|
@ -435,7 +436,11 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
|
|||
DaoMethodOutcome outcome;
|
||||
UrlParts parts = UrlUtil.parseUrl(url);
|
||||
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);
|
||||
} else {
|
||||
res.setId((String) null);
|
||||
|
|
|
@ -216,6 +216,9 @@ public class SearchParamRegistryR4 extends BaseSearchParamRegistry {
|
|||
case RETIRED:
|
||||
status = RuntimeSearchParamStatusEnum.RETIRED;
|
||||
break;
|
||||
case UNKNOWN:
|
||||
status = RuntimeSearchParamStatusEnum.UNKNOWN;
|
||||
break;
|
||||
case NULL:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -115,6 +115,8 @@ public class JpaStorageServices extends BaseHapiFhirDao<IBaseResource> implement
|
|||
break;
|
||||
case URI:
|
||||
break;
|
||||
case HAS:
|
||||
break;
|
||||
}
|
||||
|
||||
params.add(nextArgument.getName(), param);
|
||||
|
|
|
@ -24,6 +24,7 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
|
|||
import java.util.*;
|
||||
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.Qualifier;
|
||||
|
||||
|
@ -47,7 +48,7 @@ public class JpaSystemProviderDstu2 extends BaseJpaSystemProviderDstu2Plus<Bundl
|
|||
@Qualifier("mySystemDaoDstu2")
|
||||
private IFhirSystemDao<Bundle, MetaDt> mySystemDao;
|
||||
|
||||
@Autowired
|
||||
@Autowired(required = false)
|
||||
private IFulltextSearchSvc mySearchDao;
|
||||
|
||||
//@formatter:off
|
||||
|
@ -178,7 +179,8 @@ public class JpaSystemProviderDstu2 extends BaseJpaSystemProviderDstu2Plus<Bundl
|
|||
@OperationParam(name="searchParam", min=1, max=1) String theSearchParam,
|
||||
@OperationParam(name="text", min=1, max=1) String theText
|
||||
) {
|
||||
|
||||
JpaSystemProviderDstu3.validateFulltextSearchEnabled(mySearchDao);
|
||||
|
||||
if (isBlank(theContext)) {
|
||||
throw new InvalidRequestException("Parameter 'context' must be provided");
|
||||
}
|
||||
|
@ -188,16 +190,14 @@ public class JpaSystemProviderDstu2 extends BaseJpaSystemProviderDstu2Plus<Bundl
|
|||
if (isBlank(theText)) {
|
||||
throw new InvalidRequestException("Parameter 'text' must be provided");
|
||||
}
|
||||
|
||||
|
||||
List<Suggestion> keywords = mySearchDao.suggestKeywords(theContext, theSearchParam, theText);
|
||||
|
||||
Parameters retVal = new Parameters();
|
||||
for (Suggestion next : keywords) {
|
||||
//@formatter:off
|
||||
retVal.addParameter()
|
||||
.addPart(new Parameter().setName("keyword").setValue(new StringDt(next.getTerm())))
|
||||
.addPart(new Parameter().setName("score").setValue(new DecimalDt(next.getScore())));
|
||||
//@formatter:on
|
||||
}
|
||||
|
||||
return retVal;
|
||||
|
|
|
@ -1,5 +1,27 @@
|
|||
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;
|
||||
|
||||
/*
|
||||
|
@ -21,23 +43,6 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
|
|||
* limitations under the License.
|
||||
* #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> {
|
||||
|
||||
|
@ -45,122 +50,122 @@ public class JpaSystemProviderDstu3 extends BaseJpaSystemProviderDstu2Plus<Bundl
|
|||
@Qualifier("mySystemDaoDstu3")
|
||||
private IFhirSystemDao<Bundle, Meta> mySystemDao;
|
||||
|
||||
@Autowired
|
||||
@Autowired(required = false)
|
||||
private IFulltextSearchSvc mySearchDao;
|
||||
|
||||
|
||||
//@formatter:off
|
||||
// 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),/"
|
||||
@Operation(name="$get-resource-counts", idempotent=true, returnParameters= {
|
||||
@OperationParam(name="AllergyIntolerance", 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="AuditEvent", 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="BodySite", 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="CarePlan2", 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="ClinicalImpression", 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="Composition", 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="Conformance", 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="Coverage", 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="DeviceComponent", 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="DeviceUseStatement", 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="DocumentManifest", 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="EligibilityResponse", 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="EnrollmentResponse", 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="FamilyMemberHistory", 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="Group", 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="ImagingStudy", 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="ListResource", 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="Medication", 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="MedicationPrescription", 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="NamingSystem", 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="OperationDefinition", 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="OrderResponse", 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="Patient", 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="Person", 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="ProcedureRequest", 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="Provenance", 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="ReferralRequest", 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="Schedule", 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="Specimen", 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="Substance", 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="VisionPrescription", type=IntegerType.class, min=0, max=1)
|
||||
@Operation(name = "$get-resource-counts", idempotent = true, returnParameters = {
|
||||
@OperationParam(name = "AllergyIntolerance", 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 = "AuditEvent", 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 = "BodySite", 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 = "CarePlan2", 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 = "ClinicalImpression", 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 = "Composition", 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 = "Conformance", 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 = "Coverage", 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 = "DeviceComponent", 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 = "DeviceUseStatement", 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 = "DocumentManifest", 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 = "EligibilityResponse", 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 = "EnrollmentResponse", 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 = "FamilyMemberHistory", 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 = "Group", 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 = "ImagingStudy", 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 = "ListResource", 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 = "Medication", 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 = "MedicationPrescription", 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 = "NamingSystem", 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 = "OperationDefinition", 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 = "OrderResponse", 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 = "Patient", 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 = "Person", 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 = "ProcedureRequest", 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 = "Provenance", 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 = "ReferralRequest", 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 = "Schedule", 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 = "Specimen", 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 = "Substance", 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 = "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
|
||||
public Parameters getResourceCounts() {
|
||||
Parameters retVal = new Parameters();
|
||||
|
||||
|
||||
Map<String, Long> counts = mySystemDao.getResourceCounts();
|
||||
counts = new TreeMap<String, Long>(counts);
|
||||
for (Entry<String, Long> nextEntry : counts.entrySet()) {
|
||||
retVal.addParameter().setName((nextEntry.getKey())).setValue(new IntegerType(nextEntry.getValue().intValue()));
|
||||
}
|
||||
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
//@formatter:off
|
||||
@Operation(name="$meta", idempotent=true, returnParameters= {
|
||||
@OperationParam(name="return", type=Meta.class)
|
||||
@Operation(name = "$meta", idempotent = true, returnParameters = {
|
||||
@OperationParam(name = "return", type = Meta.class)
|
||||
})
|
||||
//@formatter:on
|
||||
public Parameters meta(RequestDetails theRequestDetails) {
|
||||
|
@ -168,14 +173,15 @@ public class JpaSystemProviderDstu3 extends BaseJpaSystemProviderDstu2Plus<Bundl
|
|||
parameters.addParameter().setName("return").setValue(getDao().metaGetOperation(theRequestDetails));
|
||||
return parameters;
|
||||
}
|
||||
|
||||
@Operation(name="$suggest-keywords", idempotent=true)
|
||||
|
||||
@Operation(name = "$suggest-keywords", idempotent = true)
|
||||
public Parameters suggestKeywords(
|
||||
@OperationParam(name="context", min=1, max=1) String theContext,
|
||||
@OperationParam(name="searchParam", min=1, max=1) String theSearchParam,
|
||||
@OperationParam(name="text", min=1, max=1) String theText
|
||||
) {
|
||||
|
||||
@OperationParam(name = "context", min = 1, max = 1) String theContext,
|
||||
@OperationParam(name = "searchParam", min = 1, max = 1) String theSearchParam,
|
||||
@OperationParam(name = "text", min = 1, max = 1) String theText
|
||||
) {
|
||||
|
||||
|
||||
if (isBlank(theContext)) {
|
||||
throw new InvalidRequestException("Parameter 'context' must be provided");
|
||||
}
|
||||
|
@ -185,21 +191,21 @@ public class JpaSystemProviderDstu3 extends BaseJpaSystemProviderDstu2Plus<Bundl
|
|||
if (isBlank(theText)) {
|
||||
throw new InvalidRequestException("Parameter 'text' must be provided");
|
||||
}
|
||||
|
||||
|
||||
List<Suggestion> keywords = mySearchDao.suggestKeywords(theContext, theSearchParam, theText);
|
||||
|
||||
|
||||
Parameters retVal = new Parameters();
|
||||
for (Suggestion next : keywords) {
|
||||
//@formatter:off
|
||||
retVal.addParameter()
|
||||
.addPart(new ParametersParameterComponent().setName("keyword").setValue(new StringType(next.getTerm())))
|
||||
.addPart(new ParametersParameterComponent().setName("score").setValue(new DecimalType(next.getScore())));
|
||||
.addPart(new ParametersParameterComponent().setName("keyword").setValue(new StringType(next.getTerm())))
|
||||
.addPart(new ParametersParameterComponent().setName("score").setValue(new DecimalType(next.getScore())));
|
||||
//@formatter:on
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
@Transaction
|
||||
public Bundle transaction(RequestDetails theRequestDetails, @TransactionParam Bundle theResources) {
|
||||
startRequest(((ServletRequestDetails) theRequestDetails).getServletRequest());
|
||||
|
@ -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")
|
||||
private IFhirSystemDao<Bundle, Meta> mySystemDao;
|
||||
|
||||
@Autowired
|
||||
@Autowired(required = false)
|
||||
private IFulltextSearchSvc mySearchDao;
|
||||
|
||||
//@formatter:off
|
||||
|
@ -175,6 +175,7 @@ public class JpaSystemProviderR4 extends BaseJpaSystemProviderDstu2Plus<Bundle,
|
|||
@OperationParam(name="searchParam", min=1, max=1) String theSearchParam,
|
||||
@OperationParam(name="text", min=1, max=1) String theText
|
||||
) {
|
||||
ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3.validateFulltextSearchEnabled(mySearchDao);
|
||||
|
||||
if (isBlank(theContext)) {
|
||||
throw new InvalidRequestException("Parameter 'context' must be provided");
|
||||
|
|
|
@ -19,23 +19,32 @@ package ca.uhn.fhir.jpa.search;
|
|||
* limitations under the License.
|
||||
* #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.jpa.dao.IDao;
|
||||
import ca.uhn.fhir.jpa.dao.ISearchBuilder;
|
||||
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.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 {
|
||||
|
||||
|
@ -47,6 +56,7 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
|
|||
private ISearchDao mySearchDao;
|
||||
private Search mySearchEntity;
|
||||
private String myUuid;
|
||||
private boolean myCacheHit;
|
||||
|
||||
public PersistedJpaBundleProvider(String theSearchUuid, IDao theDao) {
|
||||
myUuid = theSearchUuid;
|
||||
|
@ -179,17 +189,17 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
|
|||
});
|
||||
|
||||
switch (mySearchEntity.getSearchType()) {
|
||||
case HISTORY:
|
||||
return template.execute(new TransactionCallback<List<IBaseResource>>() {
|
||||
@Override
|
||||
public List<IBaseResource> doInTransaction(TransactionStatus theStatus) {
|
||||
return doHistoryInTransaction(theFromIndex, theToIndex);
|
||||
}
|
||||
});
|
||||
case SEARCH:
|
||||
case EVERYTHING:
|
||||
default:
|
||||
return doSearchOrEverything(theFromIndex, theToIndex);
|
||||
case HISTORY:
|
||||
return template.execute(new TransactionCallback<List<IBaseResource>>() {
|
||||
@Override
|
||||
public List<IBaseResource> doInTransaction(TransactionStatus theStatus) {
|
||||
return doHistoryInTransaction(theFromIndex, theToIndex);
|
||||
}
|
||||
});
|
||||
case SEARCH:
|
||||
case EVERYTHING:
|
||||
default:
|
||||
return doSearchOrEverything(theFromIndex, theToIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -197,6 +207,14 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
|
|||
return myUuid;
|
||||
}
|
||||
|
||||
public boolean isCacheHit() {
|
||||
return myCacheHit;
|
||||
}
|
||||
|
||||
public void setCacheHit(boolean theCacheHit) {
|
||||
myCacheHit = theCacheHit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer preferredPageSize() {
|
||||
ensureSearchEntityLoaded();
|
||||
|
|
|
@ -67,8 +67,11 @@ public class PersistedJpaSearchFirstPageBundleProvider extends PersistedJpaBundl
|
|||
|
||||
@Override
|
||||
public Integer size() {
|
||||
mySearchTask.awaitInitialSync();
|
||||
Integer size = mySearchTask.awaitInitialSync();
|
||||
SearchCoordinatorSvcImpl.verifySearchHasntFailedOrThrowInternalErrorException(mySearch);
|
||||
if (size != null) {
|
||||
return 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
* #L%family
|
||||
*/
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
|
@ -184,6 +184,8 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
|||
StopWatch w = new StopWatch();
|
||||
final String searchUuid = UUID.randomUUID().toString();
|
||||
|
||||
ourLog.debug("Registering new search {}", searchUuid);
|
||||
|
||||
Class<? extends IBaseResource> resourceTypeClass = myContext.getResourceDefinition(theResourceType).getImplementingClass();
|
||||
final ISearchBuilder sb = theCallingDao.newSearchBuilder();
|
||||
sb.setType(resourceTypeClass, theResourceType);
|
||||
|
@ -204,6 +206,8 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
|||
|
||||
if (theParams.isLoadSynchronous() || loadSynchronousUpTo != null) {
|
||||
|
||||
ourLog.debug("Search {} is loading in synchronous mode", searchUuid);
|
||||
|
||||
// Execute the query and make sure we return distinct results
|
||||
TransactionTemplate txTemplate = new TransactionTemplate(myManagedTxManager);
|
||||
txTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRED);
|
||||
|
@ -281,6 +285,8 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
|||
mySearchDao.updateSearchLastReturned(searchToUse.getId(), new Date());
|
||||
|
||||
retVal = new PersistedJpaBundleProvider(searchToUse.getUuid(), theCallingDao);
|
||||
retVal.setCacheHit(true);
|
||||
|
||||
populateBundleProvider(retVal);
|
||||
}
|
||||
|
||||
|
@ -437,7 +443,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
|||
mySearchUuid = theSearchUuid;
|
||||
}
|
||||
|
||||
public void awaitInitialSync() {
|
||||
public Integer awaitInitialSync() {
|
||||
ourLog.trace("Awaiting initial sync");
|
||||
do {
|
||||
try {
|
||||
|
@ -449,6 +455,8 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
|||
}
|
||||
} while (mySearch.getStatus() == SearchStatusEnum.LOADING);
|
||||
ourLog.trace("Initial sync completed");
|
||||
|
||||
return mySearch.getTotalCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -507,6 +515,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
|||
}
|
||||
|
||||
myIdToSearchTask.remove(mySearch.getUuid());
|
||||
myInitialCollectionLatch.countDown();
|
||||
myCompletionLatch.countDown();
|
||||
return null;
|
||||
}
|
||||
|
@ -552,27 +561,27 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
|||
public List<Long> getResourcePids(int theFromIndex, int theToIndex) {
|
||||
ourLog.info("Requesting search PIDs from {}-{}", theFromIndex, theToIndex);
|
||||
|
||||
CountDownLatch latch = null;
|
||||
synchronized (mySyncedPids) {
|
||||
if (mySyncedPids.size() < theToIndex && mySearch.getStatus() == SearchStatusEnum.LOADING) {
|
||||
int latchSize = theToIndex - mySyncedPids.size();
|
||||
ourLog.trace("Registering latch to await {} results (want {} total)", latchSize, theToIndex);
|
||||
latch = new CountDownLatch(latchSize);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
boolean keepWaiting;
|
||||
do {
|
||||
synchronized (mySyncedPids) {
|
||||
keepWaiting = false;
|
||||
if (mySyncedPids.size() < theToIndex && mySearch.getStatus() == SearchStatusEnum.LOADING) {
|
||||
keepWaiting = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
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) {
|
||||
verifySearchHasntFailedOrThrowInternalErrorException(mySearch);
|
||||
|
||||
|
@ -585,6 +594,8 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
|||
}
|
||||
}
|
||||
|
||||
ourLog.info("Done syncing results", mySyncedPids.size());
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
@ -633,8 +644,8 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
|||
myUnsyncedPids.clear();
|
||||
|
||||
if (theResultIter.hasNext() == false) {
|
||||
mySearch.setStatus(SearchStatusEnum.FINISHED);
|
||||
mySearch.setTotalCount(myCountSaved);
|
||||
mySearch.setStatus(SearchStatusEnum.FINISHED);
|
||||
}
|
||||
}
|
||||
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%
|
||||
*/
|
||||
|
||||
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.springframework.beans.factory.annotation.Autowired;
|
||||
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.TransactionTemplate;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.dao.data.*;
|
||||
import ca.uhn.fhir.jpa.entity.Search;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Deletes old searches
|
||||
|
@ -46,35 +46,32 @@ import ca.uhn.fhir.jpa.entity.Search;
|
|||
public class StaleSearchDeletingSvcImpl implements IStaleSearchDeletingSvc {
|
||||
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 Long ourNowForUnitTests;
|
||||
/*
|
||||
* 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
|
||||
* the result is to be deleted
|
||||
*/
|
||||
private long myCutoffSlack = DEFAULT_CUTOFF_SLACK;
|
||||
|
||||
@Autowired
|
||||
private DaoConfig myDaoConfig;
|
||||
|
||||
@Autowired
|
||||
private ISearchDao mySearchDao;
|
||||
|
||||
@Autowired
|
||||
private ISearchIncludeDao mySearchIncludeDao;
|
||||
|
||||
@Autowired
|
||||
private ISearchResultDao mySearchResultDao;
|
||||
|
||||
@Autowired
|
||||
private PlatformTransactionManager myTransactionManager;
|
||||
|
||||
private void deleteSearch(final Long theSearchPid) {
|
||||
Search searchToDelete = mySearchDao.findOne(theSearchPid);
|
||||
ourLog.info("Deleting search {}/{} - Created[{}] -- Last returned[{}]", searchToDelete.getId(), searchToDelete.getUuid(), searchToDelete.getCreated(), searchToDelete.getSearchLastReturned());
|
||||
mySearchIncludeDao.deleteForSearch(searchToDelete.getId());
|
||||
mySearchResultDao.deleteForSearch(searchToDelete.getId());
|
||||
mySearchDao.delete(searchToDelete);
|
||||
if (searchToDelete != null) {
|
||||
ourLog.info("Deleting search {}/{} - Created[{}] -- Last returned[{}]", searchToDelete.getId(), searchToDelete.getUuid(), searchToDelete.getCreated(), searchToDelete.getSearchLastReturned());
|
||||
mySearchIncludeDao.deleteForSearch(searchToDelete.getId());
|
||||
mySearchResultDao.deleteForSearch(searchToDelete.getId());
|
||||
mySearchDao.delete(searchToDelete);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -85,7 +82,7 @@ public class StaleSearchDeletingSvcImpl implements IStaleSearchDeletingSvc {
|
|||
if (myDaoConfig.getReuseCachedSearchResultsForMillis() != null) {
|
||||
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);
|
||||
|
||||
|
@ -129,4 +126,19 @@ public class StaleSearchDeletingSvcImpl implements IStaleSearchDeletingSvc {
|
|||
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 {
|
||||
from = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_EMAIL_FROM);
|
||||
subjectTemplate = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_SUBJECT_TEMPLATE);
|
||||
bodyTemplate = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_BODY_TEMPLATE);
|
||||
} catch (FHIRException theE) {
|
||||
throw new ConfigurationException("Failed to extract subscription extension(s): " + theE.getMessage(), theE);
|
||||
}
|
||||
retVal.getEmailDetails().setFrom(from);
|
||||
retVal.getEmailDetails().setSubjectTemplate(subjectTemplate);
|
||||
retVal.getEmailDetails().setBodyTemplate(bodyTemplate);
|
||||
}
|
||||
|
||||
} catch (FHIRException theE) {
|
||||
|
@ -191,13 +189,11 @@ public abstract class BaseSubscriptionInterceptor<S extends IBaseResource> exten
|
|||
try {
|
||||
from = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_EMAIL_FROM);
|
||||
subjectTemplate = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_SUBJECT_TEMPLATE);
|
||||
bodyTemplate = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_BODY_TEMPLATE);
|
||||
} catch (FHIRException theE) {
|
||||
throw new ConfigurationException("Failed to extract subscription extension(s): " + theE.getMessage(), theE);
|
||||
}
|
||||
retVal.getEmailDetails().setFrom(from);
|
||||
retVal.getEmailDetails().setSubjectTemplate(subjectTemplate);
|
||||
retVal.getEmailDetails().setBodyTemplate(bodyTemplate);
|
||||
}
|
||||
|
||||
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) {
|
||||
mySubscriptionActivatingSubscriber = new SubscriptionActivatingSubscriber(getSubscriptionDao(), getChannelType(), this);
|
||||
mySubscriptionActivatingSubscriber = new SubscriptionActivatingSubscriber(getSubscriptionDao(), getChannelType(), this, myTxManager);
|
||||
}
|
||||
|
||||
registerSubscriptionCheckingSubscriber();
|
||||
|
|
|
@ -131,10 +131,12 @@ public class CanonicalSubscription implements Serializable {
|
|||
return myHeaders;
|
||||
}
|
||||
|
||||
public void setHeaders(String theHeaders) {
|
||||
public void setHeaders(List<? extends IPrimitiveType<String>> theHeader) {
|
||||
myHeaders = new ArrayList<>();
|
||||
if (isNotBlank(theHeaders)) {
|
||||
myHeaders.add(theHeaders);
|
||||
for (IPrimitiveType<String> next : theHeader) {
|
||||
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<>();
|
||||
for (IPrimitiveType<String> next : theHeader) {
|
||||
if (isNotBlank(next.getValueAsString())) {
|
||||
myHeaders.add(next.getValueAsString());
|
||||
}
|
||||
if (isNotBlank(theHeaders)) {
|
||||
myHeaders.add(theHeaders);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -212,16 +212,6 @@ public class CanonicalSubscription implements Serializable {
|
|||
private String myFrom;
|
||||
@JsonProperty("subjectTemplate")
|
||||
private String mySubjectTemplate;
|
||||
@JsonProperty("bodyTemplate")
|
||||
private String myBodyTemplate;
|
||||
|
||||
public String getBodyTemplate() {
|
||||
return myBodyTemplate;
|
||||
}
|
||||
|
||||
public void setBodyTemplate(String theBodyTemplate) {
|
||||
myBodyTemplate = theBodyTemplate;
|
||||
}
|
||||
|
||||
public String getFrom() {
|
||||
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.IPrimitiveType;
|
||||
import org.hl7.fhir.r4.model.Subscription;
|
||||
import org.hl7.fhir.utilities.ucum.Canonical;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.messaging.Message;
|
||||
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.TransactionSynchronizationManager;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class SubscriptionActivatingSubscriber {
|
||||
private final IFhirResourceDao mySubscriptionDao;
|
||||
private final BaseSubscriptionInterceptor mySubscriptionInterceptor;
|
||||
private final PlatformTransactionManager myTransactionManager;
|
||||
private Logger ourLog = LoggerFactory.getLogger(SubscriptionActivatingSubscriber.class);
|
||||
private FhirContext myCtx;
|
||||
private Subscription.SubscriptionChannelType myChannelType;
|
||||
|
@ -48,11 +49,12 @@ public class SubscriptionActivatingSubscriber {
|
|||
/**
|
||||
* 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;
|
||||
mySubscriptionInterceptor = theSubscriptionInterceptor;
|
||||
myChannelType = theChannelType;
|
||||
myCtx = theSubscriptionDao.getContext();
|
||||
myTransactionManager = theTransactionManager;
|
||||
}
|
||||
|
||||
public void activateAndRegisterSubscriptionIfRequired(final IBaseResource theSubscription) {
|
||||
|
@ -67,15 +69,16 @@ public class SubscriptionActivatingSubscriber {
|
|||
final String requestedStatus = Subscription.SubscriptionStatus.REQUESTED.toCode();
|
||||
final String activeStatus = Subscription.SubscriptionStatus.ACTIVE.toCode();
|
||||
if (requestedStatus.equals(statusString)) {
|
||||
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
|
||||
@Override
|
||||
public void afterCommit() {
|
||||
status.setValueAsString(activeStatus);
|
||||
ourLog.info("Activating and registering subscription {} from status {} to {}", theSubscription.getIdElement().toUnqualified().getValue(), requestedStatus, activeStatus);
|
||||
mySubscriptionDao.update(theSubscription);
|
||||
mySubscriptionInterceptor.registerSubscription(theSubscription.getIdElement(), theSubscription);
|
||||
}
|
||||
});
|
||||
if (TransactionSynchronizationManager.isSynchronizationActive()) {
|
||||
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
|
||||
@Override
|
||||
public void afterCommit() {
|
||||
activateSubscription(status, activeStatus, theSubscription, requestedStatus);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
activateSubscription(status, activeStatus, theSubscription, requestedStatus);
|
||||
}
|
||||
} else if (activeStatus.equals(statusString)) {
|
||||
if (!mySubscriptionInterceptor.hasSubscription(theSubscription.getIdElement())) {
|
||||
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) {
|
||||
case DELETE:
|
||||
|
@ -101,7 +111,15 @@ public class SubscriptionActivatingSubscriber {
|
|||
if (!theId.getResourceType().equals("Subscription")) {
|
||||
return;
|
||||
}
|
||||
activateAndRegisterSubscriptionIfRequired(theSubscription);
|
||||
TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager);
|
||||
txTemplate.execute(new TransactionCallbackWithoutResult() {
|
||||
@Override
|
||||
protected void doInTransactionWithoutResult(TransactionStatus status) {
|
||||
activateAndRegisterSubscriptionIfRequired(theSubscription);
|
||||
}
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@ package ca.uhn.fhir.jpa.subscription.email;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class EmailDetails {
|
||||
|
@ -27,6 +29,7 @@ public class EmailDetails {
|
|||
private String myBodyTemplate;
|
||||
private List<String> myTo;
|
||||
private String myFrom;
|
||||
private IIdType mySubscription;
|
||||
|
||||
public String getBodyTemplate() {
|
||||
return myBodyTemplate;
|
||||
|
@ -52,6 +55,14 @@ public class EmailDetails {
|
|||
mySubjectTemplate = theSubjectTemplate;
|
||||
}
|
||||
|
||||
public IIdType getSubscription() {
|
||||
return mySubscription;
|
||||
}
|
||||
|
||||
public void setSubscription(IIdType theSubscription) {
|
||||
mySubscription = theSubscription;
|
||||
}
|
||||
|
||||
public List<String> getTo() {
|
||||
return myTo;
|
||||
}
|
||||
|
|
|
@ -22,11 +22,11 @@ package ca.uhn.fhir.jpa.subscription.email;
|
|||
|
||||
import ca.uhn.fhir.jpa.util.StopWatch;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.mail.SimpleMailMessage;
|
||||
import org.springframework.mail.javamail.JavaMailSender;
|
||||
import org.springframework.mail.javamail.JavaMailSenderImpl;
|
||||
import org.thymeleaf.context.Context;
|
||||
import org.thymeleaf.spring4.SpringTemplateEngine;
|
||||
|
@ -35,6 +35,9 @@ import org.thymeleaf.templatemode.TemplateMode;
|
|||
import org.thymeleaf.templateresolver.StringTemplateResolver;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.mail.Message;
|
||||
import javax.mail.MessagingException;
|
||||
import javax.mail.internet.MimeMessage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
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.trim;
|
||||
|
||||
public class EmailSender implements IEmailSender {
|
||||
public class JavaMailEmailSender implements IEmailSender {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(EmailSender.class);
|
||||
private String mySmtpServerHost;
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(JavaMailEmailSender.class);
|
||||
private String mySmtpServerHostname;
|
||||
private int mySmtpServerPort = 25;
|
||||
private JavaMailSenderImpl mySender;
|
||||
private String mySmtpServerUsername;
|
||||
private String mySmtpServerPassword;
|
||||
|
||||
@PostConstruct
|
||||
public void start() {
|
||||
Validate.notBlank(mySmtpServerHost, "No SMTP host defined");
|
||||
public String getSmtpServerHostname() {
|
||||
return mySmtpServerHostname;
|
||||
}
|
||||
|
||||
mySender = new JavaMailSenderImpl();
|
||||
mySender.setHost(mySmtpServerHost);
|
||||
mySender.setPort(mySmtpServerPort);
|
||||
mySender.setDefaultEncoding(Constants.CHARSET_UTF8.name());
|
||||
/**
|
||||
* Set the SMTP server host to use for outbound mail
|
||||
*/
|
||||
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
|
||||
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();
|
||||
|
||||
StringTemplateResolver templateResolver = new StringTemplateResolver();
|
||||
|
@ -80,39 +114,44 @@ public class EmailSender implements IEmailSender {
|
|||
String body = engine.process(theDetails.getBodyTemplate(), context);
|
||||
String subject = engine.process(theDetails.getSubjectTemplate(), context);
|
||||
|
||||
SimpleMailMessage email = new SimpleMailMessage();
|
||||
email.setFrom(trim(theDetails.getFrom()));
|
||||
email.setTo(toTrimmedStringArray(theDetails.getTo()));
|
||||
email.setSubject(subject);
|
||||
email.setText(body);
|
||||
email.setSentDate(new Date());
|
||||
MimeMessage email = mySender.createMimeMessage();
|
||||
|
||||
try {
|
||||
email.setFrom(trim(theDetails.getFrom()));
|
||||
email.setRecipients(Message.RecipientType.TO, toTrimmedCommaSeparatedString(theDetails.getTo()));
|
||||
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);
|
||||
|
||||
ourLog.info("Done sending email (took {}ms)", sw.getMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the SMTP server host to use for outbound mail
|
||||
*/
|
||||
public void setSmtpServerHost(String theSmtpServerHost) {
|
||||
mySmtpServerHost = theSmtpServerHost;
|
||||
@PostConstruct
|
||||
public void start() {
|
||||
Validate.notBlank(mySmtpServerHostname, "No SMTP host defined");
|
||||
|
||||
mySender = new JavaMailSenderImpl();
|
||||
mySender.setHost(getSmtpServerHostname());
|
||||
mySender.setPort(getSmtpServerPort());
|
||||
mySender.setUsername(getSmtpServerUsername());
|
||||
mySender.setPassword(getSmtpServerPassword());
|
||||
mySender.setDefaultEncoding(Constants.CHARSET_UTF8.name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the SMTP server port to use for outbound mail
|
||||
*/
|
||||
public void setSmtpServerPort(int theSmtpServerPort) {
|
||||
mySmtpServerPort = theSmtpServerPort;
|
||||
}
|
||||
|
||||
private static String[] toTrimmedStringArray(List<String> theTo) {
|
||||
private static String toTrimmedCommaSeparatedString(List<String> theTo) {
|
||||
List<String> to = new ArrayList<>();
|
||||
for (String next : theTo) {
|
||||
if (isNotBlank(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<>();
|
||||
String[] destinationAddressStrings = StringUtils.split(endpointUrl, ",");
|
||||
for (String next : destinationAddressStrings) {
|
||||
next = trim(defaultString(next));
|
||||
if (next.startsWith("mailto:")) {
|
||||
next = next.substring("mailto:".length());
|
||||
}
|
||||
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 bodyTemplate = defaultString(subscription.getEmailDetails().getBodyTemplate(), provideDefaultBodyTemplate());
|
||||
|
||||
EmailDetails details = new EmailDetails();
|
||||
details.setTo(destinationAddresses);
|
||||
details.setFrom(from);
|
||||
details.setBodyTemplate(bodyTemplate);
|
||||
details.setBodyTemplate(subscription.getPayloadString());
|
||||
details.setSubjectTemplate(subjectTemplate);
|
||||
details.setSubscription(subscription.getIdElement(getContext()));
|
||||
|
||||
IEmailSender emailSender = mySubscriptionEmailInterceptor.getEmailSender();
|
||||
emailSender.send(details);
|
||||
}
|
||||
|
||||
private String provideDefaultBodyTemplate() {
|
||||
return "A subscription update has been received";
|
||||
}
|
||||
|
||||
private String provideDefaultFrom() {
|
||||
return "unknown@sender.com";
|
||||
}
|
||||
|
||||
private String provideDefaultSubjectTemplate() {
|
||||
return "HAPI FHIR Subscriptions";
|
||||
|
|
|
@ -20,28 +20,51 @@ package ca.uhn.fhir.jpa.subscription.email;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.subscription.BaseSubscriptionInterceptor;
|
||||
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 java.util.List;
|
||||
|
||||
public class SubscriptionEmailInterceptor extends BaseSubscriptionInterceptor {
|
||||
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 String myDefaultFromAddress = "noreply@unknown.com";
|
||||
|
||||
@Override
|
||||
public org.hl7.fhir.r4.model.Subscription.SubscriptionChannelType getChannelType() {
|
||||
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() {
|
||||
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) {
|
||||
myEmailSender = theEmailSender;
|
||||
}
|
||||
|
@ -54,12 +77,12 @@ public class SubscriptionEmailInterceptor extends BaseSubscriptionInterceptor {
|
|||
getDeliveryChannel().subscribe(mySubscriptionDeliverySubscriber);
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void start() {
|
||||
Validate.notNull(myEmailSender, "emailSender has not been configured");
|
||||
|
||||
super.start();
|
||||
}
|
||||
// @PostConstruct
|
||||
// public void start() {
|
||||
// Validate.notNull(myEmailSender, "emailSender has not been configured");
|
||||
//
|
||||
// super.start();
|
||||
// }
|
||||
|
||||
@Override
|
||||
protected void unregisterDeliverySubscriber() {
|
||||
|
|
|
@ -24,8 +24,20 @@ public class JpaConstants {
|
|||
|
||||
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";
|
||||
|
||||
/**
|
||||
* <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_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 {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("mySubscriptionDaoR4")
|
||||
@Qualifier("mySubscriptionDaoDstu2")
|
||||
private IFhirResourceDao<Subscription> myDao;
|
||||
|
||||
@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
|
||||
public String getClientInfo(String theName) throws SQLException {
|
||||
return getClientInfo(theName);
|
||||
return myWrap.getClientInfo(theName);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -10,19 +10,30 @@ import javax.sql.DataSource;
|
|||
|
||||
import ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory;
|
||||
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.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.LocalContainerEntityManagerFactoryBean;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
|
||||
import ca.uhn.fhir.validation.ResultSeverityEnum;
|
||||
import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder;
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
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
|
||||
@EnableTransactionManagement()
|
||||
|
@ -31,11 +42,6 @@ public class TestDstu3Config extends BaseJavaConfigDstu3 {
|
|||
static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TestDstu3Config.class);
|
||||
private Exception myLastStackTrace;
|
||||
|
||||
@Bean()
|
||||
public DaoConfig daoConfig() {
|
||||
return new DaoConfig();
|
||||
}
|
||||
|
||||
@Bean()
|
||||
public BasicDataSource basicDataSource() {
|
||||
BasicDataSource retVal = new BasicDataSource() {
|
||||
|
@ -50,7 +56,7 @@ public class TestDstu3Config extends BaseJavaConfigDstu3 {
|
|||
ourLog.error("Exceeded maximum wait for connection", e);
|
||||
logGetConnectionStackTrace();
|
||||
// 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);
|
||||
retVal = null;
|
||||
|
@ -100,20 +106,33 @@ public class TestDstu3Config extends BaseJavaConfigDstu3 {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
@Bean()
|
||||
public DaoConfig daoConfig() {
|
||||
return new DaoConfig();
|
||||
}
|
||||
|
||||
@Bean()
|
||||
@Primary()
|
||||
public DataSource dataSource() {
|
||||
|
||||
DataSource dataSource = ProxyDataSourceBuilder
|
||||
.create(basicDataSource())
|
||||
.create(basicDataSource())
|
||||
// .logQueryBySlf4j(SLF4JLogLevel.INFO, "SQL")
|
||||
.logSlowQueryBySlf4j(1000, TimeUnit.MILLISECONDS)
|
||||
.countQuery()
|
||||
.build();
|
||||
.logSlowQueryBySlf4j(1000, TimeUnit.MILLISECONDS)
|
||||
.countQuery()
|
||||
.build();
|
||||
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IEmailSender emailSender() {
|
||||
JavaMailEmailSender retVal = new JavaMailEmailSender();
|
||||
retVal.setSmtpServerHostname("localhost");
|
||||
retVal.setSmtpServerPort(3025);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Bean()
|
||||
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
||||
LocalContainerEntityManagerFactoryBean retVal = new LocalContainerEntityManagerFactoryBean();
|
||||
|
|
|
@ -118,8 +118,13 @@ public abstract class BaseJpaTest {
|
|||
|
||||
protected List<String> toUnqualifiedVersionlessIdValues(IBundleProvider theFound) {
|
||||
List<String> retVal = new ArrayList<String>();
|
||||
int size = theFound.size();
|
||||
Integer size = theFound.size();
|
||||
ourLog.info("Found {} results", size);
|
||||
|
||||
if (size == null) {
|
||||
size = 99999;
|
||||
}
|
||||
|
||||
List<IBaseResource> resources = theFound.getResources(0, size);
|
||||
for (IBaseResource next : resources) {
|
||||
retVal.add(next.getIdElement().toUnqualifiedVersionless().getValue());
|
||||
|
@ -298,7 +303,7 @@ public abstract class BaseJpaTest {
|
|||
|
||||
public static void waitForSize(int theTarget, List<?> theList) {
|
||||
StopWatch sw = new StopWatch();
|
||||
while (theList.size() != theTarget && sw.getMillis() < 10000) {
|
||||
while (theList.size() != theTarget && sw.getMillis() <= 15000) {
|
||||
try {
|
||||
Thread.sleep(50);
|
||||
} catch (InterruptedException theE) {
|
||||
|
|
|
@ -120,7 +120,7 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
|
|||
protected Object myResourceProviders;
|
||||
@Autowired
|
||||
protected ISearchCoordinatorSvc mySearchCoordinatorSvc;
|
||||
@Autowired
|
||||
@Autowired(required = false)
|
||||
protected IFulltextSearchSvc mySearchDao;
|
||||
@Autowired
|
||||
protected ISearchParamPresenceSvc mySearchParamPresenceSvc;
|
||||
|
|
|
@ -56,6 +56,30 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
|
|||
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) {
|
||||
try {
|
||||
assertNotGone(theId);
|
||||
|
|
|
@ -102,6 +102,7 @@ public class FhirResourceDaoDstu2ValidateTest extends BaseJpaDstu2Test {
|
|||
} catch (PreconditionFailedException e) {
|
||||
return (OperationOutcome) e.getOperationOutcome();
|
||||
}
|
||||
break;
|
||||
case XML:
|
||||
encoded = myFhirCtx.newXmlParser().encodeResourceToString(input);
|
||||
try {
|
||||
|
@ -110,6 +111,7 @@ public class FhirResourceDaoDstu2ValidateTest extends BaseJpaDstu2Test {
|
|||
} catch (PreconditionFailedException e) {
|
||||
return (OperationOutcome) e.getOperationOutcome();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
throw new IllegalStateException(); // shouldn't get here
|
||||
|
|
|
@ -172,7 +172,7 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
|
|||
protected IResourceTagDao myResourceTagDao;
|
||||
@Autowired
|
||||
protected ISearchCoordinatorSvc mySearchCoordinatorSvc;
|
||||
@Autowired
|
||||
@Autowired(required = false)
|
||||
protected IFulltextSearchSvc mySearchDao;
|
||||
@Autowired
|
||||
protected ISearchDao mySearchEntityDao;
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
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.junit.Assert.*;
|
||||
|
||||
import ca.uhn.fhir.jpa.util.StopWatch;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.dstu3.model.Patient;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.junit.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.test.util.AopTestUtils;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
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;
|
||||
|
||||
public class FhirResourceDaoDstu3SearchPageExpiryTest extends BaseJpaDstu3Test {
|
||||
@Before
|
||||
public void beforeDisableResultReuse() {
|
||||
myDaoConfig.setReuseCachedSearchResultsForMillis(null);
|
||||
}
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(FhirResourceDaoDstu3SearchPageExpiryTest.class);
|
||||
|
||||
@After()
|
||||
public void after() {
|
||||
StaleSearchDeletingSvcImpl staleSearchDeletingSvc = AopTestUtils.getTargetObject(myStaleSearchDeletingSvc);
|
||||
staleSearchDeletingSvc.setCutoffSlackForUnitTest(StaleSearchDeletingSvcImpl.DEFAULT_CUTOFF_SLACK);
|
||||
StaleSearchDeletingSvcImpl.setNowForUnitTests(null);
|
||||
}
|
||||
|
||||
@Before
|
||||
|
@ -35,48 +37,9 @@ public class FhirResourceDaoDstu3SearchPageExpiryTest extends BaseJpaDstu3Test {
|
|||
staleSearchDeletingSvc.setCutoffSlackForUnitTest(0);
|
||||
}
|
||||
|
||||
@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);
|
||||
|
||||
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()));
|
||||
}
|
||||
});
|
||||
@Before
|
||||
public void beforeDisableResultReuse() {
|
||||
myDaoConfig.setReuseCachedSearchResultsForMillis(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -98,6 +61,7 @@ public class FhirResourceDaoDstu3SearchPageExpiryTest extends BaseJpaDstu3Test {
|
|||
|
||||
myDaoConfig.setExpireSearchResultsAfterMillis(1000L);
|
||||
myDaoConfig.setReuseCachedSearchResultsForMillis(500L);
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
final String searchUuid1;
|
||||
{
|
||||
|
@ -109,7 +73,7 @@ public class FhirResourceDaoDstu3SearchPageExpiryTest extends BaseJpaDstu3Test {
|
|||
Validate.notBlank(searchUuid1);
|
||||
}
|
||||
|
||||
Thread.sleep(250);
|
||||
sleepAtLeast(250);
|
||||
|
||||
String searchUuid2;
|
||||
{
|
||||
|
@ -122,7 +86,7 @@ public class FhirResourceDaoDstu3SearchPageExpiryTest extends BaseJpaDstu3Test {
|
|||
}
|
||||
assertEquals(searchUuid1, searchUuid2);
|
||||
|
||||
Thread.sleep(500);
|
||||
sleepAtLeast(500);
|
||||
|
||||
// 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
|
||||
|
||||
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();
|
||||
newTxTemplate().execute(new TransactionCallbackWithoutResult() {
|
||||
@Override
|
||||
protected void doInTransactionWithoutResult(TransactionStatus theArg0) {
|
||||
assertNull(mySearchEntityDao.findByUuid(searchUuid1));
|
||||
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();
|
||||
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) {
|
||||
return (OperationOutcome) e.getOperationOutcome();
|
||||
}
|
||||
break;
|
||||
case XML:
|
||||
encoded = myFhirCtx.newXmlParser().encodeResourceToString(input);
|
||||
try {
|
||||
|
@ -141,6 +142,7 @@ public class FhirResourceDaoDstu3ValidateTest extends BaseJpaDstu3Test {
|
|||
} catch (PreconditionFailedException e) {
|
||||
return (OperationOutcome) e.getOperationOutcome();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
throw new IllegalStateException(); // shouldn't get here
|
||||
|
|
|
@ -1879,6 +1879,91 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
|
|||
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
|
||||
public void testTransactionSearchWithCount() {
|
||||
String methodName = "testTransactionSearchWithCount";
|
||||
|
|
|
@ -181,7 +181,7 @@ public abstract class BaseJpaR4Test extends BaseJpaTest {
|
|||
protected IResourceTagDao myResourceTagDao;
|
||||
@Autowired
|
||||
protected ISearchCoordinatorSvc mySearchCoordinatorSvc;
|
||||
@Autowired
|
||||
@Autowired(required = false)
|
||||
protected IFulltextSearchSvc mySearchDao;
|
||||
@Autowired
|
||||
protected ISearchDao mySearchEntityDao;
|
||||
|
|
|
@ -1,33 +1,35 @@
|
|||
package ca.uhn.fhir.jpa.dao.r4;
|
||||
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
|
||||
import ca.uhn.fhir.jpa.search.StaleSearchDeletingSvcImpl;
|
||||
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.hl7.fhir.r4.model.Patient;
|
||||
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.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
|
||||
import ca.uhn.fhir.jpa.search.StaleSearchDeletingSvcImpl;
|
||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
import static ca.uhn.fhir.jpa.util.TestUtil.sleepAtLeast;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class FhirResourceDaoR4SearchPageExpiryTest extends BaseJpaR4Test {
|
||||
@Before
|
||||
public void beforeDisableResultReuse() {
|
||||
myDaoConfig.setReuseCachedSearchResultsForMillis(null);
|
||||
}
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(FhirResourceDaoR4SearchPageExpiryTest.class);
|
||||
|
||||
@After()
|
||||
public void after() {
|
||||
StaleSearchDeletingSvcImpl staleSearchDeletingSvc = AopTestUtils.getTargetObject(myStaleSearchDeletingSvc);
|
||||
staleSearchDeletingSvc.setCutoffSlackForUnitTest(StaleSearchDeletingSvcImpl.DEFAULT_CUTOFF_SLACK);
|
||||
StaleSearchDeletingSvcImpl.setNowForUnitTests(null);
|
||||
}
|
||||
|
||||
@Before
|
||||
|
@ -36,50 +38,9 @@ public class FhirResourceDaoR4SearchPageExpiryTest extends BaseJpaR4Test {
|
|||
staleSearchDeletingSvc.setCutoffSlackForUnitTest(0);
|
||||
}
|
||||
|
||||
@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();
|
||||
|
||||
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()));
|
||||
}
|
||||
});
|
||||
@Before
|
||||
public void beforeDisableResultReuse() {
|
||||
myDaoConfig.setReuseCachedSearchResultsForMillis(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -101,6 +62,7 @@ public class FhirResourceDaoR4SearchPageExpiryTest extends BaseJpaR4Test {
|
|||
|
||||
myDaoConfig.setExpireSearchResultsAfterMillis(1000L);
|
||||
myDaoConfig.setReuseCachedSearchResultsForMillis(500L);
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
final String searchUuid1;
|
||||
{
|
||||
|
@ -112,7 +74,7 @@ public class FhirResourceDaoR4SearchPageExpiryTest extends BaseJpaR4Test {
|
|||
Validate.notBlank(searchUuid1);
|
||||
}
|
||||
|
||||
Thread.sleep(250);
|
||||
sleepAtLeast(250);
|
||||
|
||||
String searchUuid2;
|
||||
{
|
||||
|
@ -125,7 +87,7 @@ public class FhirResourceDaoR4SearchPageExpiryTest extends BaseJpaR4Test {
|
|||
}
|
||||
assertEquals(searchUuid1, searchUuid2);
|
||||
|
||||
Thread.sleep(500);
|
||||
sleepAtLeast(500);
|
||||
|
||||
// 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();
|
||||
newTxTemplate().execute(new TransactionCallbackWithoutResult() {
|
||||
|
@ -166,7 +128,7 @@ public class FhirResourceDaoR4SearchPageExpiryTest extends BaseJpaR4Test {
|
|||
}
|
||||
});
|
||||
|
||||
Thread.sleep(300);
|
||||
StaleSearchDeletingSvcImpl.setNowForUnitTests(start + 2200);
|
||||
|
||||
myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem();
|
||||
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() {
|
||||
myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences());
|
||||
myDaoConfig.setTreatReferencesAsLogical(new DaoConfig().getTreatReferencesAsLogical());
|
||||
myDaoConfig.setEnforceReferentialIntegrityOnDelete(new DaoConfig().isEnforceReferentialIntegrityOnDelete());
|
||||
}
|
||||
|
||||
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
|
||||
public void beforeDisableResultReuse() {
|
||||
|
|
|
@ -133,6 +133,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
|||
} catch (PreconditionFailedException e) {
|
||||
return (OperationOutcome) e.getOperationOutcome();
|
||||
}
|
||||
break;
|
||||
case XML:
|
||||
encoded = myFhirCtx.newXmlParser().encodeResourceToString(input);
|
||||
try {
|
||||
|
@ -141,6 +142,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
|||
} catch (PreconditionFailedException e) {
|
||||
return (OperationOutcome) e.getOperationOutcome();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
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
|
||||
public void testTransaction1() throws IOException {
|
||||
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.search.DatabaseBackedPagingProvider;
|
||||
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.validation.JpaValidationSupportChainDstu3;
|
||||
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||
|
@ -58,6 +59,7 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test {
|
|||
protected static SearchParamRegistryDstu3 ourSearchParamRegistry;
|
||||
protected static DatabaseBackedPagingProvider ourPagingProvider;
|
||||
protected static SubscriptionRestHookInterceptor ourRestHookSubscriptionInterceptor;
|
||||
protected static SubscriptionEmailInterceptor ourEmailSubscriptionInterceptor;
|
||||
protected static ISearchDao mySearchEntityDao;
|
||||
protected static ISearchCoordinatorSvc mySearchCoordinatorSvc;
|
||||
|
||||
|
@ -111,13 +113,9 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test {
|
|||
ourWebApplicationContext = new GenericWebApplicationContext();
|
||||
ourWebApplicationContext.setParent(myAppCtx);
|
||||
ourWebApplicationContext.refresh();
|
||||
// ContextLoaderListener loaderListener = new ContextLoaderListener(webApplicationContext);
|
||||
// loaderListener.initWebApplicationContext(mock(ServletContext.class));
|
||||
//
|
||||
proxyHandler.getServletContext().setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ourWebApplicationContext);
|
||||
|
||||
DispatcherServlet dispatcherServlet = new DispatcherServlet();
|
||||
// dispatcherServlet.setApplicationContext(webApplicationContext);
|
||||
dispatcherServlet.setContextClass(AnnotationConfigWebApplicationContext.class);
|
||||
ServletHolder subsServletHolder = new ServletHolder();
|
||||
subsServletHolder.setServlet(dispatcherServlet);
|
||||
|
@ -150,6 +148,7 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test {
|
|||
mySearchCoordinatorSvc = wac.getBean(ISearchCoordinatorSvc.class);
|
||||
mySearchEntityDao = wac.getBean(ISearchDao.class);
|
||||
ourRestHookSubscriptionInterceptor = wac.getBean(SubscriptionRestHookInterceptor.class);
|
||||
ourEmailSubscriptionInterceptor = wac.getBean(SubscriptionEmailInterceptor.class);
|
||||
ourSearchParamRegistry = wac.getBean(SearchParamRegistryDstu3.class);
|
||||
|
||||
myFhirCtx.getRestfulClientFactory().setSocketTimeout(5000000);
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package ca.uhn.fhir.jpa.provider.dstu3;
|
||||
|
||||
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.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
|
@ -61,8 +60,8 @@ public class ResourceProviderInterceptorDstu3Test extends BaseResourceProviderDs
|
|||
public void before() throws Exception {
|
||||
super.before();
|
||||
|
||||
myServerInterceptor = mock(IServerInterceptor.class);
|
||||
myDaoInterceptor = mock(IServerInterceptor.class);
|
||||
myServerInterceptor = mock(IServerInterceptor.class, withSettings().verboseLogging());
|
||||
myDaoInterceptor = mock(IServerInterceptor.class, withSettings().verboseLogging());
|
||||
|
||||
resetServerInterceptor();
|
||||
|
||||
|
@ -125,6 +124,8 @@ public class ResourceProviderInterceptorDstu3Test extends BaseResourceProviderDs
|
|||
public void testCreateResourceInTransaction() throws IOException, ServletException {
|
||||
String methodName = "testCreateResourceInTransaction";
|
||||
|
||||
ourLog.info("** Starting {}", methodName);
|
||||
|
||||
Patient pt = new Patient();
|
||||
pt.addName().setFamily(methodName);
|
||||
|
||||
|
@ -140,6 +141,11 @@ public class ResourceProviderInterceptorDstu3Test extends BaseResourceProviderDs
|
|||
|
||||
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 + "/");
|
||||
post.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||
CloseableHttpResponse response = ourHttpClient.execute(post);
|
||||
|
@ -153,8 +159,8 @@ public class ResourceProviderInterceptorDstu3Test extends BaseResourceProviderDs
|
|||
* Server Interceptor
|
||||
*/
|
||||
|
||||
ArgumentCaptor<ActionRequestDetails> ardCaptor = ArgumentCaptor.forClass(ActionRequestDetails.class);
|
||||
ArgumentCaptor<RestOperationTypeEnum> opTypeCaptor = ArgumentCaptor.forClass(RestOperationTypeEnum.class);
|
||||
ardCaptor = ArgumentCaptor.forClass(ActionRequestDetails.class);
|
||||
opTypeCaptor = ArgumentCaptor.forClass(RestOperationTypeEnum.class);
|
||||
verify(myServerInterceptor, times(2)).incomingRequestPreHandled(opTypeCaptor.capture(), ardCaptor.capture());
|
||||
assertEquals(RestOperationTypeEnum.TRANSACTION, opTypeCaptor.getAllValues().get(0));
|
||||
assertEquals(null, ardCaptor.getAllValues().get(0).getResourceType());
|
||||
|
|
|
@ -9,6 +9,8 @@ import java.io.IOException;
|
|||
import java.util.ArrayList;
|
||||
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.entity.ContentType;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
|
@ -69,19 +71,68 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource
|
|||
Patient patient = new Patient();
|
||||
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
|
||||
patient.addName().setFamily("Tester").addGiven("Raghad");
|
||||
IIdType id = ourClient.create().resource(patient).execute().getId();
|
||||
IIdType id = myClient.create().resource(patient).execute().getId();
|
||||
|
||||
try {
|
||||
ourClient.delete().resourceById(id.toUnqualifiedVersionless()).execute();
|
||||
myClient.delete().resourceById(id.toUnqualifiedVersionless()).execute();
|
||||
fail();
|
||||
} catch (ForbiddenOperationException e) {
|
||||
// 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());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@ -92,16 +143,16 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource
|
|||
Patient patient = new Patient();
|
||||
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
|
||||
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();
|
||||
obsInCompartment.setStatus(ObservationStatus.FINAL);
|
||||
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();
|
||||
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) {
|
||||
@Override
|
||||
|
@ -114,10 +165,10 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource
|
|||
}
|
||||
});
|
||||
|
||||
ourClient.delete().resourceById(obsInCompartmentId.toUnqualifiedVersionless()).execute();
|
||||
myClient.delete().resourceById(obsInCompartmentId.toUnqualifiedVersionless()).execute();
|
||||
|
||||
try {
|
||||
ourClient.delete().resourceById(obsNotInCompartmentId.toUnqualifiedVersionless()).execute();
|
||||
myClient.delete().resourceById(obsNotInCompartmentId.toUnqualifiedVersionless()).execute();
|
||||
fail();
|
||||
} catch (ForbiddenOperationException e) {
|
||||
// good
|
||||
|
@ -130,7 +181,7 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource
|
|||
Patient patient = new Patient();
|
||||
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
|
||||
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) {
|
||||
@Override
|
||||
|
@ -148,7 +199,7 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource
|
|||
patient.setId(output1.getId().toUnqualifiedVersionless());
|
||||
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
|
||||
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());
|
||||
|
||||
|
@ -156,7 +207,7 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource
|
|||
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
|
||||
patient.addName().setFamily("Tester").addGiven("Raghad");
|
||||
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();
|
||||
} catch (ForbiddenOperationException e) {
|
||||
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.addName().setFamily("Tester").addGiven("Raghad");
|
||||
try {
|
||||
ourClient.update().resource(patient).execute();
|
||||
myClient.update().resource(patient).execute();
|
||||
fail();
|
||||
} catch (ForbiddenOperationException e) {
|
||||
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 {
|
||||
Patient pt1 = new Patient();
|
||||
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();
|
||||
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) {
|
||||
@Override
|
||||
|
@ -200,7 +251,7 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource
|
|||
Observation obs = new Observation();
|
||||
obs.setStatus(ObservationStatus.FINAL);
|
||||
obs.setSubject(new Reference(pid1));
|
||||
IIdType oid = ourClient.create().resource(obs).execute().getId().toUnqualified();
|
||||
IIdType oid = myClient.create().resource(obs).execute().getId().toUnqualified();
|
||||
|
||||
|
||||
unregisterInterceptors();
|
||||
|
@ -224,7 +275,7 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource
|
|||
obs.setSubject(new Reference(pid2));
|
||||
|
||||
try {
|
||||
ourClient.update().resource(obs).execute();
|
||||
myClient.update().resource(obs).execute();
|
||||
fail();
|
||||
} catch (ForbiddenOperationException e) {
|
||||
// good
|
||||
|
|
|
@ -47,7 +47,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|||
public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
|
||||
|
||||
protected static JpaValidationSupportChainR4 myValidationSupport;
|
||||
protected static IGenericClient ourClient;
|
||||
protected IGenericClient myClient;
|
||||
protected static CloseableHttpClient ourHttpClient;
|
||||
protected static int ourPort;
|
||||
protected static RestfulServer ourRestServer;
|
||||
|
@ -150,10 +150,6 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
|
|||
ourSearchParamRegistry = wac.getBean(SearchParamRegistryR4.class);
|
||||
|
||||
myFhirCtx.getRestfulClientFactory().setSocketTimeout(5000000);
|
||||
ourClient = myFhirCtx.newRestfulGenericClient(ourServerBase);
|
||||
if (shouldLogClient()) {
|
||||
ourClient.registerInterceptor(new LoggingInterceptor());
|
||||
}
|
||||
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||
|
@ -165,6 +161,11 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
|
|||
}
|
||||
|
||||
ourRestServer.setPagingProvider(ourPagingProvider);
|
||||
|
||||
myClient = myFhirCtx.newRestfulGenericClient(ourServerBase);
|
||||
if (shouldLogClient()) {
|
||||
myClient.registerInterceptor(new LoggingInterceptor());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
package ca.uhn.fhir.jpa.provider.r4;
|
||||
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -31,14 +31,14 @@ public class GraphQLProviderR4Test extends BaseResourceProviderR4Test {
|
|||
try {
|
||||
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(resp);
|
||||
assertEquals(resp, "{\n" +
|
||||
assertEquals(TestUtil.stripReturns(resp), TestUtil.stripReturns("{\n" +
|
||||
" \"name\":[{\n" +
|
||||
" \"family\":\"FAM\",\n" +
|
||||
" \"given\":[\"GIVEN1\",\"GIVEN2\"]\n" +
|
||||
" },{\n" +
|
||||
" \"given\":[\"GivenOnly1\",\"GivenOnly2\"]\n" +
|
||||
" }]\n" +
|
||||
"}");
|
||||
"}"));
|
||||
} finally {
|
||||
IOUtils.closeQuietly(response);
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ public class GraphQLProviderR4Test extends BaseResourceProviderR4Test {
|
|||
try {
|
||||
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(resp);
|
||||
assertEquals(resp, "{\n" +
|
||||
assertEquals(TestUtil.stripReturns(resp), TestUtil.stripReturns("{\n" +
|
||||
" \"PatientList\":[{\n" +
|
||||
" \"name\":[{\n" +
|
||||
" \"family\":\"FAM\",\n" +
|
||||
|
@ -69,7 +69,7 @@ public class GraphQLProviderR4Test extends BaseResourceProviderR4Test {
|
|||
" \"given\":[\"GivenOnlyB1\",\"GivenOnlyB2\"]\n" +
|
||||
" }]\n" +
|
||||
" }]\n" +
|
||||
"}");
|
||||
"}"));
|
||||
} finally {
|
||||
IOUtils.closeQuietly(response);
|
||||
}
|
||||
|
@ -85,13 +85,13 @@ public class GraphQLProviderR4Test extends BaseResourceProviderR4Test {
|
|||
p.addName()
|
||||
.addGiven("GivenOnly1")
|
||||
.addGiven("GivenOnly2");
|
||||
myPatientId0 = ourClient.create().resource(p).execute().getId().toUnqualifiedVersionless();
|
||||
myPatientId0 = myClient.create().resource(p).execute().getId().toUnqualifiedVersionless();
|
||||
|
||||
p = new Patient();
|
||||
p.addName()
|
||||
.addGiven("GivenOnlyB1")
|
||||
.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();
|
||||
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);
|
||||
|
||||
Patient patient = new Patient();
|
||||
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();
|
||||
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();
|
||||
enc1.setStatus(EncounterStatus.CANCELLED);
|
||||
enc1.getSubject().setReference(patId);
|
||||
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();
|
||||
enc2.setStatus(EncounterStatus.ARRIVED);
|
||||
enc2.getSubject().setReference(patId);
|
||||
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();
|
||||
wrongEnc1.setStatus(EncounterStatus.ARRIVED);
|
||||
wrongEnc1.getSubject().setReference(myWrongPatId);
|
||||
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>();
|
||||
for (int i = 0; i < 20; i++) {
|
||||
Observation obs = new Observation();
|
||||
obs.getSubject().setReference(patId);
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide
|
|||
sp.setTitle("Foo Param");
|
||||
|
||||
try {
|
||||
ourClient.create().resource(sp).execute();
|
||||
myClient.create().resource(sp).execute();
|
||||
fail();
|
||||
} catch (UnprocessableEntityException e) {
|
||||
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));
|
||||
|
||||
ourClient
|
||||
myClient
|
||||
.create()
|
||||
.resource(eyeColourSp)
|
||||
.execute();
|
||||
|
@ -139,7 +139,7 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide
|
|||
p2.addExtension().setUrl("http://acme.org/eyecolour").setValue(new CodeType("green"));
|
||||
IIdType p2id = myPatientDao.create(p2).getId().toUnqualifiedVersionless();
|
||||
|
||||
Bundle bundle = ourClient
|
||||
Bundle bundle = myClient
|
||||
.search()
|
||||
.forResource(Patient.class)
|
||||
.where(new TokenClientParam("eyecolour").exactly().code("blue"))
|
||||
|
@ -168,7 +168,7 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide
|
|||
public void testConformanceOverrideAllowed() {
|
||||
myDaoConfig.setDefaultSearchParamsCanBeOverridden(true);
|
||||
|
||||
CapabilityStatement conformance = ourClient
|
||||
CapabilityStatement conformance = myClient
|
||||
.fetchConformance()
|
||||
.ofType(CapabilityStatement.class)
|
||||
.execute();
|
||||
|
@ -220,7 +220,7 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide
|
|||
}
|
||||
});
|
||||
|
||||
conformance = ourClient
|
||||
conformance = myClient
|
||||
.fetchConformance()
|
||||
.ofType(CapabilityStatement.class)
|
||||
.execute();
|
||||
|
@ -238,7 +238,7 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide
|
|||
public void testConformanceOverrideNotAllowed() {
|
||||
myDaoConfig.setDefaultSearchParamsCanBeOverridden(false);
|
||||
|
||||
CapabilityStatement conformance = ourClient
|
||||
CapabilityStatement conformance = myClient
|
||||
.fetchConformance()
|
||||
.ofType(CapabilityStatement.class)
|
||||
.execute();
|
||||
|
@ -274,7 +274,7 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide
|
|||
|
||||
mySearchParamRegsitry.forceRefresh();
|
||||
|
||||
conformance = ourClient
|
||||
conformance = myClient
|
||||
.fetchConformance()
|
||||
.ofType(CapabilityStatement.class)
|
||||
.execute();
|
||||
|
@ -332,7 +332,7 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide
|
|||
List<String> foundResources;
|
||||
Bundle result;
|
||||
|
||||
result = ourClient
|
||||
result = myClient
|
||||
.search()
|
||||
.forResource(Patient.class)
|
||||
.where(new TokenClientParam("foo").exactly().code("male"))
|
||||
|
@ -409,7 +409,7 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide
|
|||
List<String> foundResources;
|
||||
Bundle result;
|
||||
|
||||
result = ourClient
|
||||
result = myClient
|
||||
.search()
|
||||
.forResource(Observation.class)
|
||||
.where(new ReferenceClientParam("foo").hasChainedProperty(Patient.GENDER.exactly().code("male")))
|
||||
|
|
|
@ -198,7 +198,7 @@ public class ResourceProviderInterceptorR4Test extends BaseResourceProviderR4Tes
|
|||
|
||||
Organization org = new Organization();
|
||||
org.setName("orgName");
|
||||
IIdType orgId = ourClient.create().resource(org).execute().getId().toUnqualified();
|
||||
IIdType orgId = myClient.create().resource(org).execute().getId().toUnqualified();
|
||||
assertNotNull(orgId.getVersionIdPartAsLong());
|
||||
|
||||
resetServerInterceptor();
|
||||
|
|
|
@ -72,7 +72,7 @@ public class ResourceProviderQuestionnaireResponseR4Test extends BaseResourcePro
|
|||
qr1.setStatus(QuestionnaireResponseStatus.COMPLETED);
|
||||
qr1.addItem().setLinkId("link1").addAnswer().setValue(new DecimalType(123));
|
||||
try {
|
||||
ourClient.create().resource(qr1).execute();
|
||||
myClient.create().resource(qr1).execute();
|
||||
fail();
|
||||
} catch (UnprocessableEntityException e) {
|
||||
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.addItem().setLinkId("link1").addAnswer().setValue(new DecimalType(123));
|
||||
try {
|
||||
ourClient.create().resource(qr1).execute();
|
||||
myClient.create().resource(qr1).execute();
|
||||
fail();
|
||||
} catch (UnprocessableEntityException e) {
|
||||
assertThat(e.toString(), containsString("Answer value must be of type string"));
|
||||
|
|
|
@ -34,9 +34,9 @@ public class ResourceProviderR4BundleTest extends BaseResourceProviderR4Test {
|
|||
composition.setTitle("Visit Summary");
|
||||
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));
|
||||
|
||||
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.parser.StrictErrorHandler;
|
||||
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.util.TestUtil;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
|
@ -14,13 +16,17 @@ import org.junit.Test;
|
|||
import org.springframework.test.util.AopTestUtils;
|
||||
|
||||
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.*;
|
||||
|
||||
public class ResourceProviderR4CacheTest extends BaseResourceProviderR4Test {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderR4CacheTest.class);
|
||||
private SearchCoordinatorSvcImpl mySearchCoordinatorSvcRaw;
|
||||
private CapturingInterceptor myCapturingInterceptor;
|
||||
|
||||
@Override
|
||||
@After
|
||||
|
@ -28,6 +34,8 @@ public class ResourceProviderR4CacheTest extends BaseResourceProviderR4Test {
|
|||
super.after();
|
||||
myDaoConfig.setReuseCachedSearchResultsForMillis(new DaoConfig().getReuseCachedSearchResultsForMillis());
|
||||
myDaoConfig.setCacheControlNoStoreMaxResultsUpperLimit(new DaoConfig().getCacheControlNoStoreMaxResultsUpperLimit());
|
||||
|
||||
myClient.unregisterInterceptor(myCapturingInterceptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -35,6 +43,9 @@ public class ResourceProviderR4CacheTest extends BaseResourceProviderR4Test {
|
|||
super.before();
|
||||
myFhirCtx.setParserErrorHandler(new StrictErrorHandler());
|
||||
mySearchCoordinatorSvcRaw = AopTestUtils.getTargetObject(mySearchCoordinatorSvc);
|
||||
|
||||
myCapturingInterceptor = new CapturingInterceptor();
|
||||
myClient.registerInterceptor(myCapturingInterceptor);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -42,9 +53,9 @@ public class ResourceProviderR4CacheTest extends BaseResourceProviderR4Test {
|
|||
|
||||
Patient pt1 = new Patient();
|
||||
pt1.addName().setFamily("FAM");
|
||||
ourClient.create().resource(pt1).execute();
|
||||
myClient.create().resource(pt1).execute();
|
||||
|
||||
Bundle results = ourClient
|
||||
Bundle results = myClient
|
||||
.search()
|
||||
.forResource("Patient")
|
||||
.where(Patient.FAMILY.matches().value("FAM"))
|
||||
|
@ -53,12 +64,13 @@ public class ResourceProviderR4CacheTest extends BaseResourceProviderR4Test {
|
|||
.execute();
|
||||
assertEquals(1, results.getEntry().size());
|
||||
assertEquals(0, mySearchEntityDao.count());
|
||||
assertThat(myCapturingInterceptor.getLastResponse().getHeaders(Constants.HEADER_X_CACHE), empty());
|
||||
|
||||
Patient pt2 = new Patient();
|
||||
pt2.addName().setFamily("FAM");
|
||||
ourClient.create().resource(pt2).execute();
|
||||
myClient.create().resource(pt2).execute();
|
||||
|
||||
results = ourClient
|
||||
results = myClient
|
||||
.search()
|
||||
.forResource("Patient")
|
||||
.where(Patient.FAMILY.matches().value("FAM"))
|
||||
|
@ -67,6 +79,7 @@ public class ResourceProviderR4CacheTest extends BaseResourceProviderR4Test {
|
|||
.execute();
|
||||
assertEquals(2, results.getEntry().size());
|
||||
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++) {
|
||||
Patient pt1 = new Patient();
|
||||
pt1.addName().setFamily("FAM" + i);
|
||||
ourClient.create().resource(pt1).execute();
|
||||
myClient.create().resource(pt1).execute();
|
||||
}
|
||||
|
||||
Bundle results = ourClient
|
||||
Bundle results = myClient
|
||||
.search()
|
||||
.forResource("Patient")
|
||||
.where(Patient.FAMILY.matches().value("FAM"))
|
||||
|
@ -88,6 +101,7 @@ public class ResourceProviderR4CacheTest extends BaseResourceProviderR4Test {
|
|||
.execute();
|
||||
assertEquals(5, results.getEntry().size());
|
||||
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 {
|
||||
myDaoConfig.setCacheControlNoStoreMaxResultsUpperLimit(123);
|
||||
try {
|
||||
ourClient
|
||||
myClient
|
||||
.search()
|
||||
.forResource("Patient")
|
||||
.where(Patient.FAMILY.matches().value("FAM"))
|
||||
|
@ -113,17 +127,18 @@ public class ResourceProviderR4CacheTest extends BaseResourceProviderR4Test {
|
|||
|
||||
Patient pt1 = new Patient();
|
||||
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, mySearchEntityDao.count());
|
||||
assertThat(myCapturingInterceptor.getLastResponse().getHeaders(Constants.HEADER_X_CACHE), empty());
|
||||
|
||||
Patient pt2 = new Patient();
|
||||
pt2.addName().setFamily("FAM");
|
||||
ourClient.create().resource(pt2).execute();
|
||||
myClient.create().resource(pt2).execute();
|
||||
|
||||
results = ourClient
|
||||
results = myClient
|
||||
.search()
|
||||
.forResource("Patient")
|
||||
.where(Patient.FAMILY.matches().value("FAM"))
|
||||
|
@ -132,6 +147,7 @@ public class ResourceProviderR4CacheTest extends BaseResourceProviderR4Test {
|
|||
.execute();
|
||||
assertEquals(2, results.getEntry().size());
|
||||
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();
|
||||
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();
|
||||
assertEquals(1, results.getEntry().size());
|
||||
Date beforeFirst = new Date();
|
||||
|
||||
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());
|
||||
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();
|
||||
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();
|
||||
assertEquals(1, results.getEntry().size());
|
||||
Bundle results2 = myClient.search().forResource("Patient").where(Patient.FAMILY.matches().value("FAM")).returnBundle(Bundle.class).execute();
|
||||
assertEquals(1, results2.getEntry().size());
|
||||
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
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue