Merging master into working branch.
# Conflicts: # hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java
This commit is contained in:
commit
8590e0ca4d
|
@ -16,6 +16,7 @@ ca.uhn.fhir.jpa.entity.ResourceTable/
|
|||
*.war
|
||||
*.ear
|
||||
|
||||
overlays/
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
1549576047000
|
||||
(?:[^/]+/)*?[^/]*?
|
||||
META-INF(?:$|/.+)
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -631,7 +631,10 @@ public class FhirContext {
|
|||
* Performance Note: <b>This method is cheap</b> to call, and may be called once for every message being processed
|
||||
* without incurring any performance penalty
|
||||
* </p>
|
||||
*
|
||||
* @deprecated THIS FEATURE IS NOT YET COMPLETE
|
||||
*/
|
||||
@Deprecated
|
||||
public IParser newRDFParser() {
|
||||
return new RDFParser(this, myParserErrorHandler, Lang.TURTLE);
|
||||
}
|
||||
|
|
|
@ -374,6 +374,61 @@ public enum Pointcut {
|
|||
),
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <b>Server Hook:</b>
|
||||
* This method is called after the server implementation method has been called, but before any attempt
|
||||
* to stream the response back to the client, specifically for GraphQL requests (as these do not fit
|
||||
* cleanly into the model provided by {@link #SERVER_OUTGOING_RESPONSE}).
|
||||
* <p>
|
||||
* Hooks may accept the following parameters:
|
||||
* <ul>
|
||||
* <li>
|
||||
* ca.uhn.fhir.rest.api.server.RequestDetails - A bean containing details about the request that is about to be processed, including details such as the
|
||||
* resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
|
||||
* pulled out of the servlet request.
|
||||
* </li>
|
||||
* <li>
|
||||
* ca.uhn.fhir.rest.server.servlet.ServletRequestDetails - A bean containing details about the request that is about to be processed, including details such as the
|
||||
* resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
|
||||
* pulled out of the servlet request. This parameter is identical to the RequestDetails parameter above but will
|
||||
* only be populated when operating in a RestfulServer implementation. It is provided as a convenience.
|
||||
* </li>
|
||||
* <li>
|
||||
* java.lang.String - The GraphQL query
|
||||
* </li>
|
||||
* <li>
|
||||
* java.lang.String - The GraphQL response
|
||||
* </li>
|
||||
* <li>
|
||||
* javax.servlet.http.HttpServletRequest - The servlet request, when running in a servlet environment
|
||||
* </li>
|
||||
* <li>
|
||||
* javax.servlet.http.HttpServletResponse - The servlet response, when running in a servlet environment
|
||||
* </li>
|
||||
* </ul>
|
||||
* </p>
|
||||
* <p>
|
||||
* Hook methods may return <code>true</code> or <code>void</code> if processing should continue normally.
|
||||
* This is generally the right thing to do. If your interceptor is providing a response rather than
|
||||
* letting HAPI handle the response normally, you must return <code>false</code>. In this case,
|
||||
* no further processing will occur and no further interceptors will be called.
|
||||
* </p>
|
||||
* <p>
|
||||
* Hook methods may also throw {@link AuthenticationException} to indicate that the interceptor
|
||||
* has detected an unauthorized access attempt. If thrown, processing will stop and an HTTP 401
|
||||
* will be returned to the client.
|
||||
*/
|
||||
SERVER_OUTGOING_GRAPHQL_RESPONSE(boolean.class,
|
||||
"ca.uhn.fhir.rest.api.server.RequestDetails",
|
||||
"ca.uhn.fhir.rest.server.servlet.ServletRequestDetails",
|
||||
"java.lang.String",
|
||||
"java.lang.String",
|
||||
"javax.servlet.http.HttpServletRequest",
|
||||
"javax.servlet.http.HttpServletResponse"
|
||||
),
|
||||
|
||||
|
||||
/**
|
||||
* <b>Server Hook:</b>
|
||||
* This method is called when an OperationOutcome is being returned in response to a failure.
|
||||
|
|
|
@ -138,6 +138,7 @@ public class Constants {
|
|||
* Used in paging links
|
||||
*/
|
||||
public static final String PARAM_BUNDLETYPE = "_bundletype";
|
||||
public static final String PARAM_FILTER = "_filter";
|
||||
public static final String PARAM_CONTENT = "_content";
|
||||
public static final String PARAM_COUNT = "_count";
|
||||
public static final String PARAM_DELETE = "_delete";
|
||||
|
@ -218,6 +219,7 @@ public class Constants {
|
|||
public static final int MAX_RESOURCE_NAME_LENGTH = 100;
|
||||
public static final String CACHE_CONTROL_PRIVATE = "private";
|
||||
public static final int STATUS_HTTP_412_PAYLOAD_TOO_LARGE = 413;
|
||||
public static final String OPERATION_NAME_GRAPHQL = "$graphql";
|
||||
|
||||
static {
|
||||
CHARSET_UTF8 = Charset.forName(CHARSET_NAME_UTF8);
|
||||
|
|
|
@ -28,6 +28,8 @@ import java.util.Collections;
|
|||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
|
||||
public enum EncodingEnum {
|
||||
|
||||
JSON(Constants.CT_FHIR_JSON, Constants.CT_FHIR_JSON_NEW, Constants.FORMAT_JSON) {
|
||||
|
@ -49,7 +51,9 @@ public enum EncodingEnum {
|
|||
public IParser newParser(FhirContext theContext) {
|
||||
return theContext.newRDFParser();
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
;
|
||||
|
||||
/**
|
||||
* "json"
|
||||
|
@ -164,7 +168,10 @@ public enum EncodingEnum {
|
|||
* is found.
|
||||
* <p>
|
||||
* <b>This method is lenient!</b> Things like "application/xml" will return {@link EncodingEnum#XML}
|
||||
* even if the "+fhir" part is missing from the expected content type.
|
||||
* even if the "+fhir" part is missing from the expected content type. Also,
|
||||
* spaces are treated as a plus (i.e. "application/fhir json" will be treated as
|
||||
* "application/fhir+json" in order to account for unescaped spaces in URL
|
||||
* parameters)
|
||||
* </p>
|
||||
*/
|
||||
public static EncodingEnum forContentType(final String theContentType) {
|
||||
|
@ -195,12 +202,35 @@ public enum EncodingEnum {
|
|||
}
|
||||
}
|
||||
|
||||
private static String getTypeWithoutCharset(final String theContentType) {
|
||||
if (theContentType == null) {
|
||||
static String getTypeWithoutCharset(final String theContentType) {
|
||||
if (isBlank(theContentType)) {
|
||||
return null;
|
||||
} else {
|
||||
String[] contentTypeSplitted = theContentType.split(";");
|
||||
return contentTypeSplitted[0];
|
||||
|
||||
int start = 0;
|
||||
for (; start < theContentType.length(); start++) {
|
||||
if (theContentType.charAt(start) != ' ') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
int end = start;
|
||||
for (; end < theContentType.length(); end++) {
|
||||
if (theContentType.charAt(end) == ';') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (; end > start; end--) {
|
||||
if (theContentType.charAt(end - 1) != ' ') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
String retVal = theContentType.substring(start, end);
|
||||
|
||||
if (retVal.contains(" ")) {
|
||||
retVal = retVal.replace(' ', '+');
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.apache.commons.lang3.builder.EqualsBuilder;
|
|||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import org.hl7.fhir.instance.model.api.IBaseCoding;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
@ -67,6 +68,16 @@ public class TokenParam extends BaseParam /*implements IQueryParameterType*/ {
|
|||
this(toSystemValue(theIdentifierDt.getSystemElement()), theIdentifierDt.getValueElement().getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a {@link TokenParam} from the {@link IBaseCoding#getSystem()} () system} and
|
||||
* {@link IBaseCoding#getCode()} () code} of a {@link IBaseCoding} instance.
|
||||
*
|
||||
* @param theCoding The coding
|
||||
*/
|
||||
public TokenParam(IBaseCoding theCoding) {
|
||||
this(theCoding.getSystem(), theCoding.getCode());
|
||||
}
|
||||
|
||||
public TokenParam(String theSystem, String theValue) {
|
||||
setSystem(theSystem);
|
||||
setValue(theValue);
|
||||
|
|
|
@ -163,7 +163,16 @@ public class UrlUtil {
|
|||
if (theString != null) {
|
||||
for (int i = 0; i < theString.length(); i++) {
|
||||
char nextChar = theString.charAt(i);
|
||||
if (nextChar == '<' || nextChar == '"') {
|
||||
switch (nextChar) {
|
||||
case '\'':
|
||||
case '"':
|
||||
case '<':
|
||||
case '>':
|
||||
case '\n':
|
||||
case '\r':
|
||||
return true;
|
||||
}
|
||||
if (nextChar < ' ') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -348,7 +357,17 @@ public class UrlUtil {
|
|||
|
||||
/**
|
||||
* This method specifically HTML-encodes the " and
|
||||
* < characters in order to prevent injection attacks
|
||||
* < characters in order to prevent injection attacks.
|
||||
*
|
||||
* The following characters are escaped:
|
||||
* <ul>
|
||||
* <li>'</li>
|
||||
* <li>"</li>
|
||||
* <li><</li>
|
||||
* <li>></li>
|
||||
* <li>\n (newline)</li>
|
||||
* </ul>
|
||||
*
|
||||
*/
|
||||
public static String sanitizeUrlPart(CharSequence theString) {
|
||||
if (theString == null) {
|
||||
|
@ -364,6 +383,10 @@ public class UrlUtil {
|
|||
|
||||
char nextChar = theString.charAt(j);
|
||||
switch (nextChar) {
|
||||
/*
|
||||
* NB: If you add a constant here, you also need to add it
|
||||
* to isNeedsSanitization()!!
|
||||
*/
|
||||
case '\'':
|
||||
buffer.append("'");
|
||||
break;
|
||||
|
@ -373,8 +396,19 @@ public class UrlUtil {
|
|||
case '<':
|
||||
buffer.append("<");
|
||||
break;
|
||||
case '>':
|
||||
buffer.append(">");
|
||||
break;
|
||||
case '\n':
|
||||
buffer.append(" ");
|
||||
break;
|
||||
case '\r':
|
||||
buffer.append(" ");
|
||||
break;
|
||||
default:
|
||||
buffer.append(nextChar);
|
||||
if (nextChar >= ' ') {
|
||||
buffer.append(nextChar);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,8 @@ public enum VersionEnum {
|
|||
V3_6_0,
|
||||
V3_7_0,
|
||||
V3_8_0,
|
||||
V4_0_0;
|
||||
V4_0_0,
|
||||
V4_1_0;
|
||||
|
||||
public static VersionEnum latestVersion() {
|
||||
VersionEnum[] values = VersionEnum.values();
|
||||
|
|
|
@ -56,4 +56,9 @@ public interface IBase extends Serializable {
|
|||
*/
|
||||
List<String> getFormatCommentsPost();
|
||||
|
||||
/**
|
||||
* Returns the FHIR type name for the given element, e.g. "Patient" or "unsignedInt"
|
||||
*/
|
||||
default String fhirType() { return null; }
|
||||
|
||||
}
|
||||
|
|
|
@ -60,23 +60,58 @@
|
|||
<tr th:class="${rowStat.odd}? 'hapiTableOfValuesRowOdd' : 'hapiTableOfValuesRowEven'" class="hapiTableOfValuesRowOdd">
|
||||
<td>
|
||||
<th:block th:switch="${fhirVersion}">
|
||||
<th:block th:case="'DSTU1'">
|
||||
<th:block th:if="${not result.resource.name.textElement.empty}" th:text="${result.resource.name.textElement.value}"/>
|
||||
<th:block th:if="${result.resource.name.textElement.empty} and ${not #lists.isEmpty(result.resource.name.coding)} and ${not result.resource.name.coding[0].empty} and ${not result.resource.name.coding[0].displayElement.empty}" th:text="${result.resource.name.coding[0].display}"/>
|
||||
<th:block th:if="${result.resource.name.textElement.empty} and ${not #lists.isEmpty(result.resource.name.coding)} and ${not result.resource.name.coding[0].empty} and ${result.resource.name.coding[0].displayElement.empty}" th:text="'?'"/>
|
||||
</th:block>
|
||||
<th:block th:case="'DSTU2'">
|
||||
<th:block th:if="${not result.resource.code.textElement.empty}" th:text="${result.resource.code.textElement.value}"/>
|
||||
<th:block th:if="${result.resource.code.textElement.empty} and ${not #lists.isEmpty(result.resource.code.coding)} and ${not result.resource.code.coding[0].empty} and ${not result.resource.code.coding[0].displayElement.empty}" th:text="${result.resource.code.coding[0].display}"/>
|
||||
<th:block th:if="${result.resource.code.textElement.empty} and ${not #lists.isEmpty(result.resource.code.coding)} and ${not result.resource.code.coding[0].empty} and ${result.resource.code.coding[0].displayElement.empty}" th:text="'?'"/>
|
||||
</th:block>
|
||||
<th:block th:case="'DSTU2_HL7ORG'">
|
||||
<th:block th:if="${not result.resource.code.textElement.empty}" th:text="${result.resource.code.textElement.value}"/>
|
||||
<th:block th:if="${result.resource.code.textElement.empty} and ${not #lists.isEmpty(result.resource.code.coding)} and ${not result.resource.code.coding[0].empty} and ${not result.resource.code.coding[0].displayElement.empty}" th:text="${result.resource.code.coding[0].display}"/>
|
||||
<th:block th:if="${result.resource.code.textElement.empty} and ${not #lists.isEmpty(result.resource.code.coding)} and ${not result.resource.code.coding[0].empty} and ${result.resource.code.coding[0].displayElement.empty}" th:text="'?'"/>
|
||||
</th:block>
|
||||
<th:block th:case="'DSTU2_1'">
|
||||
<th:block th:if="${not result.resource.code.textElement.empty}" th:text="${result.resource.code.textElement.value}"/>
|
||||
<th:block th:if="${result.resource.code.textElement.empty} and ${not #lists.isEmpty(result.resource.code.coding)} and ${not result.resource.code.coding[0].empty} and ${not result.resource.code.coding[0].displayElement.empty}" th:text="${result.resource.code.coding[0].display}"/>
|
||||
<th:block th:if="${result.resource.code.textElement.empty} and ${not #lists.isEmpty(result.resource.code.coding)} and ${not result.resource.code.coding[0].empty} and ${result.resource.code.coding[0].displayElement.empty}" th:text="'?'"/>
|
||||
</th:block>
|
||||
<th:block th:case="'DSTU3'">
|
||||
<th:block th:if="${not result.resource.code.textElement.empty}" th:text="${result.resource.code.textElement.value}"/>
|
||||
<th:block th:if="${result.resource.code.textElement.empty} and ${not #lists.isEmpty(result.resource.code.coding)} and ${not result.resource.code.coding[0].empty} and ${not result.resource.code.coding[0].displayElement.empty}" th:text="${result.resource.code.coding[0].display}"/>
|
||||
<th:block th:if="${result.resource.code.textElement.empty} and ${not #lists.isEmpty(result.resource.code.coding)} and ${not result.resource.code.coding[0].empty} and ${result.resource.code.coding[0].displayElement.empty}" th:text="'?'"/>
|
||||
</th:block>
|
||||
<th:block th:case="*">
|
||||
<th:block th:if="${not result.resource.code.textElement.empty}" th:text="${result.resource.code.textElement.value}"/>
|
||||
<th:block th:if="${result.resource.code.textElement.empty} and ${not #lists.isEmpty(result.resource.code.coding)} and ${not result.resource.code.coding[0].empty} and ${not result.resource.code.coding[0].displayElement.empty}" th:text="${result.resource.code.coding[0].display}"/>
|
||||
<th:block th:if="${result.resource.code.textElement.empty} and ${not #lists.isEmpty(result.resource.code.coding)} and ${not result.resource.code.coding[0].empty} and ${result.resource.code.coding[0].displayElement.empty}" th:text="'?'"/>
|
||||
<th:block th:if="${not result.resource.code.textElement.empty}" th:text="${result.resource.code.textElement.value}"/>
|
||||
<th:block th:if="${result.resource.code.textElement.empty} and ${not #lists.isEmpty(result.resource.code.coding)} and ${not result.resource.code.coding[0].empty} and ${not result.resource.code.coding[0].displayElement.empty}" th:text="${result.resource.code.coding[0].display}"/>
|
||||
<th:block th:if="${result.resource.code.textElement.empty} and ${not #lists.isEmpty(result.resource.code.coding)} and ${not result.resource.code.coding[0].empty} and ${result.resource.code.coding[0].displayElement.empty}" th:text="'?'"/>
|
||||
</th:block>
|
||||
</th:block>
|
||||
<!--/*--> Hb <!--*/-->
|
||||
</td>
|
||||
<td th:narrative="${result.resource.value}">2.2 g/L</td>
|
||||
<td>
|
||||
<th:block th:if="${not result.resource.interpretation.textElement.empty}" th:text="${result.resource.interpretation.text}"/>
|
||||
<th:block th:if="${result.resource.interpretation.textElement.empty} and ${not result.resource.interpretation.coding.empty} and ${not result.resource.interpretation.coding[0].displayElement.empty}" th:text="${result.resource.interpretation.coding[0].display}"/>
|
||||
<th:block th:switch="${fhirVersion}">
|
||||
<th:block th:case="'DSTU2'">
|
||||
<th:block th:if="${not result.resource.interpretation.textElement.empty}" th:text="${result.resource.interpretation.text}"/>
|
||||
<th:block th:if="${result.resource.interpretation.textElement.empty} and ${not result.resource.interpretation.coding.empty} and ${not result.resource.interpretation.coding[0].displayElement.empty}" th:text="${result.resource.interpretation.coding[0].display}"/>
|
||||
</th:block>
|
||||
<th:block th:case="'DSTU2_HL7ORG'">
|
||||
<th:block th:if="${not result.resource.interpretation.textElement.empty}" th:text="${result.resource.interpretation.text}"/>
|
||||
<th:block th:if="${result.resource.interpretation.textElement.empty} and ${not result.resource.interpretation.coding.empty} and ${not result.resource.interpretation.coding[0].displayElement.empty}" th:text="${result.resource.interpretation.coding[0].display}"/>
|
||||
</th:block>
|
||||
<th:block th:case="'DSTU2_1'">
|
||||
<th:block th:if="${not result.resource.interpretation.textElement.empty}" th:text="${result.resource.interpretation.text}"/>
|
||||
<th:block th:if="${result.resource.interpretation.textElement.empty} and ${not result.resource.interpretation.coding.empty} and ${not result.resource.interpretation.coding[0].displayElement.empty}" th:text="${result.resource.interpretation.coding[0].display}"/>
|
||||
</th:block>
|
||||
<th:block th:case="'DSTU3'">
|
||||
<th:block th:if="${not result.resource.interpretation.textElement.empty}" th:text="${result.resource.interpretation.text}"/>
|
||||
<th:block th:if="${result.resource.interpretation.textElement.empty} and ${not result.resource.interpretation.coding.empty} and ${not result.resource.interpretation.coding[0].displayElement.empty}" th:text="${result.resource.interpretation.coding[0].display}"/>
|
||||
</th:block>
|
||||
<th:block th:case="*">
|
||||
<th:block th:if="${not result.resource.interpretation.empty} and ${not result.resource.interpretation[0].textElement.empty}" th:text="${result.resource.interpretation[0].text}"/>
|
||||
<th:block th:if="${not result.resource.interpretation.empty} and ${result.resource.interpretation[0].textElement.empty} and ${not result.resource.interpretation[0].coding.empty} and ${not result.resource.interpretation[0].coding[0].displayElement.empty}" th:text="${result.resource.interpretation.coding[0].display}"/>
|
||||
</th:block>
|
||||
</th:block>
|
||||
<!--/*--> N <!--*/-->
|
||||
</td>
|
||||
<td>
|
||||
|
@ -90,16 +125,31 @@
|
|||
</tr>
|
||||
|
||||
<th:block th:switch="${fhirVersion}">
|
||||
<th:block th:case="'DSTU3'">
|
||||
<tr th:if="${not result.resource.commentElement.empty}" th:class="${rowStat.odd}? 'hapiTableOfValuesRowOdd' : 'hapiTableOfValuesRowEven'" class="hapiTableOfValuesRowOdd">
|
||||
<td th:text="${result.resource.commentElement.value}" colspan="5">This is a comment</td>
|
||||
</tr>
|
||||
</th:block>
|
||||
<th:block th:case="*">
|
||||
<th:block th:case="'DSTU2'">
|
||||
<tr th:if="${not result.resource.commentsElement.empty}" th:class="${rowStat.odd}? 'hapiTableOfValuesRowOdd' : 'hapiTableOfValuesRowEven'" class="hapiTableOfValuesRowOdd">
|
||||
<td th:text="${result.resource.commentsElement.value}" colspan="5">This is a comment</td>
|
||||
</tr>
|
||||
</th:block>
|
||||
<th:block th:case="'DSTU2_HL7ORG'">
|
||||
<tr th:if="${not result.resource.commentElement.empty}" th:class="${rowStat.odd}? 'hapiTableOfValuesRowOdd' : 'hapiTableOfValuesRowEven'" class="hapiTableOfValuesRowOdd">
|
||||
<td th:text="${result.resource.commentElement.value}" colspan="5">This is a comment</td>
|
||||
</tr>
|
||||
</th:block>
|
||||
<th:block th:case="'DSTU2_1'">
|
||||
<tr th:if="${not result.resource.commentElement.empty}" th:class="${rowStat.odd}? 'hapiTableOfValuesRowOdd' : 'hapiTableOfValuesRowEven'" class="hapiTableOfValuesRowOdd">
|
||||
<td th:text="${result.resource.commentElement.value}" colspan="5">This is a comment</td>
|
||||
</tr>
|
||||
</th:block>
|
||||
<th:block th:case="'DSTU3'">
|
||||
<tr th:if="${not result.resource.commentElement.empty}" th:class="${rowStat.odd}? 'hapiTableOfValuesRowOdd' : 'hapiTableOfValuesRowEven'" class="hapiTableOfValuesRowOdd">
|
||||
<td th:text="${result.resource.commentElement.value}" colspan="5">This is a comment</td>
|
||||
</tr>
|
||||
</th:block>
|
||||
<th:block th:case="*">
|
||||
<tr th:if="${not result.resource.note.empty}" th:class="${rowStat.odd}? 'hapiTableOfValuesRowOdd' : 'hapiTableOfValuesRowEven'" class="hapiTableOfValuesRowOdd">
|
||||
<td th:text="${result.resource.note[0].text}" colspan="5">This is a comment</td>
|
||||
</tr>
|
||||
</th:block>
|
||||
</th:block>
|
||||
|
||||
<!--/* The following are just examples that don't appear in the generated narrative -->
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
package ca.uhn.fhir.rest.api;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class EncodingEnumTest {
|
||||
|
||||
@Test
|
||||
public void getTypeWithoutCharset() {
|
||||
assertEquals("text/plain", EncodingEnum.getTypeWithoutCharset("text/plain"));
|
||||
assertEquals("text/plain", EncodingEnum.getTypeWithoutCharset(" text/plain"));
|
||||
assertEquals("text/plain", EncodingEnum.getTypeWithoutCharset(" text/plain; charset=utf-8"));
|
||||
assertEquals("text/plain", EncodingEnum.getTypeWithoutCharset(" text/plain ; charset=utf-8"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getTypeWithSpace() {
|
||||
assertEquals("application/fhir+xml", EncodingEnum.getTypeWithoutCharset("application/fhir xml"));
|
||||
assertEquals("application/fhir+xml", EncodingEnum.getTypeWithoutCharset("application/fhir xml; charset=utf-8"));
|
||||
assertEquals("application/fhir+xml", EncodingEnum.getTypeWithoutCharset("application/fhir xml ; charset=utf-8"));
|
||||
}
|
||||
|
||||
}
|
|
@ -59,4 +59,15 @@ public class UrlUtilTest {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSanitize() {
|
||||
assertEquals(" ' ", UrlUtil.sanitizeUrlPart(" ' "));
|
||||
assertEquals(" < ", UrlUtil.sanitizeUrlPart(" < "));
|
||||
assertEquals(" > ", UrlUtil.sanitizeUrlPart(" > "));
|
||||
assertEquals(" " ", UrlUtil.sanitizeUrlPart(" \" "));
|
||||
assertEquals(" ", UrlUtil.sanitizeUrlPart(" \n "));
|
||||
assertEquals(" ", UrlUtil.sanitizeUrlPart(" \r "));
|
||||
assertEquals(" ", UrlUtil.sanitizeUrlPart(" \0 "));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,14 +3,14 @@
|
|||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-bom</artifactId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>HAPI FHIR BOM</name>
|
||||
|
||||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-cli</artifactId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-cli</artifactId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -24,10 +24,8 @@ import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
|||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
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 org.hl7.fhir.r4.hapi.rest.server.GraphQLProvider;
|
||||
import org.springframework.web.context.ContextLoaderListener;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import ca.uhn.fhir.interceptor.executor.InterceptorService;
|
|||
import ca.uhn.fhir.jpa.binstore.BinaryAccessProvider;
|
||||
import ca.uhn.fhir.jpa.binstore.BinaryStorageInterceptor;
|
||||
import ca.uhn.fhir.jpa.dao.DaoRegistry;
|
||||
import ca.uhn.fhir.jpa.graphql.JpaStorageServices;
|
||||
import ca.uhn.fhir.jpa.interceptor.JpaConsentContextServices;
|
||||
import ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider;
|
||||
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
|
||||
|
@ -23,6 +24,7 @@ import ca.uhn.fhir.jpa.subscription.module.matcher.ISubscriptionMatcher;
|
|||
import ca.uhn.fhir.jpa.subscription.module.matcher.InMemorySubscriptionMatcher;
|
||||
import ca.uhn.fhir.rest.server.interceptor.consent.IConsentContextServices;
|
||||
import org.hibernate.jpa.HibernatePersistenceProvider;
|
||||
import org.hl7.fhir.utilities.graphql.IGraphQLStorageServices;
|
||||
import org.springframework.beans.factory.annotation.Autowire;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.*;
|
||||
|
@ -109,6 +111,12 @@ public abstract class BaseConfig implements SchedulingConfigurer {
|
|||
|
||||
public abstract FhirContext fhirContext();
|
||||
|
||||
@Bean
|
||||
@Lazy
|
||||
public IGraphQLStorageServices graphqlStorageServices() {
|
||||
return new JpaStorageServices();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ScheduledExecutorFactoryBean scheduledExecutorService() {
|
||||
ScheduledExecutorFactoryBean b = new ScheduledExecutorFactoryBean();
|
||||
|
|
|
@ -8,6 +8,7 @@ import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
|||
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
|
||||
import ca.uhn.fhir.jpa.dao.TransactionProcessor;
|
||||
import ca.uhn.fhir.jpa.dao.dstu3.TransactionProcessorVersionAdapterDstu3;
|
||||
import ca.uhn.fhir.jpa.provider.GraphQLProvider;
|
||||
import ca.uhn.fhir.jpa.provider.dstu3.TerminologyUploaderProviderDstu3;
|
||||
import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorDstu3;
|
||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||
|
@ -73,6 +74,12 @@ public class BaseDstu3Config extends BaseConfig {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
@Bean(name = GRAPHQL_PROVIDER_NAME)
|
||||
@Lazy
|
||||
public GraphQLProvider graphQLProvider() {
|
||||
return new GraphQLProvider(fhirContextDstu3(), validationSupportChainDstu3(), graphqlStorageServices());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TransactionProcessor.ITransactionProcessorVersionAdapter transactionProcessorVersionFacade() {
|
||||
return new TransactionProcessorVersionAdapterDstu3();
|
||||
|
|
|
@ -21,12 +21,13 @@ import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainR4;
|
|||
import ca.uhn.fhir.validation.IValidatorModule;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
|
||||
import org.hl7.fhir.r4.hapi.rest.server.GraphQLProvider;
|
||||
import ca.uhn.fhir.jpa.provider.GraphQLProvider;
|
||||
import org.hl7.fhir.r4.hapi.validation.CachingValidationSupport;
|
||||
import org.hl7.fhir.r4.hapi.validation.FhirInstanceValidator;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.utils.GraphQLEngine;
|
||||
import org.hl7.fhir.r5.utils.IResourceValidator;
|
||||
import org.hl7.fhir.utilities.graphql.IGraphQLStorageServices;
|
||||
import org.springframework.beans.factory.annotation.Autowire;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
@ -91,12 +92,6 @@ public class BaseR4Config extends BaseConfig {
|
|||
return new GraphQLProvider(fhirContextR4(), validationSupportChainR4(), graphqlStorageServices());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Lazy
|
||||
public GraphQLEngine.IGraphQLStorageServices graphqlStorageServices() {
|
||||
return new JpaStorageServices();
|
||||
}
|
||||
|
||||
@Bean(name = "myInstanceValidatorR4")
|
||||
@Lazy
|
||||
public IValidatorModule instanceValidatorR4() {
|
||||
|
|
|
@ -8,7 +8,7 @@ import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
|||
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
|
||||
import ca.uhn.fhir.jpa.dao.TransactionProcessor;
|
||||
import ca.uhn.fhir.jpa.dao.r5.TransactionProcessorVersionAdapterR5;
|
||||
import ca.uhn.fhir.jpa.graphql.JpaStorageServices;
|
||||
import ca.uhn.fhir.jpa.provider.GraphQLProvider;
|
||||
import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorR5;
|
||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||
import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryR5;
|
||||
|
@ -21,11 +21,9 @@ import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainR5;
|
|||
import ca.uhn.fhir.validation.IValidatorModule;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.hl7.fhir.r5.hapi.ctx.IValidationSupport;
|
||||
import org.hl7.fhir.r5.hapi.rest.server.GraphQLProvider;
|
||||
import org.hl7.fhir.r5.hapi.validation.CachingValidationSupport;
|
||||
import org.hl7.fhir.r5.hapi.validation.FhirInstanceValidator;
|
||||
import org.hl7.fhir.r5.model.Bundle;
|
||||
import org.hl7.fhir.r5.utils.GraphQLEngine;
|
||||
import org.hl7.fhir.r5.utils.IResourceValidator;
|
||||
import org.springframework.beans.factory.annotation.Autowire;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
@ -85,17 +83,11 @@ public class BaseR5Config extends BaseConfig {
|
|||
return new TransactionProcessor<>();
|
||||
}
|
||||
|
||||
// @Bean(name = GRAPHQL_PROVIDER_NAME)
|
||||
// @Lazy
|
||||
// public GraphQLProvider graphQLProvider() {
|
||||
// return new GraphQLProvider(fhirContextR5(), validationSupportChainR5(), graphqlStorageServices());
|
||||
// }
|
||||
//
|
||||
// @Bean
|
||||
// @Lazy
|
||||
// public GraphQLEngine.IGraphQLStorageServices graphqlStorageServices() {
|
||||
// return new JpaStorageServices();
|
||||
// }
|
||||
@Bean(name = GRAPHQL_PROVIDER_NAME)
|
||||
@Lazy
|
||||
public GraphQLProvider graphQLProvider() {
|
||||
return new GraphQLProvider(fhirContextR5(), validationSupportChainR5(), graphqlStorageServices());
|
||||
}
|
||||
|
||||
@Bean(name = "myInstanceValidatorR5")
|
||||
@Lazy
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ca.uhn.fhir.jpa.dao;
|
||||
|
||||
import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor;
|
||||
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceEncodingEnum;
|
||||
import ca.uhn.fhir.jpa.search.warm.WarmCacheEntry;
|
||||
|
@ -149,6 +150,7 @@ public class DaoConfig {
|
|||
* EXPERIMENTAL - Do not use in production! Do not change default of {@code false}!
|
||||
*/
|
||||
private boolean myPreExpandValueSetsExperimental = false;
|
||||
private boolean myFilterParameterEnabled = false;
|
||||
/**
|
||||
* EXPERIMENTAL - Do not use in production! Do not change default of {@code 0}!
|
||||
*/
|
||||
|
@ -984,7 +986,7 @@ public class DaoConfig {
|
|||
* and other FHIR features may not behave as expected when referential integrity is not
|
||||
* preserved. Use this feature with caution.
|
||||
* </p>
|
||||
* @see ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor
|
||||
* @see CascadingDeleteInterceptor
|
||||
*/
|
||||
public boolean isEnforceReferentialIntegrityOnDelete() {
|
||||
return myEnforceReferentialIntegrityOnDelete;
|
||||
|
@ -998,7 +1000,7 @@ public class DaoConfig {
|
|||
* and other FHIR features may not behave as expected when referential integrity is not
|
||||
* preserved. Use this feature with caution.
|
||||
* </p>
|
||||
* @see ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor
|
||||
* @see CascadingDeleteInterceptor
|
||||
*/
|
||||
public void setEnforceReferentialIntegrityOnDelete(boolean theEnforceReferentialIntegrityOnDelete) {
|
||||
myEnforceReferentialIntegrityOnDelete = theEnforceReferentialIntegrityOnDelete;
|
||||
|
@ -1644,6 +1646,28 @@ public class DaoConfig {
|
|||
myPreExpandValueSetsExperimental = thePreExpandValueSetsExperimental;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to <code>true</code> the _filter search parameter will be enabled on this server. Note that _filter
|
||||
* is very powerful, but also potentially dangerous as it can allow a user to create a query for which there
|
||||
* are no indexes or efficient query plans for the database to leverage while performing the query.
|
||||
* As a result, this feature is recommended only for servers where the querying applications are known in advance
|
||||
* and a database administrator can properly tune the database for the resulting queries.
|
||||
*/
|
||||
public boolean isFilterParameterEnabled() {
|
||||
return myFilterParameterEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to <code>true</code> the _filter search parameter will be enabled on this server. Note that _filter
|
||||
* is very powerful, but also potentially dangerous as it can allow a user to create a query for which there
|
||||
* are no indexes or efficient query plans for the database to leverage while performing the query.
|
||||
* As a result, this feature is recommended only for servers where the querying applications are known in advance
|
||||
* and a database administrator can properly tune the database for the resulting queries.
|
||||
*/
|
||||
public void setFilterParameterEnabled(boolean theFilterParameterEnabled) {
|
||||
myFilterParameterEnabled = theFilterParameterEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* EXPERIMENTAL - Do not use in production!
|
||||
* <p>
|
||||
|
|
|
@ -138,7 +138,7 @@ public class DaoRegistry implements ApplicationContextAware, IDaoRegistry {
|
|||
|
||||
@Override
|
||||
public boolean isResourceTypeSupported(String theResourceType) {
|
||||
return mySupportedResourceTypes.contains(theResourceType);
|
||||
return mySupportedResourceTypes == null || mySupportedResourceTypes.contains(theResourceType);
|
||||
}
|
||||
|
||||
private void init() {
|
||||
|
|
|
@ -43,7 +43,7 @@ public class FhirResourceDaoPatientDstu2 extends FhirResourceDaoDstu2<Patient>im
|
|||
super();
|
||||
}
|
||||
|
||||
private IBundleProvider doEverythingOperation(IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, RequestDetails theRequest) {
|
||||
private IBundleProvider doEverythingOperation(IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequest) {
|
||||
SearchParameterMap paramMap = new SearchParameterMap();
|
||||
if (theCount != null) {
|
||||
paramMap.setCount(theCount.getValue());
|
||||
|
@ -70,21 +70,21 @@ public class FhirResourceDaoPatientDstu2 extends FhirResourceDaoDstu2<Patient>im
|
|||
}
|
||||
|
||||
@Override
|
||||
public IBundleProvider patientInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, RequestDetails theRequestDetails) {
|
||||
public IBundleProvider patientInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails) {
|
||||
// Notify interceptors
|
||||
ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getResourceName(), null);
|
||||
notifyInterceptors(RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE, requestDetails);
|
||||
|
||||
return doEverythingOperation(theId, theCount, theLastUpdated, theSort, theContent, theNarrative, theRequestDetails);
|
||||
return doEverythingOperation(theId, theCount, theLastUpdated, theSort, theContent, theNarrative, theFilter, theRequestDetails);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBundleProvider patientTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, RequestDetails theRequestDetails) {
|
||||
public IBundleProvider patientTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails) {
|
||||
// Notify interceptors
|
||||
ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getResourceName(), null);
|
||||
notifyInterceptors(RestOperationTypeEnum.EXTENDED_OPERATION_TYPE, requestDetails);
|
||||
|
||||
return doEverythingOperation(null, theCount, theLastUpdated, theSort, theContent, theNarrative, theRequestDetails);
|
||||
return doEverythingOperation(null, theCount, theLastUpdated, theSort, theContent, theNarrative, theFilter, theRequestDetails);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,14 @@
|
|||
package ca.uhn.fhir.jpa.dao;
|
||||
|
||||
import ca.uhn.fhir.rest.api.SortSpec;
|
||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
import ca.uhn.fhir.rest.param.StringAndListParam;
|
||||
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 javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/*
|
||||
|
@ -21,19 +30,12 @@ import javax.servlet.http.HttpServletRequest;
|
|||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
import org.hl7.fhir.instance.model.api.*;
|
||||
|
||||
import ca.uhn.fhir.rest.api.SortSpec;
|
||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
import ca.uhn.fhir.rest.param.StringAndListParam;
|
||||
|
||||
public interface IFhirResourceDaoPatient<T extends IBaseResource> extends IFhirResourceDao<T> {
|
||||
|
||||
IBundleProvider patientInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdate, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, RequestDetails theRequestDetails);
|
||||
IBundleProvider patientInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdate, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails);
|
||||
|
||||
IBundleProvider patientTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSortSpec, StringAndListParam theContent, StringAndListParam theNarrative, RequestDetails theRequestDetails);
|
||||
IBundleProvider patientTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSortSpec, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails);
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -28,12 +28,13 @@ import ca.uhn.fhir.rest.api.server.RequestDetails;
|
|||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
public interface ISearchBuilder {
|
||||
|
||||
IResultIterator createQuery(SearchParameterMap theParams, SearchRuntimeDetails theSearchRuntime, RequestDetails theRequest);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,617 @@
|
|||
package ca.uhn.fhir.jpa.dao;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2019 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.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class SearchFilterParser {
|
||||
|
||||
private static final String XML_DATE_PATTERN = "[0-9]{4}(-(0[1-9]|1[0-2])(-(0[0-9]|[1-2][0-9]|3[0-1])(T([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\\.[0-9]+)?(Z|([+\\-])((0[0-9]|1[0-3]):[0-5][0-9]|14:00))?)?)?)?";
|
||||
private static final Pattern XML_DATE_MATCHER = Pattern.compile(XML_DATE_PATTERN);
|
||||
private static final List<String> CODES_CompareOperation = Arrays.asList("eq", "ne", "co", "sw", "ew", "gt", "lt", "ge", "le", "pr", "po", "ss", "sb", "in", "re");
|
||||
private static final List<String> CODES_LogicalOperation = Arrays.asList("and", "or", "not");
|
||||
private String original = null;
|
||||
private int cursor;
|
||||
|
||||
private boolean isDate(String s) {
|
||||
Matcher m = XML_DATE_MATCHER.matcher(s);
|
||||
return m.matches();
|
||||
}
|
||||
|
||||
private FilterLexType peek() throws FilterSyntaxException {
|
||||
|
||||
FilterLexType result;
|
||||
while ((cursor < original.length()) && (original.charAt(cursor) == ' ')) {
|
||||
cursor++;
|
||||
}
|
||||
|
||||
if (cursor >= original.length()) {
|
||||
result = FilterLexType.fsltEnded;
|
||||
} else {
|
||||
if (((original.charAt(cursor) >= 'a') && (original.charAt(cursor) <= 'z')) ||
|
||||
((original.charAt(cursor) >= 'A') && (original.charAt(cursor) <= 'Z')) ||
|
||||
(original.charAt(cursor) == '_')) {
|
||||
result = FilterLexType.fsltName;
|
||||
} else if ((original.charAt(cursor) >= '0') && (original.charAt(cursor) <= '9')) {
|
||||
result = FilterLexType.fsltNumber;
|
||||
} else if (original.charAt(cursor) == '"') {
|
||||
result = FilterLexType.fsltString;
|
||||
} else if (original.charAt(cursor) == '.') {
|
||||
result = FilterLexType.fsltDot;
|
||||
} else if (original.charAt(cursor) == '(') {
|
||||
result = FilterLexType.fsltOpen;
|
||||
} else if (original.charAt(cursor) == ')') {
|
||||
result = FilterLexType.fsltClose;
|
||||
} else if (original.charAt(cursor) == '[') {
|
||||
result = FilterLexType.fsltOpenSq;
|
||||
} else if (original.charAt(cursor) == ']') {
|
||||
result = FilterLexType.fsltCloseSq;
|
||||
} else {
|
||||
throw new FilterSyntaxException(String.format("Unknown Character \"%s\" at %d",
|
||||
peekCh(),
|
||||
cursor));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private String peekCh() {
|
||||
|
||||
String result;
|
||||
if (cursor > original.length()) {
|
||||
result = "[end!]";
|
||||
} else {
|
||||
result = original.substring(cursor, cursor + 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private String consumeName() {
|
||||
|
||||
String result;
|
||||
int i = cursor;
|
||||
do {
|
||||
i++;
|
||||
} while ((i <= original.length() - 1) &&
|
||||
(((original.charAt(i) >= 'a') && (original.charAt(i) <= 'z')) ||
|
||||
((original.charAt(i) >= 'A') && (original.charAt(i) <= 'Z')) ||
|
||||
((original.charAt(i) >= '0') && (original.charAt(i) <= '9')) ||
|
||||
(original.charAt(i) == '-') ||
|
||||
(original.charAt(i) == '_') ||
|
||||
(original.charAt(i) == ':')));
|
||||
|
||||
result = original.substring(cursor,
|
||||
i/* - cursor*/);
|
||||
cursor = i;
|
||||
return result;
|
||||
}
|
||||
|
||||
private String consumeToken() {
|
||||
|
||||
String result;
|
||||
int i = cursor;
|
||||
do {
|
||||
i++;
|
||||
} while ((i <= original.length() - 1) &&
|
||||
(original.charAt(i) > 32) &&
|
||||
(!StringUtils.isWhitespace(original.substring(i, i + 1))) &&
|
||||
(original.charAt(i) != ')') &&
|
||||
(original.charAt(i) != ']'));
|
||||
result = original.substring(cursor,
|
||||
i/* - cursor*/);
|
||||
cursor = i;
|
||||
return result;
|
||||
}
|
||||
|
||||
private String consumeNumberOrDate() {
|
||||
|
||||
String result;
|
||||
int i = cursor;
|
||||
do {
|
||||
i++;
|
||||
} while ((i <= original.length() - 1) &&
|
||||
(((original.charAt(i) >= '0') && (original.charAt(i) <= '9')) ||
|
||||
(original.charAt(i) == '.') ||
|
||||
(original.charAt(i) == '-') ||
|
||||
(original.charAt(i) == ':') ||
|
||||
(original.charAt(i) == '+') ||
|
||||
(original.charAt(i) == 'T')));
|
||||
result = original.substring(cursor,
|
||||
i/* - cursor*/);
|
||||
cursor = i;
|
||||
return result;
|
||||
}
|
||||
|
||||
private String consumeString() throws FilterSyntaxException {
|
||||
|
||||
// int l = 0;
|
||||
cursor++;
|
||||
StringBuilder str = new StringBuilder(original.length());
|
||||
// setLength(result, length(original)); // can't be longer than that
|
||||
while ((cursor <= original.length()) && (original.charAt(cursor) != '"')) {
|
||||
// l++;
|
||||
if (original.charAt(cursor) != '\\') {
|
||||
str.append(original.charAt(cursor));
|
||||
// str.setCharAt(l, original.charAt(cursor));
|
||||
} else {
|
||||
cursor++;
|
||||
if (original.charAt(cursor) == '"') {
|
||||
str.append('"');
|
||||
// str.setCharAt(l, '"');
|
||||
} else if (original.charAt(cursor) == 't') {
|
||||
str.append('\t');
|
||||
// str.setCharAt(l, '\t');
|
||||
} else if (original.charAt(cursor) == 'r') {
|
||||
str.append('\r');
|
||||
// str.setCharAt(l, '\r');
|
||||
} else if (original.charAt(cursor) == 'n') {
|
||||
str.append('\n');
|
||||
// str.setCharAt(l, '\n');
|
||||
} else {
|
||||
throw new FilterSyntaxException(String.format("Unknown escape sequence at %d",
|
||||
cursor));
|
||||
}
|
||||
}
|
||||
cursor++;
|
||||
}
|
||||
// SetLength(result, l);
|
||||
if ((cursor > original.length()) || (original.charAt(cursor) != '"')) {
|
||||
throw new FilterSyntaxException(String.format("Problem with string termination at %d",
|
||||
cursor));
|
||||
}
|
||||
|
||||
if (str.length() == 0) {
|
||||
throw new FilterSyntaxException(String.format("Problem with string at %d cannot be empty",
|
||||
cursor));
|
||||
}
|
||||
|
||||
cursor++;
|
||||
return str.toString();
|
||||
}
|
||||
|
||||
private Filter parse() throws FilterSyntaxException {
|
||||
|
||||
Filter result = parseOpen();
|
||||
if (cursor < original.length()) {
|
||||
throw new FilterSyntaxException(String.format("Expression did not terminate at %d",
|
||||
cursor));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Filter parseOpen() throws FilterSyntaxException {
|
||||
|
||||
Filter result;
|
||||
String s;
|
||||
FilterParameterGroup grp;
|
||||
if (peek() == FilterLexType.fsltOpen) {
|
||||
cursor++;
|
||||
grp = new FilterParameterGroup();
|
||||
grp.setContained(parseOpen());
|
||||
if (peek() != FilterLexType.fsltClose) {
|
||||
throw new FilterSyntaxException(String.format("Expected ')' at %d but found %s",
|
||||
cursor,
|
||||
peekCh()));
|
||||
}
|
||||
cursor++;
|
||||
FilterLexType lexType = peek();
|
||||
if (lexType == FilterLexType.fsltName) {
|
||||
result = parseLogical(grp);
|
||||
} else if ((lexType == FilterLexType.fsltEnded) || (lexType == FilterLexType.fsltClose) || (lexType == FilterLexType.fsltCloseSq)) {
|
||||
result = grp;
|
||||
} else {
|
||||
throw new FilterSyntaxException(String.format("Unexpected Character %s at %d",
|
||||
peekCh(),
|
||||
cursor));
|
||||
}
|
||||
} else {
|
||||
s = consumeName();
|
||||
if (s.compareToIgnoreCase("not") == 0) {
|
||||
result = parseLogical(null);
|
||||
} else {
|
||||
result = parseParameter(s);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Filter parseLogical(Filter filter) throws FilterSyntaxException {
|
||||
|
||||
Filter result = null;
|
||||
String s;
|
||||
FilterLogical logical;
|
||||
if (filter == null) {
|
||||
s = "not";
|
||||
} else {
|
||||
s = consumeName();
|
||||
if ((!s.equals("or")) && (!s.equals("and")) && (!s.equals("not"))) {
|
||||
throw new FilterSyntaxException(String.format("Unexpected Name %s at %d",
|
||||
s,
|
||||
cursor));
|
||||
}
|
||||
|
||||
logical = new FilterLogical();
|
||||
logical.setFilter1(filter);
|
||||
if (s.compareToIgnoreCase("or") == 0) {
|
||||
logical.setOperation(FilterLogicalOperation.or);
|
||||
} else if (s.compareToIgnoreCase("not") == 0) {
|
||||
logical.setOperation(FilterLogicalOperation.not);
|
||||
} else {
|
||||
logical.setOperation(FilterLogicalOperation.and);
|
||||
}
|
||||
|
||||
logical.setFilter2(parseOpen());
|
||||
result = logical;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private FilterParameterPath parsePath(String name) throws FilterSyntaxException {
|
||||
|
||||
FilterParameterPath result = new FilterParameterPath();
|
||||
result.setName(name);
|
||||
if (peek() == FilterLexType.fsltOpenSq) {
|
||||
cursor++;
|
||||
result.setFilter(parseOpen());
|
||||
if (peek() != FilterLexType.fsltCloseSq) {
|
||||
throw new FilterSyntaxException(String.format("Expected ']' at %d but found %c",
|
||||
cursor,
|
||||
peekCh()));
|
||||
}
|
||||
cursor++;
|
||||
}
|
||||
|
||||
if (peek() == FilterLexType.fsltDot) {
|
||||
cursor++;
|
||||
if (peek() != FilterLexType.fsltName) {
|
||||
throw new FilterSyntaxException(String.format("Unexpected Character %c at %d",
|
||||
peekCh(),
|
||||
cursor));
|
||||
}
|
||||
result.setNext(parsePath(consumeName()));
|
||||
} else if (result.getFilter() != null) {
|
||||
throw new FilterSyntaxException(String.format("Expected '.' at %d but found %c",
|
||||
cursor,
|
||||
peekCh()));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Filter parseParameter(String name) throws FilterSyntaxException {
|
||||
|
||||
Filter result;
|
||||
String s;
|
||||
FilterParameter filter = new FilterParameter();
|
||||
|
||||
// 1. the path
|
||||
filter.setParamPath(parsePath(name));
|
||||
|
||||
if (peek() != FilterLexType.fsltName) {
|
||||
throw new FilterSyntaxException(String.format("Unexpected Character %s at %d",
|
||||
peekCh(),
|
||||
cursor));
|
||||
}
|
||||
s = consumeName();
|
||||
int index = CODES_CompareOperation.indexOf(s);
|
||||
if (index == -1) {
|
||||
throw new FilterSyntaxException(String.format("Unknown operation %s at %d",
|
||||
s,
|
||||
cursor));
|
||||
}
|
||||
filter.setOperation(CompareOperation.values()[index]);
|
||||
|
||||
FilterLexType lexType = peek();
|
||||
if (lexType == FilterLexType.fsltName) {
|
||||
filter.setValue(consumeToken());
|
||||
filter.setValueType(FilterValueType.token);
|
||||
} else if (lexType == FilterLexType.fsltNumber) {
|
||||
filter.setValue(consumeNumberOrDate());
|
||||
filter.setValueType(FilterValueType.numberOrDate);
|
||||
} else if (lexType == FilterLexType.fsltString) {
|
||||
filter.setValue(consumeString());
|
||||
filter.setValueType(FilterValueType.string);
|
||||
} else {
|
||||
throw new FilterSyntaxException(String.format("Unexpected Character %s at %d",
|
||||
peekCh(),
|
||||
cursor));
|
||||
}
|
||||
|
||||
// check operation / value type results
|
||||
if (filter.getOperation() == CompareOperation.pr) {
|
||||
if ((filter.getValue().compareToIgnoreCase("true") != 0) &&
|
||||
(filter.getValue().compareToIgnoreCase("false") != 0)) {
|
||||
throw new FilterSyntaxException(String.format("Value %s not valid for operation %s at %d",
|
||||
filter.getValue(),
|
||||
CODES_CompareOperation.get(filter.getOperation().ordinal()),
|
||||
cursor));
|
||||
}
|
||||
} else if (filter.getOperation() == CompareOperation.po) {
|
||||
if (!isDate(filter.getValue())) {
|
||||
throw new FilterSyntaxException(String.format("Value %s not valid for operation %s at %d",
|
||||
filter.getValue(),
|
||||
CODES_CompareOperation.get(filter.getOperation().ordinal()),
|
||||
cursor));
|
||||
}
|
||||
}
|
||||
|
||||
lexType = peek();
|
||||
if (lexType == FilterLexType.fsltName) {
|
||||
result = parseLogical(filter);
|
||||
} else if ((lexType == FilterLexType.fsltEnded) || (lexType == FilterLexType.fsltClose) || (lexType == FilterLexType.fsltCloseSq)) {
|
||||
result = filter;
|
||||
} else {
|
||||
throw new FilterSyntaxException(String.format("Unexpected Character %s at %d",
|
||||
peekCh(),
|
||||
cursor));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public enum CompareOperation {
|
||||
eq,
|
||||
ne,
|
||||
co,
|
||||
sw,
|
||||
ew,
|
||||
gt,
|
||||
lt,
|
||||
ge,
|
||||
le,
|
||||
pr,
|
||||
po,
|
||||
ss,
|
||||
sb,
|
||||
in,
|
||||
re
|
||||
}
|
||||
|
||||
public enum FilterLogicalOperation {
|
||||
and,
|
||||
or,
|
||||
not
|
||||
}
|
||||
|
||||
public enum FilterItemType {
|
||||
parameter,
|
||||
logical,
|
||||
parameterGroup
|
||||
}
|
||||
|
||||
public enum FilterValueType {
|
||||
token,
|
||||
string,
|
||||
numberOrDate
|
||||
}
|
||||
|
||||
public enum FilterLexType {
|
||||
fsltEnded,
|
||||
fsltName,
|
||||
fsltString,
|
||||
fsltNumber,
|
||||
fsltDot,
|
||||
fsltOpen,
|
||||
fsltClose,
|
||||
fsltOpenSq,
|
||||
fsltCloseSq
|
||||
}
|
||||
|
||||
abstract public static class Filter {
|
||||
|
||||
private FilterItemType itemType;
|
||||
|
||||
public FilterItemType getFilterItemType() {
|
||||
return itemType;
|
||||
}
|
||||
}
|
||||
|
||||
public static class FilterParameterPath {
|
||||
|
||||
private String FName;
|
||||
private Filter FFilter;
|
||||
private FilterParameterPath FNext;
|
||||
|
||||
public String getName() {
|
||||
|
||||
return FName;
|
||||
}
|
||||
|
||||
public void setName(String value) {
|
||||
|
||||
FName = value;
|
||||
}
|
||||
|
||||
public Filter getFilter() {
|
||||
|
||||
return FFilter;
|
||||
}
|
||||
|
||||
public void setFilter(Filter value) {
|
||||
|
||||
FFilter = value;
|
||||
}
|
||||
|
||||
public FilterParameterPath getNext() {
|
||||
|
||||
return FNext;
|
||||
}
|
||||
|
||||
public void setNext(FilterParameterPath value) {
|
||||
|
||||
FNext = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String result;
|
||||
if (getFilter() != null) {
|
||||
result = getName() + "[" + getFilter().toString() + "]";
|
||||
} else {
|
||||
result = getName();
|
||||
}
|
||||
if (getNext() != null) {
|
||||
result += "." + getNext().toString();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public static class FilterParameterGroup extends Filter {
|
||||
|
||||
private Filter FContained;
|
||||
|
||||
public Filter getContained() {
|
||||
|
||||
return FContained;
|
||||
}
|
||||
|
||||
public void setContained(Filter value) {
|
||||
|
||||
FContained = value;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
return "(" + FContained.toString() + ")";
|
||||
}
|
||||
}
|
||||
|
||||
public static class FilterParameter extends Filter {
|
||||
|
||||
private FilterParameterPath FParamPath;
|
||||
private CompareOperation FOperation;
|
||||
private String FValue;
|
||||
private FilterValueType FValueType;
|
||||
|
||||
FilterParameterPath getParamPath() {
|
||||
|
||||
return FParamPath;
|
||||
}
|
||||
|
||||
void setParamPath(FilterParameterPath value) {
|
||||
|
||||
FParamPath = value;
|
||||
}
|
||||
|
||||
|
||||
public CompareOperation getOperation() {
|
||||
|
||||
return FOperation;
|
||||
}
|
||||
|
||||
public void setOperation(CompareOperation value) {
|
||||
|
||||
FOperation = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
|
||||
return FValue;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
|
||||
FValue = value;
|
||||
}
|
||||
|
||||
public FilterValueType getValueType() {
|
||||
|
||||
return FValueType;
|
||||
}
|
||||
|
||||
public void setValueType(FilterValueType FValueType) {
|
||||
|
||||
this.FValueType = FValueType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (FValueType == FilterValueType.string) {
|
||||
return getParamPath().toString() + " " + CODES_CompareOperation.get(getOperation().ordinal()) + " \"" + getValue() + "\"";
|
||||
} else {
|
||||
return getParamPath().toString() + " " + CODES_CompareOperation.get(getOperation().ordinal()) + " " + getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class FilterLogical extends Filter {
|
||||
|
||||
private Filter FFilter1;
|
||||
private FilterLogicalOperation FOperation;
|
||||
private Filter FFilter2;
|
||||
|
||||
|
||||
Filter getFilter1() {
|
||||
|
||||
return FFilter1;
|
||||
}
|
||||
|
||||
void setFilter1(Filter FFilter1) {
|
||||
|
||||
this.FFilter1 = FFilter1;
|
||||
}
|
||||
|
||||
public FilterLogicalOperation getOperation() {
|
||||
|
||||
return FOperation;
|
||||
}
|
||||
|
||||
public void setOperation(FilterLogicalOperation FOperation) {
|
||||
|
||||
this.FOperation = FOperation;
|
||||
}
|
||||
|
||||
Filter getFilter2() {
|
||||
|
||||
return FFilter2;
|
||||
}
|
||||
|
||||
void setFilter2(Filter FFilter2) {
|
||||
|
||||
this.FFilter2 = FFilter2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return FFilter1.toString() + " " + CODES_LogicalOperation.get(getOperation().ordinal()) + " " + FFilter2.toString();
|
||||
}
|
||||
}
|
||||
|
||||
static class FilterSyntaxException extends Exception {
|
||||
FilterSyntaxException(String theMessage) {
|
||||
super(theMessage);
|
||||
}
|
||||
}
|
||||
|
||||
static public Filter parse(String expression) throws FilterSyntaxException {
|
||||
SearchFilterParser parser = new SearchFilterParser();
|
||||
parser.original = expression;
|
||||
parser.cursor = 0;
|
||||
return parser.parse();
|
||||
}
|
||||
}
|
|
@ -423,6 +423,10 @@ public class TransactionProcessor<BUNDLE extends IBaseBundle, BUNDLEENTRY> {
|
|||
url = url.substring(0, qIndex);
|
||||
}
|
||||
|
||||
if (url.length() > 0 && url.charAt(0) == '/') {
|
||||
url = url.substring(1);
|
||||
}
|
||||
|
||||
requestDetails.setRequestPath(url);
|
||||
requestDetails.setFhirServerBase(theRequestDetails.getFhirServerBase());
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ import ca.uhn.fhir.rest.param.*;
|
|||
|
||||
public class FhirResourceDaoPatientDstu3 extends FhirResourceDaoDstu3<Patient>implements IFhirResourceDaoPatient<Patient> {
|
||||
|
||||
private IBundleProvider doEverythingOperation(IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, RequestDetails theRequest) {
|
||||
private IBundleProvider doEverythingOperation(IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequest) {
|
||||
SearchParameterMap paramMap = new SearchParameterMap();
|
||||
if (theCount != null) {
|
||||
paramMap.setCount(theCount.getValue());
|
||||
|
@ -68,13 +68,13 @@ public class FhirResourceDaoPatientDstu3 extends FhirResourceDaoDstu3<Patient>im
|
|||
}
|
||||
|
||||
@Override
|
||||
public IBundleProvider patientInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, RequestDetails theRequestDetails) {
|
||||
return doEverythingOperation(theId, theCount, theLastUpdated, theSort, theContent, theNarrative, theRequestDetails);
|
||||
public IBundleProvider patientInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails) {
|
||||
return doEverythingOperation(theId, theCount, theLastUpdated, theSort, theContent, theNarrative, theFilter, theRequestDetails);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBundleProvider patientTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, RequestDetails theRequestDetails) {
|
||||
return doEverythingOperation(null, theCount, theLastUpdated, theSort, theContent, theNarrative, theRequestDetails);
|
||||
public IBundleProvider patientTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails) {
|
||||
return doEverythingOperation(null, theCount, theLastUpdated, theSort, theContent, theNarrative, theFilter, theRequestDetails);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ import ca.uhn.fhir.rest.param.*;
|
|||
|
||||
public class FhirResourceDaoPatientR4 extends FhirResourceDaoR4<Patient>implements IFhirResourceDaoPatient<Patient> {
|
||||
|
||||
private IBundleProvider doEverythingOperation(IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, RequestDetails theRequest) {
|
||||
private IBundleProvider doEverythingOperation(IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequest) {
|
||||
SearchParameterMap paramMap = new SearchParameterMap();
|
||||
if (theCount != null) {
|
||||
paramMap.setCount(theCount.getValue());
|
||||
|
@ -68,13 +68,13 @@ public class FhirResourceDaoPatientR4 extends FhirResourceDaoR4<Patient>implemen
|
|||
}
|
||||
|
||||
@Override
|
||||
public IBundleProvider patientInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, RequestDetails theRequestDetails) {
|
||||
return doEverythingOperation(theId, theCount, theLastUpdated, theSort, theContent, theNarrative, theRequestDetails);
|
||||
public IBundleProvider patientInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails) {
|
||||
return doEverythingOperation(theId, theCount, theLastUpdated, theSort, theContent, theNarrative, theFilter, theRequestDetails);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBundleProvider patientTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, RequestDetails theRequestDetails) {
|
||||
return doEverythingOperation(null, theCount, theLastUpdated, theSort, theContent, theNarrative, theRequestDetails);
|
||||
public IBundleProvider patientTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails) {
|
||||
return doEverythingOperation(null, theCount, theLastUpdated, theSort, theContent, theNarrative, theFilter, theRequestDetails);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -68,12 +68,12 @@ public class FhirResourceDaoPatientR5 extends FhirResourceDaoR5<Patient> impleme
|
|||
}
|
||||
|
||||
@Override
|
||||
public IBundleProvider patientInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, RequestDetails theRequestDetails) {
|
||||
public IBundleProvider patientInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails) {
|
||||
return doEverythingOperation(theId, theCount, theLastUpdated, theSort, theContent, theNarrative, theRequestDetails);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBundleProvider patientTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, RequestDetails theRequestDetails) {
|
||||
public IBundleProvider patientTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails) {
|
||||
return doEverythingOperation(null, theCount, theLastUpdated, theSort, theContent, theNarrative, theRequestDetails);
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,6 @@ import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
|||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
|
||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.model.entity.BaseHasResource;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
|
@ -33,22 +32,21 @@ import ca.uhn.fhir.rest.param.*;
|
|||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||
import org.hl7.fhir.instance.model.api.IBaseReference;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.model.IdType;
|
||||
import org.hl7.fhir.r4.model.Reference;
|
||||
import org.hl7.fhir.r4.model.Resource;
|
||||
import org.hl7.fhir.r4.utils.GraphQLEngine;
|
||||
import org.hl7.fhir.utilities.graphql.Argument;
|
||||
import org.hl7.fhir.utilities.graphql.IGraphQLStorageServices;
|
||||
import org.hl7.fhir.utilities.graphql.Value;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class JpaStorageServices extends BaseHapiFhirDao<IBaseResource> implements GraphQLEngine.IGraphQLStorageServices {
|
||||
public class JpaStorageServices extends BaseHapiFhirDao<IBaseResource> implements IGraphQLStorageServices {
|
||||
|
||||
private static final int MAX_SEARCH_SIZE = 500;
|
||||
|
||||
private IFhirResourceDao<? extends IBaseResource> getDao(String theResourceType) {
|
||||
RuntimeResourceDefinition typeDef = getContext().getResourceDefinition(theResourceType);
|
||||
|
@ -57,13 +55,13 @@ public class JpaStorageServices extends BaseHapiFhirDao<IBaseResource> implement
|
|||
|
||||
@Transactional(propagation = Propagation.NEVER)
|
||||
@Override
|
||||
public void listResources(Object theAppInfo, String theType, List<Argument> theSearchParams, List<Resource> theMatches) throws FHIRException {
|
||||
public void listResources(Object theAppInfo, String theType, List<Argument> theSearchParams, List<IBaseResource> theMatches) throws FHIRException {
|
||||
|
||||
RuntimeResourceDefinition typeDef = getContext().getResourceDefinition(theType);
|
||||
IFhirResourceDao<? extends IBaseResource> dao = getDao(typeDef.getImplementingClass());
|
||||
|
||||
SearchParameterMap params = new SearchParameterMap();
|
||||
params.setLoadSynchronousUpTo(500);
|
||||
params.setLoadSynchronousUpTo(MAX_SEARCH_SIZE);
|
||||
|
||||
for (Argument nextArgument : theSearchParams) {
|
||||
|
||||
|
@ -114,40 +112,37 @@ public class JpaStorageServices extends BaseHapiFhirDao<IBaseResource> implement
|
|||
size = response.preferredPageSize();
|
||||
}
|
||||
|
||||
for (IBaseResource next : response.getResources(0, size)) {
|
||||
theMatches.add((Resource) next);
|
||||
}
|
||||
theMatches.addAll(response.getResources(0, size));
|
||||
|
||||
}
|
||||
|
||||
@Transactional(propagation = Propagation.REQUIRED)
|
||||
@Override
|
||||
public Resource lookup(Object theAppInfo, String theType, String theId) throws FHIRException {
|
||||
public IBaseResource lookup(Object theAppInfo, String theType, String theId) throws FHIRException {
|
||||
IIdType refId = getContext().getVersion().newIdType();
|
||||
refId.setValue(theType + "/" + theId);
|
||||
return lookup(theAppInfo, refId);
|
||||
}
|
||||
|
||||
private Resource lookup(Object theAppInfo, IIdType theRefId) {
|
||||
private IBaseResource lookup(Object theAppInfo, IIdType theRefId) {
|
||||
IFhirResourceDao<? extends IBaseResource> dao = getDao(theRefId.getResourceType());
|
||||
RequestDetails requestDetails = (RequestDetails) theAppInfo;
|
||||
return (Resource) dao.read(theRefId, requestDetails, false);
|
||||
return dao.read(theRefId, requestDetails, false);
|
||||
}
|
||||
|
||||
@Transactional(propagation = Propagation.REQUIRED)
|
||||
@Override
|
||||
public ReferenceResolution lookup(Object theAppInfo, Resource theContext, Reference theReference) throws FHIRException {
|
||||
IdType refId = new IdType(theReference.getReference());
|
||||
Resource outcome = lookup(theAppInfo, refId);
|
||||
@Override
|
||||
public ReferenceResolution lookup(Object theAppInfo, IBaseResource theContext, IBaseReference theReference) throws FHIRException {
|
||||
IBaseResource outcome = lookup(theAppInfo, theReference.getReferenceElement());
|
||||
if (outcome == null) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return new ReferenceResolution(theContext, outcome);
|
||||
}
|
||||
|
||||
@Transactional(propagation = Propagation.NEVER)
|
||||
@Override
|
||||
public Bundle search(Object theAppInfo, String theType, List<Argument> theSearchParams) throws FHIRException {
|
||||
public IBaseBundle search(Object theAppInfo, String theType, List<Argument> theSearchParams) throws FHIRException {
|
||||
throw new NotImplementedOperationException("Not yet able to handle this GraphQL request");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,6 +66,10 @@ public class BaseJpaResourceProviderPatientDstu2 extends JpaResourceProviderDstu
|
|||
@OperationParam(name = Constants.PARAM_TEXT, min=0, max=OperationParam.MAX_UNLIMITED)
|
||||
List<StringDt> theNarrative,
|
||||
|
||||
@Description(shortDefinition = "Filter the resources to return only resources matching the given _filter filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)")
|
||||
@OperationParam(name = Constants.PARAM_FILTER, min = 0, max = OperationParam.MAX_UNLIMITED)
|
||||
List<StringDt> theFilter,
|
||||
|
||||
@Sort
|
||||
SortSpec theSortSpec,
|
||||
|
||||
|
@ -74,7 +78,7 @@ public class BaseJpaResourceProviderPatientDstu2 extends JpaResourceProviderDstu
|
|||
|
||||
startRequest(theServletRequest);
|
||||
try {
|
||||
return ((IFhirResourceDaoPatient<Patient>) getDao()).patientInstanceEverything(theServletRequest, theId, theCount, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), theRequestDetails);
|
||||
return ((IFhirResourceDaoPatient<Patient>) getDao()).patientInstanceEverything(theServletRequest, theId, theCount, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), toStringAndList(theFilter), theRequestDetails);
|
||||
} finally {
|
||||
endRequest(theServletRequest);
|
||||
}
|
||||
|
@ -104,6 +108,10 @@ public class BaseJpaResourceProviderPatientDstu2 extends JpaResourceProviderDstu
|
|||
@OperationParam(name = Constants.PARAM_TEXT, min=0, max=OperationParam.MAX_UNLIMITED)
|
||||
List<StringDt> theNarrative,
|
||||
|
||||
@Description(shortDefinition = "Filter the resources to return only resources matching the given _filter filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)")
|
||||
@OperationParam(name = Constants.PARAM_FILTER, min = 0, max = OperationParam.MAX_UNLIMITED)
|
||||
List<StringDt> theFilter,
|
||||
|
||||
@Sort
|
||||
SortSpec theSortSpec,
|
||||
|
||||
|
@ -112,7 +120,7 @@ public class BaseJpaResourceProviderPatientDstu2 extends JpaResourceProviderDstu
|
|||
|
||||
startRequest(theServletRequest);
|
||||
try {
|
||||
return ((IFhirResourceDaoPatient<Patient>) getDao()).patientTypeEverything(theServletRequest, theCount, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), theRequestDetails);
|
||||
return ((IFhirResourceDaoPatient<Patient>) getDao()).patientTypeEverything(theServletRequest, theCount, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), toStringAndList(theFilter), theRequestDetails);
|
||||
} finally {
|
||||
endRequest(theServletRequest);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,165 @@
|
|||
package ca.uhn.fhir.jpa.provider;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2019 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.context.support.IContextValidationSupport;
|
||||
import ca.uhn.fhir.rest.annotation.GraphQL;
|
||||
import ca.uhn.fhir.rest.annotation.GraphQLQuery;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Initialize;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.dstu3.hapi.ctx.DefaultProfileValidationSupport;
|
||||
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.utilities.graphql.IGraphQLEngine;
|
||||
import org.hl7.fhir.utilities.graphql.IGraphQLStorageServices;
|
||||
import org.hl7.fhir.utilities.graphql.ObjectValue;
|
||||
import org.hl7.fhir.utilities.graphql.Parser;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class GraphQLProvider {
|
||||
private final Supplier<IGraphQLEngine> engineFactory;
|
||||
private final IGraphQLStorageServices myStorageServices;
|
||||
private Logger ourLog = LoggerFactory.getLogger(GraphQLProvider.class);
|
||||
|
||||
/**
|
||||
* Constructor which uses a default context and validation support object
|
||||
*
|
||||
* @param theStorageServices The storage services (this object will be used to retrieve various resources as required by the GraphQL engine)
|
||||
*/
|
||||
public GraphQLProvider(IGraphQLStorageServices theStorageServices) {
|
||||
this(FhirContext.forR4(), null, theStorageServices);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor which uses the given worker context
|
||||
*
|
||||
* @param theFhirContext The HAPI FHIR Context object
|
||||
* @param theValidationSupport The HAPI Validation Support object, or null
|
||||
* @param theStorageServices The storage services (this object will be used to retrieve various resources as required by the GraphQL engine)
|
||||
*/
|
||||
public GraphQLProvider(@Nonnull FhirContext theFhirContext, @Nullable IContextValidationSupport theValidationSupport, @Nonnull IGraphQLStorageServices theStorageServices) {
|
||||
Validate.notNull(theFhirContext, "theFhirContext must not be null");
|
||||
Validate.notNull(theStorageServices, "theStorageServices must not be null");
|
||||
|
||||
switch (theFhirContext.getVersion().getVersion()) {
|
||||
case DSTU3: {
|
||||
IValidationSupport validationSupport = (IValidationSupport) theValidationSupport;
|
||||
validationSupport = ObjectUtils.defaultIfNull(validationSupport, new org.hl7.fhir.dstu3.hapi.ctx.DefaultProfileValidationSupport());
|
||||
org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext workerContext = new org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext(theFhirContext, validationSupport);
|
||||
engineFactory = () -> new org.hl7.fhir.dstu3.utils.GraphQLEngine(workerContext);
|
||||
break;
|
||||
}
|
||||
case R4: {
|
||||
org.hl7.fhir.r4.hapi.ctx.IValidationSupport validationSupport = (org.hl7.fhir.r4.hapi.ctx.IValidationSupport) theValidationSupport;
|
||||
validationSupport = ObjectUtils.defaultIfNull(validationSupport, new org.hl7.fhir.r4.hapi.ctx.DefaultProfileValidationSupport());
|
||||
org.hl7.fhir.r4.hapi.ctx.HapiWorkerContext workerContext = new org.hl7.fhir.r4.hapi.ctx.HapiWorkerContext(theFhirContext, validationSupport);
|
||||
engineFactory = () -> new org.hl7.fhir.r4.utils.GraphQLEngine(workerContext);
|
||||
break;
|
||||
}
|
||||
case R5: {
|
||||
org.hl7.fhir.r5.hapi.ctx.IValidationSupport validationSupport = (org.hl7.fhir.r5.hapi.ctx.IValidationSupport) theValidationSupport;
|
||||
validationSupport = ObjectUtils.defaultIfNull(validationSupport, new org.hl7.fhir.r5.hapi.ctx.DefaultProfileValidationSupport());
|
||||
org.hl7.fhir.r5.hapi.ctx.HapiWorkerContext workerContext = new org.hl7.fhir.r5.hapi.ctx.HapiWorkerContext(theFhirContext, validationSupport);
|
||||
engineFactory = () -> new org.hl7.fhir.r5.utils.GraphQLEngine(workerContext);
|
||||
break;
|
||||
}
|
||||
case DSTU2:
|
||||
case DSTU2_HL7ORG:
|
||||
case DSTU2_1:
|
||||
default: {
|
||||
throw new UnsupportedOperationException("GraphQL not supported for version: " + theFhirContext.getVersion().getVersion());
|
||||
}
|
||||
}
|
||||
|
||||
myStorageServices = theStorageServices;
|
||||
}
|
||||
|
||||
@GraphQL
|
||||
public String processGraphQlRequest(ServletRequestDetails theRequestDetails, @IdParam IIdType theId, @GraphQLQuery String theQuery) {
|
||||
|
||||
IGraphQLEngine engine = engineFactory.get();
|
||||
engine.setAppInfo(theRequestDetails);
|
||||
engine.setServices(myStorageServices);
|
||||
try {
|
||||
engine.setGraphQL(Parser.parse(theQuery));
|
||||
} catch (Exception theE) {
|
||||
throw new InvalidRequestException("Unable to parse GraphQL Expression: " + theE.toString());
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
if (theId != null) {
|
||||
IBaseResource focus = myStorageServices.lookup(theRequestDetails, theId.getResourceType(), theId.getIdPart());
|
||||
engine.setFocus(focus);
|
||||
}
|
||||
engine.execute();
|
||||
|
||||
StringBuilder outputBuilder = new StringBuilder();
|
||||
ObjectValue output = engine.getOutput();
|
||||
output.write(outputBuilder, 0, "\n");
|
||||
|
||||
return outputBuilder.toString();
|
||||
|
||||
} catch (Exception e) {
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append("Unable to execute GraphQL Expression: ");
|
||||
int statusCode = 500;
|
||||
if (e instanceof BaseServerResponseException) {
|
||||
b.append("HTTP ");
|
||||
statusCode = ((BaseServerResponseException) e).getStatusCode();
|
||||
b.append(statusCode);
|
||||
b.append(" ");
|
||||
} else {
|
||||
// This means it's a bug, so let's log
|
||||
ourLog.error("Failure during GraphQL processing", e);
|
||||
}
|
||||
b.append(e.getMessage());
|
||||
throw new UnclassifiedServerFailureException(statusCode, b.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Initialize
|
||||
public void initialize(RestfulServer theServer) {
|
||||
ourLog.trace("Initializing GraphQL provider");
|
||||
if (!theServer.getFhirContext().getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.DSTU3)) {
|
||||
throw new ConfigurationException("Can not use " + getClass().getName() + " provider on server with FHIR " + theServer.getFhirContext().getVersion().getVersion().name() + " context");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
package ca.uhn.fhir.jpa.provider.dstu3;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoPatient;
|
||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||
import ca.uhn.fhir.model.api.annotation.Description;
|
||||
|
@ -23,8 +25,6 @@ import org.hl7.fhir.dstu3.model.UnsignedIntType;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
|
@ -74,6 +74,10 @@ public class BaseJpaResourceProviderPatientDstu3 extends JpaResourceProviderDstu
|
|||
@OperationParam(name = Constants.PARAM_TEXT, min = 0, max = OperationParam.MAX_UNLIMITED)
|
||||
List<StringType> theNarrative,
|
||||
|
||||
@Description(shortDefinition = "Filter the resources to return only resources matching the given _filter filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)")
|
||||
@OperationParam(name = Constants.PARAM_FILTER, min = 0, max = OperationParam.MAX_UNLIMITED)
|
||||
List<StringType> theFilter,
|
||||
|
||||
@Sort
|
||||
SortSpec theSortSpec,
|
||||
|
||||
|
@ -82,7 +86,7 @@ public class BaseJpaResourceProviderPatientDstu3 extends JpaResourceProviderDstu
|
|||
|
||||
startRequest(theServletRequest);
|
||||
try {
|
||||
return ((IFhirResourceDaoPatient<Patient>) getDao()).patientInstanceEverything(theServletRequest, theId, theCount, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), theRequestDetails);
|
||||
return ((IFhirResourceDaoPatient<Patient>) getDao()).patientInstanceEverything(theServletRequest, theId, theCount, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), toStringAndList(theFilter), theRequestDetails);
|
||||
} finally {
|
||||
endRequest(theServletRequest);
|
||||
}
|
||||
|
@ -112,6 +116,10 @@ public class BaseJpaResourceProviderPatientDstu3 extends JpaResourceProviderDstu
|
|||
@OperationParam(name = Constants.PARAM_TEXT, min = 0, max = OperationParam.MAX_UNLIMITED)
|
||||
List<StringType> theNarrative,
|
||||
|
||||
@Description(shortDefinition = "Filter the resources to return only resources matching the given _filter filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)")
|
||||
@OperationParam(name = Constants.PARAM_FILTER, min = 0, max = OperationParam.MAX_UNLIMITED)
|
||||
List<StringType> theFilter,
|
||||
|
||||
@Sort
|
||||
SortSpec theSortSpec,
|
||||
|
||||
|
@ -120,7 +128,7 @@ public class BaseJpaResourceProviderPatientDstu3 extends JpaResourceProviderDstu
|
|||
|
||||
startRequest(theServletRequest);
|
||||
try {
|
||||
return ((IFhirResourceDaoPatient<Patient>) getDao()).patientTypeEverything(theServletRequest, theCount, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), theRequestDetails);
|
||||
return ((IFhirResourceDaoPatient<Patient>) getDao()).patientTypeEverything(theServletRequest, theCount, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), toStringAndList(theFilter), theRequestDetails);
|
||||
} finally {
|
||||
endRequest(theServletRequest);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package ca.uhn.fhir.jpa.provider.r4;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoPatient;
|
||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||
import ca.uhn.fhir.model.api.annotation.Description;
|
||||
|
@ -23,8 +25,6 @@ import org.hl7.fhir.r4.model.UnsignedIntType;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
|
@ -74,6 +74,10 @@ public class BaseJpaResourceProviderPatientR4 extends JpaResourceProviderR4<Pati
|
|||
@OperationParam(name = Constants.PARAM_TEXT, min = 0, max = OperationParam.MAX_UNLIMITED)
|
||||
List<StringType> theNarrative,
|
||||
|
||||
@Description(shortDefinition = "Filter the resources to return only resources matching the given _filter filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)")
|
||||
@OperationParam(name = Constants.PARAM_FILTER, min = 0, max = OperationParam.MAX_UNLIMITED)
|
||||
List<StringType> theFilter,
|
||||
|
||||
@Sort
|
||||
SortSpec theSortSpec,
|
||||
|
||||
|
@ -82,7 +86,7 @@ public class BaseJpaResourceProviderPatientR4 extends JpaResourceProviderR4<Pati
|
|||
|
||||
startRequest(theServletRequest);
|
||||
try {
|
||||
return ((IFhirResourceDaoPatient<Patient>) getDao()).patientInstanceEverything(theServletRequest, theId, theCount, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), theRequestDetails);
|
||||
return ((IFhirResourceDaoPatient<Patient>) getDao()).patientInstanceEverything(theServletRequest, theId, theCount, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), toStringAndList(theFilter), theRequestDetails);
|
||||
} finally {
|
||||
endRequest(theServletRequest);
|
||||
}
|
||||
|
@ -112,6 +116,10 @@ public class BaseJpaResourceProviderPatientR4 extends JpaResourceProviderR4<Pati
|
|||
@OperationParam(name = Constants.PARAM_TEXT, min = 0, max = OperationParam.MAX_UNLIMITED)
|
||||
List<StringType> theNarrative,
|
||||
|
||||
@Description(shortDefinition = "Filter the resources to return only resources matching the given _filter filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)")
|
||||
@OperationParam(name = Constants.PARAM_FILTER, min = 0, max = OperationParam.MAX_UNLIMITED)
|
||||
List<StringType> theFilter,
|
||||
|
||||
@Sort
|
||||
SortSpec theSortSpec,
|
||||
|
||||
|
@ -120,7 +128,7 @@ public class BaseJpaResourceProviderPatientR4 extends JpaResourceProviderR4<Pati
|
|||
|
||||
startRequest(theServletRequest);
|
||||
try {
|
||||
return ((IFhirResourceDaoPatient<Patient>) getDao()).patientTypeEverything(theServletRequest, theCount, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), theRequestDetails);
|
||||
return ((IFhirResourceDaoPatient<Patient>) getDao()).patientTypeEverything(theServletRequest, theCount, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), toStringAndList(theFilter), theRequestDetails);
|
||||
} finally {
|
||||
endRequest(theServletRequest);
|
||||
}
|
||||
|
|
|
@ -74,6 +74,10 @@ public class BaseJpaResourceProviderPatientR5 extends JpaResourceProviderR5<Pati
|
|||
@OperationParam(name = Constants.PARAM_TEXT, min = 0, max = OperationParam.MAX_UNLIMITED)
|
||||
List<StringType> theNarrative,
|
||||
|
||||
@Description(shortDefinition = "Filter the resources to return only resources matching the given _filter filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)")
|
||||
@OperationParam(name = Constants.PARAM_FILTER, min = 0, max = OperationParam.MAX_UNLIMITED)
|
||||
List<StringType> theFilter,
|
||||
|
||||
@Sort
|
||||
SortSpec theSortSpec,
|
||||
|
||||
|
@ -82,7 +86,7 @@ public class BaseJpaResourceProviderPatientR5 extends JpaResourceProviderR5<Pati
|
|||
|
||||
startRequest(theServletRequest);
|
||||
try {
|
||||
return ((IFhirResourceDaoPatient<Patient>) getDao()).patientInstanceEverything(theServletRequest, theId, theCount, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), theRequestDetails);
|
||||
return ((IFhirResourceDaoPatient<Patient>) getDao()).patientInstanceEverything(theServletRequest, theId, theCount, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), toStringAndList(theFilter), theRequestDetails);
|
||||
} finally {
|
||||
endRequest(theServletRequest);
|
||||
}
|
||||
|
@ -112,6 +116,10 @@ public class BaseJpaResourceProviderPatientR5 extends JpaResourceProviderR5<Pati
|
|||
@OperationParam(name = Constants.PARAM_TEXT, min = 0, max = OperationParam.MAX_UNLIMITED)
|
||||
List<StringType> theNarrative,
|
||||
|
||||
@Description(shortDefinition = "Filter the resources to return only resources matching the given _filter filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)")
|
||||
@OperationParam(name = Constants.PARAM_FILTER, min = 0, max = OperationParam.MAX_UNLIMITED)
|
||||
List<StringType> theFilter,
|
||||
|
||||
@Sort
|
||||
SortSpec theSortSpec,
|
||||
|
||||
|
@ -120,7 +128,7 @@ public class BaseJpaResourceProviderPatientR5 extends JpaResourceProviderR5<Pati
|
|||
|
||||
startRequest(theServletRequest);
|
||||
try {
|
||||
return ((IFhirResourceDaoPatient<Patient>) getDao()).patientTypeEverything(theServletRequest, theCount, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), theRequestDetails);
|
||||
return ((IFhirResourceDaoPatient<Patient>) getDao()).patientTypeEverything(theServletRequest, theCount, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), toStringAndList(theFilter), theRequestDetails);
|
||||
} finally {
|
||||
endRequest(theServletRequest);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import ca.uhn.fhir.jpa.util.CurrentThreadCaptureQueriesListener;
|
|||
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
|
||||
import ca.uhn.fhir.validation.ResultSeverityEnum;
|
||||
import net.ttddyy.dsproxy.listener.SingleQueryCountHolder;
|
||||
import net.ttddyy.dsproxy.listener.logging.SLF4JLogLevel;
|
||||
import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder;
|
||||
import org.apache.commons.dbcp2.BasicDataSource;
|
||||
import org.hibernate.dialect.H2Dialect;
|
||||
|
@ -106,9 +107,10 @@ public class TestR4Config extends BaseJavaConfigR4 {
|
|||
retVal.setPassword("");
|
||||
retVal.setMaxTotal(ourMaxThreads);
|
||||
|
||||
SLF4JLogLevel level = SLF4JLogLevel.INFO;
|
||||
DataSource dataSource = ProxyDataSourceBuilder
|
||||
.create(retVal)
|
||||
// .logQueryBySlf4j(SLF4JLogLevel.INFO, "SQL")
|
||||
.logQueryBySlf4j(level, "SQL")
|
||||
// .logSlowQueryBySlf4j(10, TimeUnit.SECONDS)
|
||||
// .countQuery(new ThreadQueryCountHolder())
|
||||
.beforeQuery(new BlockLargeNumbersOfParamsListener())
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
package ca.uhn.fhir.jpa.dao;
|
||||
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class SearchFilterSyntaxTest {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchFilterSyntaxTest.class);
|
||||
|
||||
private void testParse(String theExpression) throws SearchFilterParser.FilterSyntaxException {
|
||||
SearchFilterParser.Filter filter = SearchFilterParser.parse(theExpression);
|
||||
ourLog.info("Source: {}", theExpression);
|
||||
ourLog.info("Parsed: {}", filter.toString());
|
||||
Assert.assertNotNull("Parsing failed - returned null",
|
||||
filter);
|
||||
Assert.assertEquals(String.format("Expression mismatch: found %s, expecting %s",
|
||||
filter.toString(),
|
||||
theExpression),
|
||||
theExpression,
|
||||
filter.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testString() throws SearchFilterParser.FilterSyntaxException {
|
||||
testParse("userName eq \"bjensen\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToken() throws SearchFilterParser.FilterSyntaxException {
|
||||
testParse("name eq loinc|1234");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUrl() throws SearchFilterParser.FilterSyntaxException {
|
||||
testParse("name in http://loinc.org/vs/LP234");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDate() throws SearchFilterParser.FilterSyntaxException {
|
||||
testParse("date ge 2010-10-10");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubsumes() throws SearchFilterParser.FilterSyntaxException {
|
||||
testParse("code sb snomed|diabetes");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubsumesId() throws SearchFilterParser.FilterSyntaxException {
|
||||
testParse("code ss snomed|diabetes-NIDDM-stage-1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilter() throws SearchFilterParser.FilterSyntaxException {
|
||||
testParse("related[type eq comp].target pr false");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilter2() throws SearchFilterParser.FilterSyntaxException {
|
||||
testParse("related[type eq comp and this lt that].target pr false");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParentheses() throws SearchFilterParser.FilterSyntaxException {
|
||||
testParse("((userName eq \"bjensen\") or (userName eq \"jdoe\")) and (code sb snomed|diabetes)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrecedence() throws SearchFilterParser.FilterSyntaxException {
|
||||
testParse("this eq that and this1 eq that1");
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() {
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
}
|
|
@ -132,6 +132,9 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
|
|||
@Qualifier("myPatientDaoDstu2")
|
||||
protected IFhirResourceDaoPatient<Patient> myPatientDao;
|
||||
@Autowired
|
||||
@Qualifier("myConformanceDaoDstu2")
|
||||
protected IFhirResourceDao<Conformance> myConformanceDao;
|
||||
@Autowired
|
||||
@Qualifier("myGroupDaoDstu2")
|
||||
protected IFhirResourceDao<Group> myGroupDao;
|
||||
@Autowired
|
||||
|
|
|
@ -251,16 +251,16 @@ public class FhirResourceDaoDstu2SearchFtTest extends BaseJpaDstu2Test {
|
|||
|
||||
param = new StringAndListParam();
|
||||
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, param, null, mySrd));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, param, null, null, mySrd));
|
||||
assertThat(actual, containsInAnyOrder(ptId1, obsId1, devId1));
|
||||
|
||||
param = new StringAndListParam();
|
||||
param.addAnd(new StringOrListParam().addOr(new StringParam("obstext1")));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, null, param, mySrd));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, null, param, null, mySrd));
|
||||
assertThat(actual, containsInAnyOrder(ptId1, obsId1, devId1));
|
||||
|
||||
request = mock(HttpServletRequest.class);
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, null, null, mySrd));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, null, null, null, mySrd));
|
||||
assertThat(actual, containsInAnyOrder(ptId1, obsId1, obsId2, devId1));
|
||||
|
||||
/*
|
||||
|
@ -276,7 +276,7 @@ public class FhirResourceDaoDstu2SearchFtTest extends BaseJpaDstu2Test {
|
|||
|
||||
param = new StringAndListParam();
|
||||
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, param, null, mySrd));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, param, null, null, mySrd));
|
||||
assertThat(actual, containsInAnyOrder(ptId1, obsId1, obsId4, devId1));
|
||||
|
||||
/*
|
||||
|
@ -292,7 +292,7 @@ public class FhirResourceDaoDstu2SearchFtTest extends BaseJpaDstu2Test {
|
|||
|
||||
param = new StringAndListParam();
|
||||
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, param, null, mySrd));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, param, null, null, mySrd));
|
||||
assertThat(actual, containsInAnyOrder(ptId1, obsId4));
|
||||
|
||||
}
|
||||
|
@ -343,11 +343,11 @@ public class FhirResourceDaoDstu2SearchFtTest extends BaseJpaDstu2Test {
|
|||
|
||||
param = new StringAndListParam();
|
||||
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.patientTypeEverything(request, null, null, null, param, null, mySrd));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.patientTypeEverything(request, null, null, null, param, null, null, mySrd));
|
||||
assertThat(actual, containsInAnyOrder(ptId1, obsId1, devId1));
|
||||
|
||||
request = mock(HttpServletRequest.class);
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.patientTypeEverything(request, null, null, null, null, null, mySrd));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.patientTypeEverything(request, null, null, null, null, null, null, mySrd));
|
||||
assertThat(actual, containsInAnyOrder(ptId1, obsId1, obsId2, devId1, ptId2, obsId3));
|
||||
|
||||
/*
|
||||
|
@ -363,7 +363,7 @@ public class FhirResourceDaoDstu2SearchFtTest extends BaseJpaDstu2Test {
|
|||
|
||||
param = new StringAndListParam();
|
||||
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.patientTypeEverything(request, null, null, null, param, null, mySrd));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.patientTypeEverything(request, null, null, null, param, null, null, mySrd));
|
||||
assertThat(actual, containsInAnyOrder(ptId1, obsId1, obsId4, devId1));
|
||||
|
||||
/*
|
||||
|
@ -379,7 +379,7 @@ public class FhirResourceDaoDstu2SearchFtTest extends BaseJpaDstu2Test {
|
|||
|
||||
param = new StringAndListParam();
|
||||
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.patientTypeEverything(request, null, null, null, param, null, mySrd));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.patientTypeEverything(request, null, null, null, param, null, null, mySrd));
|
||||
assertThat(actual, containsInAnyOrder(ptId1, obsId4));
|
||||
|
||||
}
|
||||
|
|
|
@ -92,11 +92,11 @@ public class FhirResourceDaoDstu2SearchNoFtTest extends BaseJpaDstu2Test {
|
|||
IIdType moId = myMedicationOrderDao.create(mo, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
IBundleProvider resp = myPatientDao.patientTypeEverything(request, null, null, null, null, null, mySrd);
|
||||
IBundleProvider resp = myPatientDao.patientTypeEverything(request, null, null, null, null, null, null, mySrd);
|
||||
assertThat(toUnqualifiedVersionlessIds(resp), containsInAnyOrder(orgId, medId, patId, moId, patId2));
|
||||
|
||||
request = mock(HttpServletRequest.class);
|
||||
resp = myPatientDao.patientInstanceEverything(request, patId, null, null, null, null, null, mySrd);
|
||||
resp = myPatientDao.patientInstanceEverything(request, patId, null, null, null, null, null, null, mySrd);
|
||||
assertThat(toUnqualifiedVersionlessIds(resp), containsInAnyOrder(orgId, medId, patId, moId));
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import ca.uhn.fhir.model.api.Tag;
|
|||
import ca.uhn.fhir.model.api.TagList;
|
||||
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
|
||||
import ca.uhn.fhir.model.dstu2.composite.CodingDt;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Conformance;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Organization;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
|
@ -18,6 +19,7 @@ import org.hl7.fhir.instance.model.api.IIdType;
|
|||
import org.junit.AfterClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
@ -84,6 +86,18 @@ public class FhirResourceDaoDstu2UpdateTest extends BaseJpaDstu2Test {
|
|||
myPatientDao.update(p, mySrd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure we can upload a real example resource from the DSTU2 argonaut example pack
|
||||
*/
|
||||
@Test
|
||||
public void testUploadArgonautExampleConformance() throws IOException {
|
||||
Conformance conformance = loadResourceFromClasspath(Conformance.class, "/dstu2/Conformance-server.json");
|
||||
conformance.setId("");
|
||||
myConformanceDao.create(conformance);
|
||||
|
||||
assertEquals(1, myConformanceDao.search(new SearchParameterMap().setLoadSynchronous(true)).size().intValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Per the spec, update should preserve tags and security labels but not profiles
|
||||
*/
|
||||
|
|
|
@ -334,16 +334,16 @@ public class FhirResourceDaoDstu3SearchFtTest extends BaseJpaDstu3Test {
|
|||
|
||||
param = new StringAndListParam();
|
||||
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
|
||||
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, param, null, mockSrd()));
|
||||
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, param, null, null, mockSrd()));
|
||||
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId1, devId1)));
|
||||
|
||||
param = new StringAndListParam();
|
||||
param.addAnd(new StringOrListParam().addOr(new StringParam("obstext1")));
|
||||
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, null, param, mockSrd()));
|
||||
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, null, param, null, mockSrd()));
|
||||
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId1, devId1)));
|
||||
|
||||
request = mock(HttpServletRequest.class);
|
||||
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, null, null, mockSrd()));
|
||||
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, null, null, null, mockSrd()));
|
||||
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId1, obsId2, devId1)));
|
||||
|
||||
/*
|
||||
|
@ -359,7 +359,7 @@ public class FhirResourceDaoDstu3SearchFtTest extends BaseJpaDstu3Test {
|
|||
|
||||
param = new StringAndListParam();
|
||||
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
|
||||
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, param, null, mockSrd()));
|
||||
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, param, null, null, mockSrd()));
|
||||
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId1, obsId4, devId1)));
|
||||
|
||||
/*
|
||||
|
@ -375,7 +375,7 @@ public class FhirResourceDaoDstu3SearchFtTest extends BaseJpaDstu3Test {
|
|||
|
||||
param = new StringAndListParam();
|
||||
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
|
||||
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, param, null, mockSrd()));
|
||||
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, param, null, null, mockSrd()));
|
||||
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId4)));
|
||||
|
||||
}
|
||||
|
@ -426,11 +426,11 @@ public class FhirResourceDaoDstu3SearchFtTest extends BaseJpaDstu3Test {
|
|||
|
||||
param = new StringAndListParam();
|
||||
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
|
||||
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientTypeEverything(request, null, null, null, param, null, mockSrd()));
|
||||
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientTypeEverything(request, null, null, null, param, null, null, mockSrd()));
|
||||
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId1, devId1)));
|
||||
|
||||
request = mock(HttpServletRequest.class);
|
||||
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientTypeEverything(request, null, null, null, null, null, mockSrd()));
|
||||
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientTypeEverything(request, null, null, null, null, null, null, mockSrd()));
|
||||
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId1, obsId2, devId1, ptId2, obsId3)));
|
||||
|
||||
/*
|
||||
|
@ -446,7 +446,7 @@ public class FhirResourceDaoDstu3SearchFtTest extends BaseJpaDstu3Test {
|
|||
|
||||
param = new StringAndListParam();
|
||||
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
|
||||
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientTypeEverything(request, null, null, null, param, null, mockSrd()));
|
||||
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientTypeEverything(request, null, null, null, param, null, null, mockSrd()));
|
||||
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId1, obsId4, devId1)));
|
||||
|
||||
/*
|
||||
|
@ -462,7 +462,7 @@ public class FhirResourceDaoDstu3SearchFtTest extends BaseJpaDstu3Test {
|
|||
|
||||
param = new StringAndListParam();
|
||||
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
|
||||
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientTypeEverything(request, null, null, null, param, null, mockSrd()));
|
||||
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientTypeEverything(request, null, null, null, param, null, null, mockSrd()));
|
||||
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId4)));
|
||||
|
||||
}
|
||||
|
|
|
@ -171,11 +171,11 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
|
|||
IIdType moId = myMedicationRequestDao.create(mo, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
IBundleProvider resp = myPatientDao.patientTypeEverything(request, null, null, null, null, null, mySrd);
|
||||
IBundleProvider resp = myPatientDao.patientTypeEverything(request, null, null, null, null, null, null, mySrd);
|
||||
assertThat(toUnqualifiedVersionlessIds(resp), containsInAnyOrder(orgId, medId, patId, moId, patId2));
|
||||
|
||||
request = mock(HttpServletRequest.class);
|
||||
resp = myPatientDao.patientInstanceEverything(request, patId, null, null, null, null, null, mySrd);
|
||||
resp = myPatientDao.patientInstanceEverything(request, patId, null, null, null, null, null, null, mySrd);
|
||||
assertThat(toUnqualifiedVersionlessIds(resp), containsInAnyOrder(orgId, medId, patId, moId));
|
||||
}
|
||||
|
||||
|
@ -202,7 +202,7 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
|
|||
SearchParameterMap map = new SearchParameterMap();
|
||||
map.setEverythingMode(EverythingModeEnum.PATIENT_INSTANCE);
|
||||
IPrimitiveType<Integer> count = new IntegerType(1000);
|
||||
IBundleProvider everything = myPatientDao.patientInstanceEverything(mySrd.getServletRequest(), new IdType("Patient/A161443"), count, null, null, null, null, mySrd);
|
||||
IBundleProvider everything = myPatientDao.patientInstanceEverything(mySrd.getServletRequest(), new IdType("Patient/A161443"), count, null, null, null, null, null, mySrd);
|
||||
|
||||
TreeSet<String> ids = new TreeSet<>(toUnqualifiedVersionlessIdValues(everything));
|
||||
assertThat(ids, hasItem("List/A161444"));
|
||||
|
|
|
@ -464,12 +464,6 @@ public class ConsentEventsDaoR4Test extends BaseJpaR4SystemTest {
|
|||
|
||||
ourLog.info("Allowing IDs: {}", nonBlocked);
|
||||
|
||||
try {
|
||||
throw new Exception();
|
||||
} catch (Exception e) {
|
||||
ourLog.error("Trace", e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,177 @@
|
|||
package ca.uhn.fhir.jpa.dao.r4;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.jpa.util.TestUtil;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.r4.model.CarePlan;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
@SuppressWarnings({"Duplicates"})
|
||||
public class FhirResourceDaoR4FilterTest extends BaseJpaR4Test {
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
myDaoConfig.setFilterParameterEnabled(new DaoConfig().isFilterParameterEnabled());
|
||||
}
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
myDaoConfig.setFilterParameterEnabled(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMalformedFilter() {
|
||||
SearchParameterMap map = new SearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
map.add(Constants.PARAM_FILTER, new StringParam("name eq smith))"));
|
||||
try {
|
||||
myPatientDao.search(map);
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Error parsing _filter syntax: Expression did not terminate at 13", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBrackets() {
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addName().setFamily("Smith").addGiven("John");
|
||||
p.setActive(true);
|
||||
String id1 = myPatientDao.create(p).getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
p = new Patient();
|
||||
p.addName().setFamily("Jones").addGiven("Frank");
|
||||
p.setActive(false);
|
||||
String id2 = myPatientDao.create(p).getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
SearchParameterMap map;
|
||||
List<String> found;
|
||||
|
||||
map = new SearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
map.add(Constants.PARAM_FILTER, new StringParam("name eq smith"));
|
||||
found = toUnqualifiedVersionlessIdValues(myPatientDao.search(map));
|
||||
assertThat(found, containsInAnyOrder(id1));
|
||||
|
||||
map = new SearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
map.add(Constants.PARAM_FILTER, new StringParam("(name eq smith) or (name eq jones)"));
|
||||
found = toUnqualifiedVersionlessIdValues(myPatientDao.search(map));
|
||||
assertThat(found, containsInAnyOrder(id1, id2));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStringComparatorEq() {
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addName().setFamily("Smith").addGiven("John");
|
||||
p.setActive(true);
|
||||
String id1 = myPatientDao.create(p).getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
p = new Patient();
|
||||
p.addName().setFamily("Jones").addGiven("Frank");
|
||||
p.setActive(false);
|
||||
String id2 = myPatientDao.create(p).getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
SearchParameterMap map;
|
||||
List<String> found;
|
||||
|
||||
map = new SearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
map.add(Constants.PARAM_FILTER, new StringParam("name eq smi"));
|
||||
found = toUnqualifiedVersionlessIdValues(myPatientDao.search(map));
|
||||
assertThat(found, Matchers.empty());
|
||||
|
||||
map = new SearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
map.add(Constants.PARAM_FILTER, new StringParam("name eq smith"));
|
||||
found = toUnqualifiedVersionlessIdValues(myPatientDao.search(map));
|
||||
assertThat(found, containsInAnyOrder(id1));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReferenceComparatorEq() {
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addName().setFamily("Smith").addGiven("John");
|
||||
p.setActive(true);
|
||||
IIdType ptId = myPatientDao.create(p).getId().toUnqualifiedVersionless();
|
||||
|
||||
p = new Patient();
|
||||
p.addName().setFamily("Smith").addGiven("John2");
|
||||
p.setActive(true);
|
||||
IIdType ptId2 = myPatientDao.create(p).getId().toUnqualifiedVersionless();
|
||||
|
||||
p = new Patient();
|
||||
p.addName().setFamily("Smith").addGiven("John3");
|
||||
p.setActive(true);
|
||||
IIdType ptId3 = myPatientDao.create(p).getId().toUnqualifiedVersionless();
|
||||
|
||||
CarePlan cp = new CarePlan();
|
||||
cp.getSubject().setReference(ptId.getValue());
|
||||
String cpId = myCarePlanDao.create(cp).getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
cp = new CarePlan();
|
||||
cp.addActivity().getDetail().addPerformer().setReference(ptId2.getValue());
|
||||
String cpId2 = myCarePlanDao.create(cp).getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
SearchParameterMap map;
|
||||
List<String> found;
|
||||
|
||||
map = new SearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
map.add(Constants.PARAM_FILTER, new StringParam("subject eq " + ptId.getValue()));
|
||||
found = toUnqualifiedVersionlessIdValues(myCarePlanDao.search(map));
|
||||
assertThat(found, containsInAnyOrder(cpId));
|
||||
|
||||
map = new SearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
map.add(Constants.PARAM_FILTER, new StringParam("subject eq " + ptId.getIdPart()));
|
||||
found = toUnqualifiedVersionlessIdValues(myCarePlanDao.search(map));
|
||||
assertThat(found, containsInAnyOrder(cpId));
|
||||
|
||||
map = new SearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
map.add(Constants.PARAM_FILTER, new StringParam("(subject eq " + ptId.getIdPart() + ") or (performer eq " + ptId2.getValue() + ")"));
|
||||
found = toUnqualifiedVersionlessIdValues(myCarePlanDao.search(map));
|
||||
assertThat(found, containsInAnyOrder(cpId, cpId2));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilterDisabled() {
|
||||
myDaoConfig.setFilterParameterEnabled(false);
|
||||
|
||||
SearchParameterMap map = new SearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
map.add(Constants.PARAM_FILTER, new StringParam("name eq smith"));
|
||||
try {
|
||||
myPatientDao.search(map);
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("_filter parameter is disabled on this server", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() {
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
}
|
|
@ -494,6 +494,56 @@ public class FhirResourceDaoR4SearchCustomSearchParamTest extends BaseJpaR4Test
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchForExtensionReferenceWithTwoPaths() {
|
||||
Practitioner p1 = new Practitioner();
|
||||
p1.addName().setFamily("P1");
|
||||
IIdType p1id = myPractitionerDao.create(p1).getId().toUnqualifiedVersionless();
|
||||
|
||||
Practitioner p2 = new Practitioner();
|
||||
p2.addName().setFamily("P2");
|
||||
IIdType p2id = myPractitionerDao.create(p2).getId().toUnqualifiedVersionless();
|
||||
|
||||
SearchParameter sp = new SearchParameter();
|
||||
sp.addBase("DiagnosticReport");
|
||||
sp.setCode("fooBar");
|
||||
sp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.REFERENCE);
|
||||
sp.setTitle("FOO AND BAR");
|
||||
sp.setExpression("DiagnosticReport.extension('http://foo') | DiagnosticReport.extension('http://bar')");
|
||||
sp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
|
||||
sp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE);
|
||||
mySearchParameterDao.create(sp, mySrd);
|
||||
|
||||
mySearchParamRegistry.forceRefresh();
|
||||
|
||||
DiagnosticReport dr1 = new DiagnosticReport();
|
||||
dr1.addExtension("http://foo", new Reference(p1id.getValue()));
|
||||
IIdType dr1id = myDiagnosticReportDao.create(dr1).getId().toUnqualifiedVersionless();
|
||||
|
||||
DiagnosticReport dr2 = new DiagnosticReport();
|
||||
dr2.addExtension("http://bar", new Reference(p2id.getValue()));
|
||||
IIdType dr2id = myDiagnosticReportDao.create(dr2).getId().toUnqualifiedVersionless();
|
||||
|
||||
SearchParameterMap map;
|
||||
IBundleProvider results;
|
||||
List<String> foundResources;
|
||||
|
||||
// Find one
|
||||
map = new SearchParameterMap();
|
||||
map.add(sp.getCode(), new ReferenceParam(p1id.getValue()));
|
||||
results = myDiagnosticReportDao.search(map);
|
||||
foundResources = toUnqualifiedVersionlessIdValues(results);
|
||||
assertThat(foundResources, containsInAnyOrder(dr1id.getValue()));
|
||||
|
||||
// Find both
|
||||
map = new SearchParameterMap();
|
||||
map.add(sp.getCode(), new ReferenceOrListParam().addOr(new ReferenceParam(p1id.getValue())).addOr(new ReferenceParam(p2id.getValue())));
|
||||
results = myDiagnosticReportDao.search(map);
|
||||
foundResources = toUnqualifiedVersionlessIdValues(results);
|
||||
assertThat(foundResources, containsInAnyOrder(dr1id.getValue(), dr2id.getValue()));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchForExtensionReferenceWithTarget() {
|
||||
SearchParameter siblingSp = new SearchParameter();
|
||||
|
|
|
@ -345,16 +345,16 @@ public class FhirResourceDaoR4SearchFtTest extends BaseJpaR4Test {
|
|||
|
||||
param = new StringAndListParam();
|
||||
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
|
||||
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, param, null, mockSrd()));
|
||||
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, param, null, null, mockSrd()));
|
||||
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId1, devId1)));
|
||||
|
||||
param = new StringAndListParam();
|
||||
param.addAnd(new StringOrListParam().addOr(new StringParam("obstext1")));
|
||||
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, null, param, mockSrd()));
|
||||
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, null, param, null, mockSrd()));
|
||||
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId1, devId1)));
|
||||
|
||||
request = mock(HttpServletRequest.class);
|
||||
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, null, null, mockSrd()));
|
||||
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, null, null, null, mockSrd()));
|
||||
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId1, obsId2, devId1)));
|
||||
|
||||
/*
|
||||
|
@ -370,7 +370,7 @@ public class FhirResourceDaoR4SearchFtTest extends BaseJpaR4Test {
|
|||
|
||||
param = new StringAndListParam();
|
||||
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
|
||||
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, param, null, mockSrd()));
|
||||
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, param, null, null, mockSrd()));
|
||||
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId1, obsId4, devId1)));
|
||||
|
||||
/*
|
||||
|
@ -386,7 +386,7 @@ public class FhirResourceDaoR4SearchFtTest extends BaseJpaR4Test {
|
|||
|
||||
param = new StringAndListParam();
|
||||
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
|
||||
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, param, null, mockSrd()));
|
||||
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, param, null, null, mockSrd()));
|
||||
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId4)));
|
||||
|
||||
}
|
||||
|
@ -437,11 +437,11 @@ public class FhirResourceDaoR4SearchFtTest extends BaseJpaR4Test {
|
|||
|
||||
param = new StringAndListParam();
|
||||
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
|
||||
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientTypeEverything(request, null, null, null, param, null, mockSrd()));
|
||||
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientTypeEverything(request, null, null, null, param, null, null, mockSrd()));
|
||||
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId1, devId1)));
|
||||
|
||||
request = mock(HttpServletRequest.class);
|
||||
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientTypeEverything(request, null, null, null, null, null, mockSrd()));
|
||||
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientTypeEverything(request, null, null, null, null, null, null, mockSrd()));
|
||||
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId1, obsId2, devId1, ptId2, obsId3)));
|
||||
|
||||
/*
|
||||
|
@ -457,7 +457,7 @@ public class FhirResourceDaoR4SearchFtTest extends BaseJpaR4Test {
|
|||
|
||||
param = new StringAndListParam();
|
||||
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
|
||||
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientTypeEverything(request, null, null, null, param, null, mockSrd()));
|
||||
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientTypeEverything(request, null, null, null, param, null, null, mockSrd()));
|
||||
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId1, obsId4, devId1)));
|
||||
|
||||
/*
|
||||
|
@ -473,7 +473,7 @@ public class FhirResourceDaoR4SearchFtTest extends BaseJpaR4Test {
|
|||
|
||||
param = new StringAndListParam();
|
||||
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
|
||||
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientTypeEverything(request, null, null, null, param, null, mockSrd()));
|
||||
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientTypeEverything(request, null, null, null, param, null, null, mockSrd()));
|
||||
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId4)));
|
||||
|
||||
}
|
||||
|
|
|
@ -470,11 +470,11 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
IIdType moId = myMedicationRequestDao.create(mo, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
IBundleProvider resp = myPatientDao.patientTypeEverything(request, null, null, null, null, null, mySrd);
|
||||
IBundleProvider resp = myPatientDao.patientTypeEverything(request, null, null, null, null, null, null, mySrd);
|
||||
assertThat(toUnqualifiedVersionlessIds(resp), containsInAnyOrder(orgId, medId, patId, moId, patId2));
|
||||
|
||||
request = mock(HttpServletRequest.class);
|
||||
resp = myPatientDao.patientInstanceEverything(request, patId, null, null, null, null, null, mySrd);
|
||||
resp = myPatientDao.patientInstanceEverything(request, patId, null, null, null, null, null, null, mySrd);
|
||||
assertThat(toUnqualifiedVersionlessIds(resp), containsInAnyOrder(orgId, medId, patId, moId));
|
||||
}
|
||||
|
||||
|
@ -502,7 +502,7 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
SearchParameterMap map = new SearchParameterMap();
|
||||
map.setEverythingMode(EverythingModeEnum.PATIENT_INSTANCE);
|
||||
IPrimitiveType<Integer> count = new IntegerType(1000);
|
||||
IBundleProvider everything = myPatientDao.patientInstanceEverything(mySrd.getServletRequest(), new IdType("Patient/A161443"), count, null, null, null, null, mySrd);
|
||||
IBundleProvider everything = myPatientDao.patientInstanceEverything(mySrd.getServletRequest(), new IdType("Patient/A161443"), count, null, null, null, null, null, mySrd);
|
||||
|
||||
TreeSet<String> ids = new TreeSet<>(toUnqualifiedVersionlessIdValues(everything));
|
||||
assertThat(ids, hasItem("List/A161444"));
|
||||
|
|
|
@ -292,11 +292,11 @@ public class FhirResourceDaoR4SearchNoHashesTest extends BaseJpaR4Test {
|
|||
IIdType moId = myMedicationRequestDao.create(mo, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
IBundleProvider resp = myPatientDao.patientTypeEverything(request, null, null, null, null, null, mySrd);
|
||||
IBundleProvider resp = myPatientDao.patientTypeEverything(request, null, null, null, null, null, null, mySrd);
|
||||
assertThat(toUnqualifiedVersionlessIds(resp), containsInAnyOrder(orgId, medId, patId, moId, patId2));
|
||||
|
||||
request = mock(HttpServletRequest.class);
|
||||
resp = myPatientDao.patientInstanceEverything(request, patId, null, null, null, null, null, mySrd);
|
||||
resp = myPatientDao.patientInstanceEverything(request, patId, null, null, null, null, null, null, mySrd);
|
||||
assertThat(toUnqualifiedVersionlessIds(resp), containsInAnyOrder(orgId, medId, patId, moId));
|
||||
}
|
||||
|
||||
|
@ -324,9 +324,9 @@ public class FhirResourceDaoR4SearchNoHashesTest extends BaseJpaR4Test {
|
|||
SearchParameterMap map = new SearchParameterMap();
|
||||
map.setEverythingMode(EverythingModeEnum.PATIENT_INSTANCE);
|
||||
IPrimitiveType<Integer> count = new IntegerType(1000);
|
||||
IBundleProvider everything = myPatientDao.patientInstanceEverything(mySrd.getServletRequest(), new IdType("Patient/A161443"), count, null, null, null, null, mySrd);
|
||||
IBundleProvider everything = myPatientDao.patientInstanceEverything(mySrd.getServletRequest(), new IdType("Patient/A161443"), count, null, null, null, null, null, mySrd);
|
||||
|
||||
TreeSet<String> ids = new TreeSet<String>(toUnqualifiedVersionlessIdValues(everything));
|
||||
TreeSet<String> ids = new TreeSet<>(toUnqualifiedVersionlessIdValues(everything));
|
||||
assertThat(ids, hasItem("List/A161444"));
|
||||
assertThat(ids, hasItem("List/A161468"));
|
||||
assertThat(ids, hasItem("List/A161500"));
|
||||
|
@ -335,7 +335,7 @@ public class FhirResourceDaoR4SearchNoHashesTest extends BaseJpaR4Test {
|
|||
ourLog.info("Actual {} - {}", ids.size(), ids);
|
||||
assertEquals(allIds, ids);
|
||||
|
||||
ids = new TreeSet<String>();
|
||||
ids = new TreeSet<>();
|
||||
for (int i = 0; i < everything.size(); i++) {
|
||||
for (IBaseResource next : everything.getResources(i, i + 1)) {
|
||||
ids.add(next.getIdElement().toUnqualifiedVersionless().getValue());
|
||||
|
|
|
@ -710,7 +710,6 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
|
|||
|
||||
@Test
|
||||
public void testCreateLongString() {
|
||||
//@formatter:off
|
||||
String input = "<NamingSystem>\n" +
|
||||
" <name value=\"NDF-RT (National Drug File – Reference Terminology)\"/>\n" +
|
||||
" <status value=\"draft\"/>\n" +
|
||||
|
@ -728,12 +727,13 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
|
|||
" <preferred value=\"false\"/>\n" +
|
||||
" </uniqueId>\n" +
|
||||
" </NamingSystem>";
|
||||
//@formatter:on
|
||||
|
||||
NamingSystem res = myFhirCtx.newXmlParser().parseResource(NamingSystem.class, input);
|
||||
IIdType id = myNamingSystemDao.create(res, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
assertThat(toUnqualifiedVersionlessIdValues(myNamingSystemDao.search(new SearchParameterMap(NamingSystem.SP_NAME, new StringParam("NDF")).setLoadSynchronous(true))), org.hamcrest.Matchers.contains(id.getValue()));
|
||||
SearchParameterMap params = new SearchParameterMap(NamingSystem.SP_NAME, new StringParam("NDF")).setLoadSynchronous(true);
|
||||
IBundleProvider result = myNamingSystemDao.search(params);
|
||||
assertThat(toUnqualifiedVersionlessIdValues(result), org.hamcrest.Matchers.contains(id.getValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package ca.uhn.fhir.rest.server;
|
||||
package ca.uhn.fhir.jpa.provider;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.annotation.OptionalParam;
|
||||
|
@ -6,6 +6,10 @@ import ca.uhn.fhir.rest.annotation.Search;
|
|||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.param.TokenAndListParam;
|
||||
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.test.utilities.JettyUtil;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
@ -18,11 +22,14 @@ import org.eclipse.jetty.server.Server;
|
|||
import org.eclipse.jetty.servlet.ServletHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.instance.model.api.IBaseReference;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4.hapi.rest.server.GraphQLProvider;
|
||||
import org.hl7.fhir.r4.model.*;
|
||||
import org.hl7.fhir.r4.utils.GraphQLEngine;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.model.HumanName;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.hl7.fhir.r4.model.Resource;
|
||||
import org.hl7.fhir.utilities.graphql.Argument;
|
||||
import org.hl7.fhir.utilities.graphql.IGraphQLStorageServices;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
|
@ -34,9 +41,8 @@ import java.util.List;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import ca.uhn.fhir.test.utilities.JettyUtil;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class GraphQLR4ProviderTest {
|
||||
|
||||
|
@ -216,9 +222,9 @@ public class GraphQLR4ProviderTest {
|
|||
|
||||
}
|
||||
|
||||
private static class MyStorageServices implements GraphQLEngine.IGraphQLStorageServices {
|
||||
private static class MyStorageServices implements IGraphQLStorageServices {
|
||||
@Override
|
||||
public void listResources(Object theAppInfo, String theType, List<Argument> theSearchParams, List<Resource> theMatches) throws FHIRException {
|
||||
public void listResources(Object theAppInfo, String theType, List<Argument> theSearchParams, List<IBaseResource> theMatches) throws FHIRException {
|
||||
ourLog.info("listResources of {} - {}", theType, theSearchParams);
|
||||
|
||||
if (theSearchParams.size() == 1) {
|
||||
|
@ -264,8 +270,8 @@ public class GraphQLR4ProviderTest {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ReferenceResolution lookup(Object theAppInfo, Resource theContext, Reference theReference) throws FHIRException {
|
||||
ourLog.info("lookup from {} to {}", theContext.getIdElement().getValue(), theReference.getReference());
|
||||
public IGraphQLStorageServices.ReferenceResolution lookup(Object theAppInfo, IBaseResource theContext, IBaseReference theReference) throws FHIRException {
|
||||
ourLog.info("lookup from {} to {}", theContext.getIdElement().getValue(), theReference.getReferenceElement().getValue());
|
||||
return null;
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.provider.dstu3;
|
|||
import ca.uhn.fhir.jpa.config.WebsocketDispatcherConfig;
|
||||
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
|
||||
import ca.uhn.fhir.jpa.dao.dstu3.BaseJpaDstu3Test;
|
||||
import ca.uhn.fhir.jpa.provider.GraphQLProvider;
|
||||
import ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider;
|
||||
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
|
||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||
|
@ -97,6 +98,8 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test {
|
|||
SubscriptionTriggeringProvider subscriptionTriggeringProvider = myAppCtx.getBean(SubscriptionTriggeringProvider.class);
|
||||
ourRestServer.registerProvider(subscriptionTriggeringProvider);
|
||||
|
||||
ourRestServer.registerProvider(myAppCtx.getBean(GraphQLProvider.class));
|
||||
|
||||
JpaConformanceProviderDstu3 confProvider = new JpaConformanceProviderDstu3(ourRestServer, mySystemDao, myDaoConfig);
|
||||
confProvider.setImplementationDescription("THIS IS THE DESC");
|
||||
ourRestServer.setServerConformanceProvider(confProvider);
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
package ca.uhn.fhir.jpa.provider.dstu3;
|
||||
|
||||
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.dstu3.model.Patient;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class GraphQLProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
||||
private Logger ourLog = LoggerFactory.getLogger(GraphQLProviderDstu3Test.class);
|
||||
private IIdType myPatientId0;
|
||||
|
||||
@Test
|
||||
public void testInstanceSimpleRead() throws IOException {
|
||||
initTestPatients();
|
||||
|
||||
String query = "{name{family,given}}";
|
||||
HttpGet httpGet = new HttpGet(ourServerBase + "/Patient/" + myPatientId0.getIdPart() + "/$graphql?query=" + UrlUtil.escapeUrlParam(query));
|
||||
|
||||
try (CloseableHttpResponse response = ourHttpClient.execute(httpGet)) {
|
||||
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(resp);
|
||||
assertEquals(TestUtil.stripReturns("{\n" +
|
||||
" \"name\":[{\n" +
|
||||
" \"family\":\"FAM\",\n" +
|
||||
" \"given\":[\"GIVEN1\",\"GIVEN2\"]\n" +
|
||||
" },{\n" +
|
||||
" \"given\":[\"GivenOnly1\",\"GivenOnly2\"]\n" +
|
||||
" }]\n" +
|
||||
"}"), TestUtil.stripReturns(resp));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSystemSimpleSearch() throws IOException {
|
||||
initTestPatients();
|
||||
|
||||
String query = "{PatientList(given:\"given\"){name{family,given}}}";
|
||||
HttpGet httpGet = new HttpGet(ourServerBase + "/$graphql?query=" + UrlUtil.escapeUrlParam(query));
|
||||
|
||||
try (CloseableHttpResponse response = ourHttpClient.execute(httpGet)) {
|
||||
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(resp);
|
||||
assertEquals(TestUtil.stripReturns("{\n" +
|
||||
" \"PatientList\":[{\n" +
|
||||
" \"name\":[{\n" +
|
||||
" \"family\":\"FAM\",\n" +
|
||||
" \"given\":[\"GIVEN1\",\"GIVEN2\"]\n" +
|
||||
" },{\n" +
|
||||
" \"given\":[\"GivenOnly1\",\"GivenOnly2\"]\n" +
|
||||
" }]\n" +
|
||||
" },{\n" +
|
||||
" \"name\":[{\n" +
|
||||
" \"given\":[\"GivenOnlyB1\",\"GivenOnlyB2\"]\n" +
|
||||
" }]\n" +
|
||||
" }]\n" +
|
||||
"}"), TestUtil.stripReturns(resp));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void initTestPatients() {
|
||||
Patient p = new Patient();
|
||||
p.addName()
|
||||
.setFamily("FAM")
|
||||
.addGiven("GIVEN1")
|
||||
.addGiven("GIVEN2");
|
||||
p.addName()
|
||||
.addGiven("GivenOnly1")
|
||||
.addGiven("GivenOnly2");
|
||||
myPatientId0 = ourClient.create().resource(p).execute().getId().toUnqualifiedVersionless();
|
||||
|
||||
p = new Patient();
|
||||
p.addName()
|
||||
.addGiven("GivenOnlyB1")
|
||||
.addGiven("GivenOnlyB2");
|
||||
ourClient.create().resource(p).execute();
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -5,6 +5,7 @@ import static org.junit.Assert.assertEquals;
|
|||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
@ -14,10 +15,10 @@ import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
|||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.hl7.fhir.dstu3.model.Bundle;
|
||||
import org.hl7.fhir.dstu3.model.*;
|
||||
import org.hl7.fhir.dstu3.model.Bundle.*;
|
||||
import org.hl7.fhir.dstu3.model.Enumerations.AdministrativeGender;
|
||||
import org.hl7.fhir.dstu3.model.Patient;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.junit.*;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
|
@ -31,6 +32,8 @@ import ca.uhn.fhir.rest.server.RestfulServer;
|
|||
import ca.uhn.fhir.test.utilities.JettyUtil;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
public class SystemProviderTransactionSearchDstu3Test extends BaseJpaDstu3Test {
|
||||
|
||||
private static RestfulServer myRestServer;
|
||||
|
@ -262,6 +265,24 @@ public class SystemProviderTransactionSearchDstu3Test extends BaseJpaDstu3Test {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransactionGetStartsWithSlash() {
|
||||
IIdType patientId = ourClient.create().resource(new Patient()).execute().getId().toUnqualifiedVersionless();
|
||||
|
||||
Bundle input = new Bundle();
|
||||
input.setType(BundleType.BATCH);
|
||||
input.setId("bundle-batch-test");
|
||||
input.addEntry().getRequest().setMethod(HTTPVerb.GET)
|
||||
.setUrl("/Patient?_id="+patientId.getIdPart());
|
||||
|
||||
Bundle output = ourClient.transaction().withBundle(input).execute();
|
||||
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(output));
|
||||
assertThat(output.getEntryFirstRep().getResponse().getStatus(), startsWith("200"));
|
||||
Bundle respBundle = (Bundle) output.getEntry().get(0).getResource();
|
||||
List<String> actualIds = toIds(respBundle);
|
||||
assertThat(actualIds, containsInAnyOrder(patientId.getValue()));
|
||||
}
|
||||
|
||||
private List<String> toIds(Bundle theRespBundle) {
|
||||
ArrayList<String> retVal = new ArrayList<String>();
|
||||
for (BundleEntryComponent next : theRespBundle.getEntry()) {
|
||||
|
|
|
@ -4,6 +4,7 @@ import ca.uhn.fhir.jpa.config.WebsocketDispatcherConfig;
|
|||
import ca.uhn.fhir.jpa.dao.DaoRegistry;
|
||||
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
|
||||
import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test;
|
||||
import ca.uhn.fhir.jpa.provider.GraphQLProvider;
|
||||
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
|
||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
|
||||
|
@ -71,7 +72,6 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
|
|||
protected IGenericClient ourClient;
|
||||
ResourceCountCache ourResourceCountsCache;
|
||||
private TerminologyUploaderProvider myTerminologyUploaderProvider;
|
||||
private Object ourGraphQLProvider;
|
||||
private boolean ourRestHookSubscriptionInterceptorRequested;
|
||||
|
||||
@Autowired
|
||||
|
@ -105,10 +105,10 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
|
|||
ourRestServer.setDefaultResponseEncoding(EncodingEnum.XML);
|
||||
|
||||
myTerminologyUploaderProvider = myAppCtx.getBean(TerminologyUploaderProvider.class);
|
||||
ourGraphQLProvider = myAppCtx.getBean("myGraphQLProvider");
|
||||
myDaoRegistry = myAppCtx.getBean(DaoRegistry.class);
|
||||
|
||||
ourRestServer.registerProviders(mySystemProvider, myTerminologyUploaderProvider, ourGraphQLProvider);
|
||||
ourRestServer.registerProviders(mySystemProvider, myTerminologyUploaderProvider);
|
||||
ourRestServer.registerProvider(myAppCtx.getBean(GraphQLProvider.class));
|
||||
|
||||
JpaConformanceProviderR4 confProvider = new JpaConformanceProviderR4(ourRestServer, mySystemDao, myDaoConfig);
|
||||
confProvider.setImplementationDescription("THIS IS THE DESC");
|
||||
|
|
|
@ -27,20 +27,17 @@ public class GraphQLProviderR4Test extends BaseResourceProviderR4Test {
|
|||
String query = "{name{family,given}}";
|
||||
HttpGet httpGet = new HttpGet(ourServerBase + "/Patient/" + myPatientId0.getIdPart() + "/$graphql?query=" + UrlUtil.escapeUrlParam(query));
|
||||
|
||||
CloseableHttpResponse response = ourHttpClient.execute(httpGet);
|
||||
try {
|
||||
try (CloseableHttpResponse response = ourHttpClient.execute(httpGet)) {
|
||||
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(resp);
|
||||
assertEquals(TestUtil.stripReturns(resp), TestUtil.stripReturns("{\n" +
|
||||
assertEquals(TestUtil.stripReturns("{\n" +
|
||||
" \"name\":[{\n" +
|
||||
" \"family\":\"FAM\",\n" +
|
||||
" \"given\":[\"GIVEN1\",\"GIVEN2\"]\n" +
|
||||
" },{\n" +
|
||||
" \"given\":[\"GivenOnly1\",\"GivenOnly2\"]\n" +
|
||||
" }]\n" +
|
||||
"}"));
|
||||
} finally {
|
||||
IOUtils.closeQuietly(response);
|
||||
"}"), TestUtil.stripReturns(resp));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -52,8 +49,7 @@ public class GraphQLProviderR4Test extends BaseResourceProviderR4Test {
|
|||
String query = "{PatientList(given:\"given\"){name{family,given}}}";
|
||||
HttpGet httpGet = new HttpGet(ourServerBase + "/$graphql?query=" + UrlUtil.escapeUrlParam(query));
|
||||
|
||||
CloseableHttpResponse response = ourHttpClient.execute(httpGet);
|
||||
try {
|
||||
try (CloseableHttpResponse response = ourHttpClient.execute(httpGet)) {
|
||||
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(resp);
|
||||
assertEquals(TestUtil.stripReturns("{\n" +
|
||||
|
@ -70,10 +66,7 @@ public class GraphQLProviderR4Test extends BaseResourceProviderR4Test {
|
|||
" }]\n" +
|
||||
" }]\n" +
|
||||
"}"), TestUtil.stripReturns(resp));
|
||||
} finally {
|
||||
IOUtils.closeQuietly(response);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void initTestPatients() {
|
||||
|
|
|
@ -4,6 +4,7 @@ import ca.uhn.fhir.jpa.config.WebsocketDispatcherConfig;
|
|||
import ca.uhn.fhir.jpa.dao.DaoRegistry;
|
||||
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
|
||||
import ca.uhn.fhir.jpa.dao.r5.BaseJpaR5Test;
|
||||
import ca.uhn.fhir.jpa.provider.GraphQLProvider;
|
||||
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
|
||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
|
||||
|
@ -108,8 +109,7 @@ public abstract class BaseResourceProviderR5Test extends BaseJpaR5Test {
|
|||
myDaoRegistry = myAppCtx.getBean(DaoRegistry.class);
|
||||
ourRestServer.registerProviders(mySystemProvider, myTerminologyUploaderProvider);
|
||||
|
||||
// ourGraphQLProvider = myAppCtx.getBean("myGraphQLProvider");
|
||||
// ourRestServer.registerProvider(ourGraphQLProvider);
|
||||
ourRestServer.registerProvider(myAppCtx.getBean(GraphQLProvider.class));
|
||||
|
||||
JpaConformanceProviderR5 confProvider = new JpaConformanceProviderR5(ourRestServer, mySystemDao, myDaoConfig);
|
||||
confProvider.setImplementationDescription("THIS IS THE DESC");
|
||||
|
|
|
@ -3,7 +3,12 @@ package ca.uhn.fhir.jpa.stresstest;
|
|||
import ca.uhn.fhir.jpa.config.TestR4Config;
|
||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.provider.r4.BaseResourceProviderR4Test;
|
||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
|
||||
import ca.uhn.fhir.jpa.search.SearchCoordinatorSvcImpl;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.rest.api.SortSpec;
|
||||
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||
import ca.uhn.fhir.test.utilities.UnregisterScheduledProcessor;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
|
@ -32,10 +37,9 @@ import org.junit.Before;
|
|||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
import org.springframework.test.util.AopTestUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
@ -64,7 +68,8 @@ public class StressTestR4Test extends BaseResourceProviderR4Test {
|
|||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(StressTestR4Test.class);
|
||||
private RequestValidatingInterceptor myRequestValidatingInterceptor;
|
||||
@Autowired
|
||||
private IPagingProvider myPagingProvider;
|
||||
private DatabaseBackedPagingProvider myPagingProvider;
|
||||
private int myPreviousMaxPageSize;
|
||||
|
||||
@Override
|
||||
@After
|
||||
|
@ -74,6 +79,12 @@ public class StressTestR4Test extends BaseResourceProviderR4Test {
|
|||
ourRestServer.unregisterInterceptor(myRequestValidatingInterceptor);
|
||||
myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.ENABLED);
|
||||
|
||||
myPagingProvider.setMaximumPageSize(myPreviousMaxPageSize);
|
||||
|
||||
SearchCoordinatorSvcImpl searchCoordinator = AopTestUtils.getTargetObject(mySearchCoordinatorSvc);
|
||||
searchCoordinator.setLoadingThrottleForUnitTests(null);
|
||||
myDaoConfig.setSearchPreFetchThresholds(new DaoConfig().getSearchPreFetchThresholds());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -85,8 +96,92 @@ public class StressTestR4Test extends BaseResourceProviderR4Test {
|
|||
FhirInstanceValidator module = new FhirInstanceValidator();
|
||||
module.setValidationSupport(myValidationSupport);
|
||||
myRequestValidatingInterceptor.addValidatorModule(module);
|
||||
|
||||
myPreviousMaxPageSize = myPagingProvider.getMaximumPageSize();
|
||||
myPagingProvider.setMaximumPageSize(300);
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private ISearchCoordinatorSvc mySearchCoordinatorSvc;
|
||||
|
||||
|
||||
@Test
|
||||
public void testNoDuplicatesInSearchResults() throws Exception {
|
||||
int resourceCount = 1000;
|
||||
int queryCount = 30;
|
||||
myDaoConfig.setSearchPreFetchThresholds(Lists.newArrayList(50, 200, -1));
|
||||
|
||||
SearchCoordinatorSvcImpl searchCoordinator = AopTestUtils.getTargetObject(mySearchCoordinatorSvc);
|
||||
searchCoordinator.setLoadingThrottleForUnitTests(10);
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
|
||||
for (int i = 0; i < resourceCount; i++) {
|
||||
Observation o = new Observation();
|
||||
o.setId("A" + leftPad(Integer.toString(i), 4, '0'));
|
||||
o.setEffective( DateTimeType.now());
|
||||
o.setStatus(Observation.ObservationStatus.FINAL);
|
||||
bundle.addEntry().setFullUrl(o.getId()).setResource(o).getRequest().setMethod(HTTPVerb.PUT).setUrl("Observation/A" + i);
|
||||
}
|
||||
StopWatch sw = new StopWatch();
|
||||
ourLog.info("Saving {} resources", bundle.getEntry().size());
|
||||
mySystemDao.transaction(null, bundle);
|
||||
ourLog.info("Saved {} resources in {}", bundle.getEntry().size(), sw.toString());
|
||||
|
||||
Map<String, IBaseResource> ids = new HashMap<>();
|
||||
|
||||
IGenericClient fhirClient = this.ourClient;
|
||||
|
||||
String url = ourServerBase + "/Observation?date=gt2000&_sort=-_lastUpdated";
|
||||
|
||||
int pageIndex = 0;
|
||||
ourLog.info("Loading page {}", pageIndex);
|
||||
Bundle searchResult = fhirClient
|
||||
.search()
|
||||
.byUrl(url)
|
||||
.count(queryCount)
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
while(true) {
|
||||
List<String> passIds = searchResult
|
||||
.getEntry()
|
||||
.stream()
|
||||
.map(t -> t.getResource().getIdElement().getValue())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
int index = 0;
|
||||
for (String nextId : passIds) {
|
||||
Resource nextResource = searchResult.getEntry().get(index).getResource();
|
||||
|
||||
if (ids.containsKey(nextId)) {
|
||||
String previousContent = fhirClient.getFhirContext().newJsonParser().encodeResourceToString(ids.get(nextId));
|
||||
String newContent = fhirClient.getFhirContext().newJsonParser().encodeResourceToString(nextResource);
|
||||
throw new Exception("Duplicate ID " + nextId + " found at index " + index + " of page " + pageIndex + "\n\nPrevious: " + previousContent + "\n\nNew: " + newContent);
|
||||
}
|
||||
ids.put(nextId, nextResource);
|
||||
index++;
|
||||
}
|
||||
|
||||
if (searchResult.getLink(Constants.LINK_NEXT) == null) {
|
||||
break;
|
||||
} else {
|
||||
if (searchResult.getEntry().size() != queryCount) {
|
||||
throw new Exception("Page had " + searchResult.getEntry().size() + " resources");
|
||||
}
|
||||
if (passIds.size() != queryCount) {
|
||||
throw new Exception("Page had " + passIds.size() + " unique ids");
|
||||
}
|
||||
}
|
||||
|
||||
pageIndex++;
|
||||
ourLog.info("Loading page {}: {}", pageIndex, searchResult.getLink(Constants.LINK_NEXT).getUrl());
|
||||
searchResult = fhirClient.loadPage().next(searchResult).execute();
|
||||
}
|
||||
|
||||
assertEquals(resourceCount, ids.size());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testPageThroughLotsOfPages() {
|
||||
myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.DISABLED);
|
||||
|
|
|
@ -0,0 +1,240 @@
|
|||
package ca.uhn.fhir.jpa.subscription;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.provider.r5.BaseResourceProviderR5Test;
|
||||
import ca.uhn.fhir.jpa.subscription.module.LinkedBlockingQueueSubscribableChannel;
|
||||
import ca.uhn.fhir.rest.annotation.Create;
|
||||
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||
import ca.uhn.fhir.rest.annotation.Update;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.test.utilities.JettyUtil;
|
||||
import ca.uhn.fhir.util.BundleUtil;
|
||||
import com.google.common.collect.Lists;
|
||||
import net.ttddyy.dsproxy.QueryCount;
|
||||
import net.ttddyy.dsproxy.listener.SingleQueryCountHolder;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.r5.model.*;
|
||||
import org.junit.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
|
||||
@Ignore
|
||||
public abstract class BaseSubscriptionsR5Test extends BaseResourceProviderR5Test {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseSubscriptionsR5Test.class);
|
||||
|
||||
private static Server ourListenerServer;
|
||||
protected static int ourListenerPort;
|
||||
protected static List<String> ourContentTypes = Collections.synchronizedList(new ArrayList<>());
|
||||
protected static List<String> ourHeaders = Collections.synchronizedList(new ArrayList<>());
|
||||
private static SingleQueryCountHolder ourCountHolder;
|
||||
|
||||
@Autowired
|
||||
private SingleQueryCountHolder myCountHolder;
|
||||
@Autowired
|
||||
protected SubscriptionTestUtil mySubscriptionTestUtil;
|
||||
@Autowired
|
||||
protected SubscriptionMatcherInterceptor mySubscriptionMatcherInterceptor;
|
||||
|
||||
protected CountingInterceptor myCountingInterceptor;
|
||||
|
||||
protected static List<Observation> ourCreatedObservations = Collections.synchronizedList(Lists.newArrayList());
|
||||
protected static List<Observation> ourUpdatedObservations = Collections.synchronizedList(Lists.newArrayList());
|
||||
private static String ourListenerServerBase;
|
||||
|
||||
protected List<IIdType> mySubscriptionIds = Collections.synchronizedList(new ArrayList<>());
|
||||
|
||||
|
||||
@After
|
||||
public void afterUnregisterRestHookListener() {
|
||||
for (IIdType next : mySubscriptionIds) {
|
||||
IIdType nextId = next.toUnqualifiedVersionless();
|
||||
ourLog.info("Deleting: {}", nextId);
|
||||
ourClient.delete().resourceById(nextId).execute();
|
||||
}
|
||||
mySubscriptionIds.clear();
|
||||
|
||||
myDaoConfig.setAllowMultipleDelete(true);
|
||||
ourLog.info("Deleting all subscriptions");
|
||||
ourClient.delete().resourceConditionalByUrl("Subscription?status=active").execute();
|
||||
ourClient.delete().resourceConditionalByUrl("Observation?code:missing=false").execute();
|
||||
ourLog.info("Done deleting all subscriptions");
|
||||
myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete());
|
||||
|
||||
mySubscriptionTestUtil.unregisterSubscriptionInterceptor();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void beforeRegisterRestHookListener() {
|
||||
mySubscriptionTestUtil.registerRestHookInterceptor();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void beforeReset() throws Exception {
|
||||
ourCreatedObservations.clear();
|
||||
ourUpdatedObservations.clear();
|
||||
ourContentTypes.clear();
|
||||
ourHeaders.clear();
|
||||
|
||||
// Delete all Subscriptions
|
||||
if (ourClient != null) {
|
||||
Bundle allSubscriptions = ourClient.search().forResource(Subscription.class).returnBundle(Bundle.class).execute();
|
||||
for (IBaseResource next : BundleUtil.toListOfResources(myFhirCtx, allSubscriptions)) {
|
||||
ourClient.delete().resource(next).execute();
|
||||
}
|
||||
waitForActivatedSubscriptionCount(0);
|
||||
}
|
||||
|
||||
LinkedBlockingQueueSubscribableChannel processingChannel = mySubscriptionMatcherInterceptor.getProcessingChannelForUnitTest();
|
||||
if (processingChannel != null) {
|
||||
processingChannel.clearInterceptorsForUnitTest();
|
||||
}
|
||||
myCountingInterceptor = new CountingInterceptor();
|
||||
if (processingChannel != null) {
|
||||
processingChannel.addInterceptorForUnitTest(myCountingInterceptor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected Subscription createSubscription(String theCriteria, String thePayload) {
|
||||
Subscription subscription = newSubscription(theCriteria, thePayload);
|
||||
|
||||
MethodOutcome methodOutcome = ourClient.create().resource(subscription).execute();
|
||||
subscription.setId(methodOutcome.getId().getIdPart());
|
||||
mySubscriptionIds.add(methodOutcome.getId());
|
||||
|
||||
return subscription;
|
||||
}
|
||||
|
||||
protected Subscription newSubscription(String theCriteria, String thePayload) {
|
||||
Subscription subscription = new Subscription();
|
||||
subscription.setReason("Monitor new neonatal function (note, age will be determined by the monitor)");
|
||||
subscription.setStatus(Subscription.SubscriptionStatus.REQUESTED);
|
||||
subscription.setCriteria(theCriteria);
|
||||
|
||||
Subscription.SubscriptionChannelComponent channel = subscription.getChannel();
|
||||
channel.setType(Subscription.SubscriptionChannelType.RESTHOOK);
|
||||
channel.setPayload(thePayload);
|
||||
channel.setEndpoint(ourListenerServerBase);
|
||||
return subscription;
|
||||
}
|
||||
|
||||
|
||||
protected void waitForQueueToDrain() throws InterruptedException {
|
||||
mySubscriptionTestUtil.waitForQueueToDrain();
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void initializeOurCountHolder() {
|
||||
ourCountHolder = myCountHolder;
|
||||
}
|
||||
|
||||
|
||||
protected Observation sendObservation(String code, String system) {
|
||||
Observation observation = new Observation();
|
||||
CodeableConcept codeableConcept = new CodeableConcept();
|
||||
observation.setCode(codeableConcept);
|
||||
observation.getIdentifierFirstRep().setSystem("foo").setValue("1");
|
||||
Coding coding = codeableConcept.addCoding();
|
||||
coding.setCode(code);
|
||||
coding.setSystem(system);
|
||||
|
||||
observation.setStatus(Observation.ObservationStatus.FINAL);
|
||||
|
||||
IIdType id = myObservationDao.create(observation).getId();
|
||||
observation.setId(id);
|
||||
|
||||
return observation;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static class ObservationListener implements IResourceProvider {
|
||||
|
||||
@Create
|
||||
public MethodOutcome create(@ResourceParam Observation theObservation, HttpServletRequest theRequest) {
|
||||
ourLog.info("Received Listener Create");
|
||||
ourContentTypes.add(theRequest.getHeader(Constants.HEADER_CONTENT_TYPE).replaceAll(";.*", ""));
|
||||
ourCreatedObservations.add(theObservation);
|
||||
extractHeaders(theRequest);
|
||||
return new MethodOutcome(new IdType("Observation/1"), true);
|
||||
}
|
||||
|
||||
private void extractHeaders(HttpServletRequest theRequest) {
|
||||
Enumeration<String> headerNamesEnum = theRequest.getHeaderNames();
|
||||
while (headerNamesEnum.hasMoreElements()) {
|
||||
String nextName = headerNamesEnum.nextElement();
|
||||
Enumeration<String> valueEnum = theRequest.getHeaders(nextName);
|
||||
while (valueEnum.hasMoreElements()) {
|
||||
String nextValue = valueEnum.nextElement();
|
||||
ourHeaders.add(nextName + ": " + nextValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return Observation.class;
|
||||
}
|
||||
|
||||
@Update
|
||||
public MethodOutcome update(@ResourceParam Observation theObservation, HttpServletRequest theRequest) {
|
||||
ourLog.info("Received Listener Update");
|
||||
ourUpdatedObservations.add(theObservation);
|
||||
ourContentTypes.add(theRequest.getHeader(Constants.HEADER_CONTENT_TYPE).replaceAll(";.*", ""));
|
||||
extractHeaders(theRequest);
|
||||
return new MethodOutcome(new IdType("Observation/1"), false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void reportTotalSelects() {
|
||||
ourLog.info("Total database select queries: {}", getQueryCount().getSelect());
|
||||
}
|
||||
|
||||
private static QueryCount getQueryCount() {
|
||||
return ourCountHolder.getQueryCountMap().get("");
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void startListenerServer() throws Exception {
|
||||
RestfulServer ourListenerRestServer = new RestfulServer(FhirContext.forR5());
|
||||
|
||||
ObservationListener obsListener = new ObservationListener();
|
||||
ourListenerRestServer.setResourceProviders(obsListener);
|
||||
|
||||
ourListenerServer = new Server(0);
|
||||
|
||||
ServletContextHandler proxyHandler = new ServletContextHandler();
|
||||
proxyHandler.setContextPath("/");
|
||||
|
||||
ServletHolder servletHolder = new ServletHolder();
|
||||
servletHolder.setServlet(ourListenerRestServer);
|
||||
proxyHandler.addServlet(servletHolder, "/fhir/context/*");
|
||||
|
||||
ourListenerServer.setHandler(proxyHandler);
|
||||
JettyUtil.startServer(ourListenerServer);
|
||||
ourListenerPort = JettyUtil.getPortForStartedServer(ourListenerServer);
|
||||
ourListenerServerBase = "http://localhost:" + ourListenerPort + "/fhir/context";
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopListenerServer() throws Exception {
|
||||
JettyUtil.closeServer(ourListenerServer);
|
||||
}
|
||||
|
||||
}
|
|
@ -141,7 +141,7 @@ public class EmailSubscriptionDstu2Test extends BaseResourceProviderDstu2Test {
|
|||
assertEquals(1, messages[msgIdx].getHeader("Content-Type").length);
|
||||
assertEquals("text/plain; charset=us-ascii", messages[msgIdx].getHeader("Content-Type")[0]);
|
||||
String foundBody = GreenMailUtil.getBody(messages[msgIdx]);
|
||||
assertEquals("A subscription update has been received", foundBody);
|
||||
assertEquals("", foundBody);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -112,9 +112,13 @@ public class EmailSubscriptionDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
return observation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests an email subscription with payload set to XML. The email sent must include content in the body of the email that is formatted as XML.
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testEmailSubscriptionNormal() throws Exception {
|
||||
String payload = "This is the body";
|
||||
String payload = "application/fhir+xml";
|
||||
|
||||
String code = "1000000050";
|
||||
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
||||
|
@ -138,13 +142,21 @@ public class EmailSubscriptionDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
assertEquals(1, received.get(0).getAllRecipients().length);
|
||||
assertEquals("foo@example.com", ((InternetAddress) received.get(0).getAllRecipients()[0]).getAddress());
|
||||
assertEquals("text/plain; charset=us-ascii", received.get(0).getContentType());
|
||||
assertEquals("This is the body", received.get(0).getContent().toString().trim());
|
||||
assertEquals(mySubscriptionIds.get(0).toUnqualifiedVersionless().getValue(), received.get(0).getHeader("X-FHIR-Subscription")[0]);
|
||||
|
||||
// Expect the body of the email subscription to be an Observation formatted as XML
|
||||
Observation parsedObservation = (Observation) ourClient.getFhirContext().newXmlParser().parseResource(received.get(0).getContent().toString().trim());
|
||||
assertEquals("SNOMED-CT", parsedObservation.getCode().getCodingFirstRep().getSystem());
|
||||
assertEquals("1000000050", parsedObservation.getCode().getCodingFirstRep().getCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests an email subscription with payload set to JSON. The email sent must include content in the body of the email that is formatted as JSON.
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testEmailSubscriptionWithCustom() throws Exception {
|
||||
String payload = "This is the body";
|
||||
String payload = "application/fhir+json";
|
||||
|
||||
String code = "1000000050";
|
||||
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
||||
|
@ -185,13 +197,21 @@ public class EmailSubscriptionDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
assertEquals("foo@example.com", ((InternetAddress) received.get(0).getAllRecipients()[0]).getAddress());
|
||||
assertEquals("text/plain; charset=us-ascii", received.get(0).getContentType());
|
||||
assertEquals("This is a subject", received.get(0).getSubject().toString().trim());
|
||||
assertEquals("This is the body", received.get(0).getContent().toString().trim());
|
||||
assertEquals(mySubscriptionIds.get(0).toUnqualifiedVersionless().getValue(), received.get(0).getHeader("X-FHIR-Subscription")[0]);
|
||||
|
||||
// Expect the body of the email subscription to be an Observation formatted as JSON
|
||||
Observation parsedObservation = (Observation) ourClient.getFhirContext().newJsonParser().parseResource(received.get(0).getContent().toString().trim());
|
||||
assertEquals("SNOMED-CT", parsedObservation.getCode().getCodingFirstRep().getSystem());
|
||||
assertEquals("1000000050", parsedObservation.getCode().getCodingFirstRep().getCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests an email subscription with no payload. When the email is sent, the body of the email must be empty.
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testEmailSubscriptionWithCustomNoMailtoOnFrom() throws Exception {
|
||||
String payload = "This is the body";
|
||||
String payload = "";
|
||||
|
||||
String code = "1000000050";
|
||||
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
||||
|
@ -231,7 +251,7 @@ public class EmailSubscriptionDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
assertEquals("foo@example.com", ((InternetAddress) received.get(0).getAllRecipients()[0]).getAddress());
|
||||
assertEquals("text/plain; charset=us-ascii", received.get(0).getContentType());
|
||||
assertEquals("This is a subject", received.get(0).getSubject().toString().trim());
|
||||
assertEquals("This is the body", received.get(0).getContent().toString().trim());
|
||||
assertEquals("", received.get(0).getContent().toString().trim());
|
||||
assertEquals(mySubscriptionIds.get(0).toUnqualifiedVersionless().getValue(), received.get(0).getHeader("X-FHIR-Subscription")[0]);
|
||||
|
||||
ourLog.info("Subscription: {}", myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(ourClient.history().onInstance(id).andReturnBundle(Bundle.class).execute()));
|
||||
|
|
|
@ -175,13 +175,15 @@ public class RestHookTestDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
@Test
|
||||
public void testRestHookSubscription() throws Exception {
|
||||
String code = "1000000050";
|
||||
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
||||
String criteria2 = "Observation?code=SNOMED-CT|" + code + "111&_format=xml";
|
||||
String criteria1 = "Observation?code=SNOMED-CT|" + code;
|
||||
String criteria2 = "Observation?code=SNOMED-CT|" + code + "111";
|
||||
|
||||
createSubscription(criteria1, null, ourNotificationListenerServer,
|
||||
Collections.singletonList(new StringType("Authorization: abc-def")));
|
||||
createSubscription(criteria2, null, ourNotificationListenerServer);
|
||||
|
||||
ourLog.debug("Sending first observation");
|
||||
|
||||
sendObservation(code, "SNOMED-CT");
|
||||
|
||||
// Should see 1 subscription notification with authorization header
|
||||
|
|
|
@ -0,0 +1,957 @@
|
|||
package ca.uhn.fhir.jpa.subscription.resthook;
|
||||
|
||||
import ca.uhn.fhir.jpa.config.StoppableSubscriptionDeliveringRestHookSubscriber;
|
||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||
import ca.uhn.fhir.jpa.subscription.BaseSubscriptionsR5Test;
|
||||
import ca.uhn.fhir.rest.api.CacheControlDirective;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||
import org.hl7.fhir.r5.model.*;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Test the rest-hook subscriptions
|
||||
*/
|
||||
public class RestHookTestR5Test extends BaseSubscriptionsR5Test {
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(RestHookTestR5Test.class);
|
||||
|
||||
@Autowired
|
||||
StoppableSubscriptionDeliveringRestHookSubscriber myStoppableSubscriptionDeliveringRestHookSubscriber;
|
||||
|
||||
@After
|
||||
public void cleanupStoppableSubscriptionDeliveringRestHookSubscriber() {
|
||||
ourLog.info("@After");
|
||||
myStoppableSubscriptionDeliveringRestHookSubscriber.setCountDownLatch(null);
|
||||
myStoppableSubscriptionDeliveringRestHookSubscriber.unPause();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRestHookSubscriptionApplicationFhirJson() throws Exception {
|
||||
String payload = "application/fhir+json";
|
||||
|
||||
String code = "1000000050";
|
||||
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
||||
String criteria2 = "Observation?code=SNOMED-CT|" + code + "111&_format=xml";
|
||||
|
||||
createSubscription(criteria1, payload);
|
||||
createSubscription(criteria2, payload);
|
||||
waitForActivatedSubscriptionCount(2);
|
||||
|
||||
sendObservation(code, "SNOMED-CT");
|
||||
|
||||
// Should see 1 subscription notification
|
||||
waitForQueueToDrain();
|
||||
waitForSize(0, ourCreatedObservations);
|
||||
waitForSize(1, ourUpdatedObservations);
|
||||
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdatesHaveCorrectMetadata() throws Exception {
|
||||
String payload = "application/fhir+json";
|
||||
|
||||
String code = "1000000050";
|
||||
String criteria1 = "Observation?";
|
||||
|
||||
createSubscription(criteria1, payload);
|
||||
waitForActivatedSubscriptionCount(1);
|
||||
|
||||
/*
|
||||
* Send version 1
|
||||
*/
|
||||
|
||||
Observation obs = sendObservation(code, "SNOMED-CT");
|
||||
obs = myObservationDao.read(obs.getIdElement().toUnqualifiedVersionless());
|
||||
|
||||
// Should see 1 subscription notification
|
||||
waitForQueueToDrain();
|
||||
int idx = 0;
|
||||
waitForSize(0, ourCreatedObservations);
|
||||
waitForSize(1, ourUpdatedObservations);
|
||||
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(idx));
|
||||
assertEquals("1", ourUpdatedObservations.get(idx).getIdElement().getVersionIdPart());
|
||||
assertEquals("1", ourUpdatedObservations.get(idx).getMeta().getVersionId());
|
||||
assertEquals(obs.getMeta().getLastUpdatedElement().getValueAsString(), ourUpdatedObservations.get(idx).getMeta().getLastUpdatedElement().getValueAsString());
|
||||
assertEquals(obs.getMeta().getLastUpdatedElement().getValueAsString(), ourUpdatedObservations.get(idx).getMeta().getLastUpdatedElement().getValueAsString());
|
||||
assertEquals("1", ourUpdatedObservations.get(idx).getIdentifierFirstRep().getValue());
|
||||
|
||||
/*
|
||||
* Send version 2
|
||||
*/
|
||||
|
||||
obs.getIdentifierFirstRep().setSystem("foo").setValue("2");
|
||||
myObservationDao.update(obs);
|
||||
obs = myObservationDao.read(obs.getIdElement().toUnqualifiedVersionless());
|
||||
|
||||
// Should see 1 subscription notification
|
||||
waitForQueueToDrain();
|
||||
idx++;
|
||||
waitForSize(0, ourCreatedObservations);
|
||||
waitForSize(2, ourUpdatedObservations);
|
||||
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(idx));
|
||||
assertEquals("2", ourUpdatedObservations.get(idx).getIdElement().getVersionIdPart());
|
||||
assertEquals("2", ourUpdatedObservations.get(idx).getMeta().getVersionId());
|
||||
assertEquals(obs.getMeta().getLastUpdatedElement().getValueAsString(), ourUpdatedObservations.get(idx).getMeta().getLastUpdatedElement().getValueAsString());
|
||||
assertEquals(obs.getMeta().getLastUpdatedElement().getValueAsString(), ourUpdatedObservations.get(idx).getMeta().getLastUpdatedElement().getValueAsString());
|
||||
assertEquals("2", ourUpdatedObservations.get(idx).getIdentifierFirstRep().getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPlaceholderReferencesInTransactionAreResolvedCorrectly() throws Exception {
|
||||
|
||||
String payload = "application/fhir+json";
|
||||
String code = "1000000050";
|
||||
String criteria1 = "Observation?";
|
||||
createSubscription(criteria1, payload);
|
||||
waitForActivatedSubscriptionCount(1);
|
||||
|
||||
// Create a transaction that should match
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.setType(Bundle.BundleType.TRANSACTION);
|
||||
|
||||
Patient patient = new Patient();
|
||||
patient.setId(IdType.newRandomUuid());
|
||||
patient.getIdentifierFirstRep().setSystem("foo").setValue("AAA");
|
||||
bundle.addEntry().setResource(patient).getRequest().setMethod(Bundle.HTTPVerb.POST).setUrl("Patient");
|
||||
|
||||
Observation observation = new Observation();
|
||||
observation.getIdentifierFirstRep().setSystem("foo").setValue("1");
|
||||
observation.getCode().addCoding().setCode(code).setSystem("SNOMED-CT");
|
||||
observation.setStatus(Observation.ObservationStatus.FINAL);
|
||||
observation.getSubject().setReference(patient.getId());
|
||||
bundle.addEntry().setResource(observation).getRequest().setMethod(Bundle.HTTPVerb.POST).setUrl("Observation");
|
||||
|
||||
// Send the transaction
|
||||
mySystemDao.transaction(null, bundle);
|
||||
|
||||
waitForSize(1, ourUpdatedObservations);
|
||||
|
||||
assertThat(ourUpdatedObservations.get(0).getSubject().getReference(), matchesPattern("Patient/[0-9]+"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdatesHaveCorrectMetadataUsingTransactions() throws Exception {
|
||||
String payload = "application/fhir+json";
|
||||
|
||||
String code = "1000000050";
|
||||
String criteria1 = "Observation?";
|
||||
|
||||
createSubscription(criteria1, payload);
|
||||
waitForActivatedSubscriptionCount(1);
|
||||
|
||||
/*
|
||||
* Send version 1
|
||||
*/
|
||||
|
||||
Observation observation = new Observation();
|
||||
observation.getIdentifierFirstRep().setSystem("foo").setValue("1");
|
||||
observation.getCode().addCoding().setCode(code).setSystem("SNOMED-CT");
|
||||
observation.setStatus(Observation.ObservationStatus.FINAL);
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.setType(Bundle.BundleType.TRANSACTION);
|
||||
bundle.addEntry().setResource(observation).getRequest().setMethod(Bundle.HTTPVerb.POST).setUrl("Observation");
|
||||
Bundle responseBundle = mySystemDao.transaction(null, bundle);
|
||||
|
||||
Observation obs = myObservationDao.read(new IdType(responseBundle.getEntry().get(0).getResponse().getLocation()));
|
||||
|
||||
// Should see 1 subscription notification
|
||||
waitForQueueToDrain();
|
||||
int idx = 0;
|
||||
waitForSize(0, ourCreatedObservations);
|
||||
waitForSize(1, ourUpdatedObservations);
|
||||
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(idx));
|
||||
assertEquals("1", ourUpdatedObservations.get(idx).getIdElement().getVersionIdPart());
|
||||
assertEquals("1", ourUpdatedObservations.get(idx).getMeta().getVersionId());
|
||||
assertEquals(obs.getMeta().getLastUpdatedElement().getValueAsString(), ourUpdatedObservations.get(idx).getMeta().getLastUpdatedElement().getValueAsString());
|
||||
assertEquals("1", ourUpdatedObservations.get(idx).getIdentifierFirstRep().getValue());
|
||||
|
||||
/*
|
||||
* Send version 2
|
||||
*/
|
||||
|
||||
observation = new Observation();
|
||||
observation.setId(obs.getId());
|
||||
observation.getIdentifierFirstRep().setSystem("foo").setValue("2");
|
||||
observation.getCode().addCoding().setCode(code).setSystem("SNOMED-CT");
|
||||
observation.setStatus(Observation.ObservationStatus.FINAL);
|
||||
bundle = new Bundle();
|
||||
bundle.setType(Bundle.BundleType.TRANSACTION);
|
||||
bundle.addEntry().setResource(observation).getRequest().setMethod(Bundle.HTTPVerb.PUT).setUrl(obs.getIdElement().toUnqualifiedVersionless().getValue());
|
||||
mySystemDao.transaction(null, bundle);
|
||||
obs = myObservationDao.read(obs.getIdElement().toUnqualifiedVersionless());
|
||||
|
||||
// Should see 1 subscription notification
|
||||
waitForQueueToDrain();
|
||||
idx++;
|
||||
waitForSize(0, ourCreatedObservations);
|
||||
waitForSize(2, ourUpdatedObservations);
|
||||
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(idx));
|
||||
assertEquals("2", ourUpdatedObservations.get(idx).getIdElement().getVersionIdPart());
|
||||
assertEquals("2", ourUpdatedObservations.get(idx).getMeta().getVersionId());
|
||||
assertEquals(obs.getMeta().getLastUpdatedElement().getValueAsString(), ourUpdatedObservations.get(idx).getMeta().getLastUpdatedElement().getValueAsString());
|
||||
assertEquals("2", ourUpdatedObservations.get(idx).getIdentifierFirstRep().getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRepeatedDeliveries() throws Exception {
|
||||
String payload = "application/fhir+json";
|
||||
|
||||
String code = "1000000050";
|
||||
String criteria1 = "Observation?";
|
||||
|
||||
createSubscription(criteria1, payload);
|
||||
waitForActivatedSubscriptionCount(1);
|
||||
|
||||
for (int i = 0; i < 100; i++) {
|
||||
Observation observation = new Observation();
|
||||
observation.getIdentifierFirstRep().setSystem("foo").setValue("ID" + i);
|
||||
observation.getCode().addCoding().setCode(code).setSystem("SNOMED-CT");
|
||||
observation.setStatus(Observation.ObservationStatus.FINAL);
|
||||
myObservationDao.create(observation);
|
||||
}
|
||||
|
||||
waitForSize(100, ourUpdatedObservations);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testActiveSubscriptionShouldntReActivate() throws Exception {
|
||||
String criteria = "Observation?code=111111111&_format=xml";
|
||||
String payload = "application/fhir+json";
|
||||
createSubscription(criteria, payload);
|
||||
|
||||
waitForActivatedSubscriptionCount(1);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
int changes = this.mySubscriptionLoader.doSyncSubscriptionsForUnitTest();
|
||||
assertEquals(0, changes);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRestHookSubscriptionNoopUpdateDoesntTriggerNewDelivery() throws Exception {
|
||||
String payload = "application/fhir+json";
|
||||
|
||||
String code = "1000000050";
|
||||
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
||||
String criteria2 = "Observation?code=SNOMED-CT|" + code + "111&_format=xml";
|
||||
|
||||
createSubscription(criteria1, payload);
|
||||
createSubscription(criteria2, payload);
|
||||
waitForActivatedSubscriptionCount(2);
|
||||
|
||||
Observation obs = sendObservation(code, "SNOMED-CT");
|
||||
|
||||
// Should see 1 subscription notification
|
||||
waitForQueueToDrain();
|
||||
waitForSize(0, ourCreatedObservations);
|
||||
waitForSize(1, ourUpdatedObservations);
|
||||
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0));
|
||||
|
||||
// Send an update with no changes
|
||||
obs.setId(obs.getIdElement().toUnqualifiedVersionless());
|
||||
ourClient.update().resource(obs).execute();
|
||||
|
||||
// Should be no further deliveries
|
||||
Thread.sleep(1000);
|
||||
waitForQueueToDrain();
|
||||
waitForSize(0, ourCreatedObservations);
|
||||
waitForSize(1, ourUpdatedObservations);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRestHookSubscriptionApplicationJsonDisableVersionIdInDelivery() throws Exception {
|
||||
String payload = "application/json";
|
||||
|
||||
String code = "1000000050";
|
||||
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
||||
|
||||
waitForActivatedSubscriptionCount(0);
|
||||
Subscription subscription1 = createSubscription(criteria1, payload);
|
||||
waitForActivatedSubscriptionCount(1);
|
||||
|
||||
|
||||
ourLog.info("** About to send observation");
|
||||
Observation observation1 = sendObservation(code, "SNOMED-CT");
|
||||
|
||||
waitForQueueToDrain();
|
||||
waitForSize(0, ourCreatedObservations);
|
||||
waitForSize(1, ourUpdatedObservations);
|
||||
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0));
|
||||
|
||||
IdType idElement = ourUpdatedObservations.get(0).getIdElement();
|
||||
assertEquals(observation1.getIdElement().getIdPart(), idElement.getIdPart());
|
||||
// VersionId is present
|
||||
assertEquals(observation1.getIdElement().getVersionIdPart(), idElement.getVersionIdPart());
|
||||
|
||||
subscription1
|
||||
.getChannel()
|
||||
.addExtension(JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_STRIP_VERSION_IDS, new BooleanType("true"));
|
||||
ourLog.info("** About to update subscription");
|
||||
|
||||
int modCount = myCountingInterceptor.getSentCount();
|
||||
ourClient.update().resource(subscription1).execute();
|
||||
waitForSize(modCount + 1, () -> myCountingInterceptor.getSentCount());
|
||||
|
||||
ourLog.info("** About to send observation");
|
||||
Observation observation2 = sendObservation(code, "SNOMED-CT");
|
||||
|
||||
waitForQueueToDrain();
|
||||
waitForSize(0, ourCreatedObservations);
|
||||
waitForSize(2, ourUpdatedObservations);
|
||||
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(1));
|
||||
|
||||
idElement = ourUpdatedObservations.get(1).getIdElement();
|
||||
assertEquals(observation2.getIdElement().getIdPart(), idElement.getIdPart());
|
||||
// Now VersionId is stripped
|
||||
assertEquals(null, idElement.getVersionIdPart());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRestHookSubscriptionDoesntGetLatestVersionByDefault() throws Exception {
|
||||
String payload = "application/json";
|
||||
|
||||
String code = "1000000050";
|
||||
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
||||
|
||||
waitForActivatedSubscriptionCount(0);
|
||||
createSubscription(criteria1, payload);
|
||||
waitForActivatedSubscriptionCount(1);
|
||||
|
||||
myStoppableSubscriptionDeliveringRestHookSubscriber.pause();
|
||||
final CountDownLatch countDownLatch = new CountDownLatch(1);
|
||||
myStoppableSubscriptionDeliveringRestHookSubscriber.setCountDownLatch(countDownLatch);
|
||||
|
||||
ourLog.info("** About to send observation");
|
||||
Observation observation = sendObservation(code, "SNOMED-CT");
|
||||
assertEquals("1", observation.getIdElement().getVersionIdPart());
|
||||
assertNull(observation.getNoteFirstRep().getText());
|
||||
|
||||
observation.getNoteFirstRep().setText("changed");
|
||||
MethodOutcome methodOutcome = ourClient.update().resource(observation).execute();
|
||||
assertEquals("2", methodOutcome.getId().getVersionIdPart());
|
||||
assertEquals("changed", observation.getNoteFirstRep().getText());
|
||||
|
||||
// Wait for our two delivery channel threads to be paused
|
||||
assertTrue(countDownLatch.await(5L, TimeUnit.SECONDS));
|
||||
// Open the floodgates!
|
||||
myStoppableSubscriptionDeliveringRestHookSubscriber.unPause();
|
||||
|
||||
|
||||
waitForSize(0, ourCreatedObservations);
|
||||
waitForSize(2, ourUpdatedObservations);
|
||||
|
||||
Observation observation1 = ourUpdatedObservations.get(0);
|
||||
Observation observation2 = ourUpdatedObservations.get(1);
|
||||
|
||||
assertEquals("1", observation1.getIdElement().getVersionIdPart());
|
||||
assertNull(observation1.getNoteFirstRep().getText());
|
||||
assertEquals("2", observation2.getIdElement().getVersionIdPart());
|
||||
assertEquals("changed", observation2.getNoteFirstRep().getText());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRestHookSubscriptionGetsLatestVersionWithFlag() throws Exception {
|
||||
String payload = "application/json";
|
||||
|
||||
String code = "1000000050";
|
||||
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
||||
|
||||
waitForActivatedSubscriptionCount(0);
|
||||
|
||||
Subscription subscription = newSubscription(criteria1, payload);
|
||||
subscription
|
||||
.getChannel()
|
||||
.addExtension(JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_DELIVER_LATEST_VERSION, new BooleanType("true"));
|
||||
ourClient.create().resource(subscription).execute();
|
||||
|
||||
waitForActivatedSubscriptionCount(1);
|
||||
|
||||
myStoppableSubscriptionDeliveringRestHookSubscriber.pause();
|
||||
final CountDownLatch countDownLatch = new CountDownLatch(1);
|
||||
myStoppableSubscriptionDeliveringRestHookSubscriber.setCountDownLatch(countDownLatch);
|
||||
|
||||
ourLog.info("** About to send observation");
|
||||
Observation observation = sendObservation(code, "SNOMED-CT");
|
||||
assertEquals("1", observation.getIdElement().getVersionIdPart());
|
||||
assertNull(observation.getNoteFirstRep().getText());
|
||||
|
||||
observation.getNoteFirstRep().setText("changed");
|
||||
MethodOutcome methodOutcome = ourClient.update().resource(observation).execute();
|
||||
assertEquals("2", methodOutcome.getId().getVersionIdPart());
|
||||
assertEquals("changed", observation.getNoteFirstRep().getText());
|
||||
|
||||
// Wait for our two delivery channel threads to be paused
|
||||
assertTrue(countDownLatch.await(5L, TimeUnit.SECONDS));
|
||||
// Open the floodgates!
|
||||
myStoppableSubscriptionDeliveringRestHookSubscriber.unPause();
|
||||
|
||||
|
||||
waitForSize(0, ourCreatedObservations);
|
||||
waitForSize(2, ourUpdatedObservations);
|
||||
|
||||
Observation observation1 = ourUpdatedObservations.get(0);
|
||||
Observation observation2 = ourUpdatedObservations.get(1);
|
||||
|
||||
assertEquals("2", observation1.getIdElement().getVersionIdPart());
|
||||
assertEquals("changed", observation1.getNoteFirstRep().getText());
|
||||
assertEquals("2", observation2.getIdElement().getVersionIdPart());
|
||||
assertEquals("changed", observation2.getNoteFirstRep().getText());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRestHookSubscriptionApplicationJson() throws Exception {
|
||||
String payload = "application/json";
|
||||
|
||||
String code = "1000000050";
|
||||
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
||||
String criteria2 = "Observation?code=SNOMED-CT|" + code + "111&_format=xml";
|
||||
|
||||
Subscription subscription1 = createSubscription(criteria1, payload);
|
||||
Subscription subscription2 = createSubscription(criteria2, payload);
|
||||
waitForActivatedSubscriptionCount(2);
|
||||
|
||||
Observation observation1 = sendObservation(code, "SNOMED-CT");
|
||||
|
||||
// Should see 1 subscription notification
|
||||
waitForQueueToDrain();
|
||||
waitForSize(0, ourCreatedObservations);
|
||||
waitForSize(1, ourUpdatedObservations);
|
||||
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0));
|
||||
|
||||
assertEquals("1", ourUpdatedObservations.get(0).getIdElement().getVersionIdPart());
|
||||
|
||||
Subscription subscriptionTemp = ourClient.read(Subscription.class, subscription2.getId());
|
||||
Assert.assertNotNull(subscriptionTemp);
|
||||
|
||||
subscriptionTemp.setCriteria(criteria1);
|
||||
ourClient.update().resource(subscriptionTemp).withId(subscriptionTemp.getIdElement()).execute();
|
||||
waitForQueueToDrain();
|
||||
|
||||
Observation observation2 = sendObservation(code, "SNOMED-CT");
|
||||
waitForQueueToDrain();
|
||||
|
||||
// Should see two subscription notifications
|
||||
waitForSize(0, ourCreatedObservations);
|
||||
waitForSize(3, ourUpdatedObservations);
|
||||
|
||||
ourClient.delete().resourceById(new IdType("Subscription/" + subscription2.getId())).execute();
|
||||
waitForQueueToDrain();
|
||||
|
||||
Observation observationTemp3 = sendObservation(code, "SNOMED-CT");
|
||||
waitForQueueToDrain();
|
||||
|
||||
// Should see only one subscription notification
|
||||
waitForSize(0, ourCreatedObservations);
|
||||
waitForSize(4, ourUpdatedObservations);
|
||||
|
||||
Observation observation3 = ourClient.read(Observation.class, observationTemp3.getId());
|
||||
CodeableConcept codeableConcept = new CodeableConcept();
|
||||
observation3.setCode(codeableConcept);
|
||||
Coding coding = codeableConcept.addCoding();
|
||||
coding.setCode(code + "111");
|
||||
coding.setSystem("SNOMED-CT");
|
||||
ourClient.update().resource(observation3).withId(observation3.getIdElement()).execute();
|
||||
|
||||
// Should see no subscription notification
|
||||
waitForQueueToDrain();
|
||||
waitForSize(0, ourCreatedObservations);
|
||||
waitForSize(4, ourUpdatedObservations);
|
||||
|
||||
Observation observation3a = ourClient.read(Observation.class, observationTemp3.getId());
|
||||
|
||||
CodeableConcept codeableConcept1 = new CodeableConcept();
|
||||
observation3a.setCode(codeableConcept1);
|
||||
Coding coding1 = codeableConcept1.addCoding();
|
||||
coding1.setCode(code);
|
||||
coding1.setSystem("SNOMED-CT");
|
||||
ourClient.update().resource(observation3a).withId(observation3a.getIdElement()).execute();
|
||||
|
||||
// Should see only one subscription notification
|
||||
waitForQueueToDrain();
|
||||
waitForSize(0, ourCreatedObservations);
|
||||
waitForSize(5, ourUpdatedObservations);
|
||||
|
||||
assertFalse(subscription1.getId().equals(subscription2.getId()));
|
||||
assertFalse(observation1.getId().isEmpty());
|
||||
assertFalse(observation2.getId().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRestHookSubscriptionApplicationJsonDatabase() throws Exception {
|
||||
// Same test as above, but now run it using database matching
|
||||
myDaoConfig.setEnableInMemorySubscriptionMatching(false);
|
||||
String payload = "application/json";
|
||||
|
||||
String code = "1000000050";
|
||||
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
||||
String criteria2 = "Observation?code=SNOMED-CT|" + code + "111&_format=xml";
|
||||
|
||||
Subscription subscription1 = createSubscription(criteria1, payload);
|
||||
Subscription subscription2 = createSubscription(criteria2, payload);
|
||||
waitForActivatedSubscriptionCount(2);
|
||||
|
||||
Observation observation1 = sendObservation(code, "SNOMED-CT");
|
||||
|
||||
// Should see 1 subscription notification
|
||||
waitForQueueToDrain();
|
||||
waitForSize(0, ourCreatedObservations);
|
||||
waitForSize(1, ourUpdatedObservations);
|
||||
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0));
|
||||
|
||||
assertEquals("1", ourUpdatedObservations.get(0).getIdElement().getVersionIdPart());
|
||||
|
||||
Subscription subscriptionTemp = ourClient.read(Subscription.class, subscription2.getId());
|
||||
Assert.assertNotNull(subscriptionTemp);
|
||||
|
||||
subscriptionTemp.setCriteria(criteria1);
|
||||
ourClient.update().resource(subscriptionTemp).withId(subscriptionTemp.getIdElement()).execute();
|
||||
waitForQueueToDrain();
|
||||
|
||||
Observation observation2 = sendObservation(code, "SNOMED-CT");
|
||||
waitForQueueToDrain();
|
||||
|
||||
// Should see two subscription notifications
|
||||
waitForSize(0, ourCreatedObservations);
|
||||
waitForSize(3, ourUpdatedObservations);
|
||||
|
||||
ourClient.delete().resourceById(new IdType("Subscription/" + subscription2.getId())).execute();
|
||||
waitForQueueToDrain();
|
||||
|
||||
Observation observationTemp3 = sendObservation(code, "SNOMED-CT");
|
||||
waitForQueueToDrain();
|
||||
|
||||
// Should see only one subscription notification
|
||||
waitForSize(0, ourCreatedObservations);
|
||||
waitForSize(4, ourUpdatedObservations);
|
||||
|
||||
Observation observation3 = ourClient.read(Observation.class, observationTemp3.getId());
|
||||
CodeableConcept codeableConcept = new CodeableConcept();
|
||||
observation3.setCode(codeableConcept);
|
||||
Coding coding = codeableConcept.addCoding();
|
||||
coding.setCode(code + "111");
|
||||
coding.setSystem("SNOMED-CT");
|
||||
ourClient.update().resource(observation3).withId(observation3.getIdElement()).execute();
|
||||
|
||||
// Should see no subscription notification
|
||||
waitForQueueToDrain();
|
||||
waitForSize(0, ourCreatedObservations);
|
||||
waitForSize(4, ourUpdatedObservations);
|
||||
|
||||
Observation observation3a = ourClient.read(Observation.class, observationTemp3.getId());
|
||||
|
||||
CodeableConcept codeableConcept1 = new CodeableConcept();
|
||||
observation3a.setCode(codeableConcept1);
|
||||
Coding coding1 = codeableConcept1.addCoding();
|
||||
coding1.setCode(code);
|
||||
coding1.setSystem("SNOMED-CT");
|
||||
ourClient.update().resource(observation3a).withId(observation3a.getIdElement()).execute();
|
||||
|
||||
// Should see only one subscription notification
|
||||
waitForQueueToDrain();
|
||||
waitForSize(0, ourCreatedObservations);
|
||||
waitForSize(5, ourUpdatedObservations);
|
||||
|
||||
assertFalse(subscription1.getId().equals(subscription2.getId()));
|
||||
assertFalse(observation1.getId().isEmpty());
|
||||
assertFalse(observation2.getId().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRestHookSubscriptionApplicationXml() throws Exception {
|
||||
String payload = "application/xml";
|
||||
|
||||
String code = "1000000050";
|
||||
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
||||
String criteria2 = "Observation?code=SNOMED-CT|" + code + "111&_format=xml";
|
||||
|
||||
Subscription subscription1 = createSubscription(criteria1, payload);
|
||||
Subscription subscription2 = createSubscription(criteria2, payload);
|
||||
waitForActivatedSubscriptionCount(2);
|
||||
|
||||
ourLog.info("** About to send obervation");
|
||||
Observation observation1 = sendObservation(code, "SNOMED-CT");
|
||||
|
||||
// Should see 1 subscription notification
|
||||
waitForSize(0, ourCreatedObservations);
|
||||
waitForSize(1, ourUpdatedObservations);
|
||||
assertEquals(Constants.CT_FHIR_XML_NEW, ourContentTypes.get(0));
|
||||
|
||||
Subscription subscriptionTemp = ourClient.read(Subscription.class, subscription2.getId());
|
||||
Assert.assertNotNull(subscriptionTemp);
|
||||
subscriptionTemp.setCriteria(criteria1);
|
||||
ourClient.update().resource(subscriptionTemp).withId(subscriptionTemp.getIdElement()).execute();
|
||||
waitForQueueToDrain();
|
||||
|
||||
Observation observation2 = sendObservation(code, "SNOMED-CT");
|
||||
waitForQueueToDrain();
|
||||
|
||||
// Should see two subscription notifications
|
||||
waitForSize(0, ourCreatedObservations);
|
||||
waitForSize(3, ourUpdatedObservations);
|
||||
|
||||
ourClient.delete().resourceById(new IdType("Subscription/" + subscription2.getId())).execute();
|
||||
|
||||
Observation observationTemp3 = sendObservation(code, "SNOMED-CT");
|
||||
|
||||
// Should see only one subscription notification
|
||||
waitForQueueToDrain();
|
||||
waitForSize(0, ourCreatedObservations);
|
||||
waitForSize(4, ourUpdatedObservations);
|
||||
|
||||
Observation observation3 = ourClient.read(Observation.class, observationTemp3.getId());
|
||||
CodeableConcept codeableConcept = new CodeableConcept();
|
||||
observation3.setCode(codeableConcept);
|
||||
Coding coding = codeableConcept.addCoding();
|
||||
coding.setCode(code + "111");
|
||||
coding.setSystem("SNOMED-CT");
|
||||
ourClient.update().resource(observation3).withId(observation3.getIdElement()).execute();
|
||||
|
||||
// Should see no subscription notification
|
||||
waitForQueueToDrain();
|
||||
waitForSize(0, ourCreatedObservations);
|
||||
waitForSize(4, ourUpdatedObservations);
|
||||
|
||||
Observation observation3a = ourClient.read(Observation.class, observationTemp3.getId());
|
||||
|
||||
CodeableConcept codeableConcept1 = new CodeableConcept();
|
||||
observation3a.setCode(codeableConcept1);
|
||||
Coding coding1 = codeableConcept1.addCoding();
|
||||
coding1.setCode(code);
|
||||
coding1.setSystem("SNOMED-CT");
|
||||
ourClient.update().resource(observation3a).withId(observation3a.getIdElement()).execute();
|
||||
|
||||
// Should see only one subscription notification
|
||||
waitForQueueToDrain();
|
||||
waitForSize(0, ourCreatedObservations);
|
||||
waitForSize(5, ourUpdatedObservations);
|
||||
|
||||
assertFalse(subscription1.getId().equals(subscription2.getId()));
|
||||
assertFalse(observation1.getId().isEmpty());
|
||||
assertFalse(observation2.getId().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubscriptionTriggerViaSubscription() throws Exception {
|
||||
String payload = "application/xml";
|
||||
|
||||
String code = "1000000050";
|
||||
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
||||
|
||||
createSubscription(criteria1, payload);
|
||||
waitForActivatedSubscriptionCount(1);
|
||||
|
||||
ourLog.info("** About to send obervation");
|
||||
|
||||
Observation observation = new Observation();
|
||||
observation.addIdentifier().setSystem("foo").setValue("bar1");
|
||||
observation.setId(IdType.newRandomUuid().getValue());
|
||||
CodeableConcept codeableConcept = new CodeableConcept()
|
||||
.addCoding(new Coding().setCode(code).setSystem("SNOMED-CT"));
|
||||
observation.setCode(codeableConcept);
|
||||
observation.setStatus(Observation.ObservationStatus.FINAL);
|
||||
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier().setSystem("foo").setValue("bar2");
|
||||
patient.setId(IdType.newRandomUuid().getValue());
|
||||
patient.setActive(true);
|
||||
observation.getSubject().setReference(patient.getId());
|
||||
|
||||
Bundle requestBundle = new Bundle();
|
||||
requestBundle.setType(Bundle.BundleType.TRANSACTION);
|
||||
requestBundle.addEntry()
|
||||
.setResource(observation)
|
||||
.setFullUrl(observation.getId())
|
||||
.getRequest()
|
||||
.setUrl("Obervation?identifier=foo|bar1")
|
||||
.setMethod(Bundle.HTTPVerb.PUT);
|
||||
requestBundle.addEntry()
|
||||
.setResource(patient)
|
||||
.setFullUrl(patient.getId())
|
||||
.getRequest()
|
||||
.setUrl("Patient?identifier=foo|bar2")
|
||||
.setMethod(Bundle.HTTPVerb.PUT);
|
||||
ourClient.transaction().withBundle(requestBundle).execute();
|
||||
|
||||
// Should see 1 subscription notification
|
||||
waitForSize(0, ourCreatedObservations);
|
||||
waitForSize(1, ourUpdatedObservations);
|
||||
assertEquals(Constants.CT_FHIR_XML_NEW, ourContentTypes.get(0));
|
||||
|
||||
Observation obs = ourUpdatedObservations.get(0);
|
||||
ourLog.info("Observation content: {}", myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(obs));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateSubscriptionToMatchLater() throws Exception {
|
||||
String payload = "application/xml";
|
||||
|
||||
String code = "1000000050";
|
||||
String criteriaBad = "Observation?code=SNOMED-CT|" + code + "111&_format=xml";
|
||||
|
||||
ourLog.info("** About to create non-matching subscription");
|
||||
|
||||
Subscription subscription2 = createSubscription(criteriaBad, payload);
|
||||
|
||||
ourLog.info("** About to send observation that wont match");
|
||||
|
||||
Observation observation1 = sendObservation(code, "SNOMED-CT");
|
||||
|
||||
// Criteria didn't match, shouldn't see any updates
|
||||
waitForQueueToDrain();
|
||||
Thread.sleep(1000);
|
||||
assertEquals(0, ourUpdatedObservations.size());
|
||||
|
||||
Subscription subscriptionTemp = ourClient.read().resource(Subscription.class).withId(subscription2.getId()).execute();
|
||||
Assert.assertNotNull(subscriptionTemp);
|
||||
String criteriaGood = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
||||
subscriptionTemp.setCriteria(criteriaGood);
|
||||
ourLog.info("** About to update subscription");
|
||||
ourClient.update().resource(subscriptionTemp).withId(subscriptionTemp.getIdElement()).execute();
|
||||
waitForQueueToDrain();
|
||||
|
||||
ourLog.info("** About to send Observation 2");
|
||||
Observation observation2 = sendObservation(code, "SNOMED-CT");
|
||||
waitForQueueToDrain();
|
||||
|
||||
// Should see a subscription notification this time
|
||||
waitForSize(0, ourCreatedObservations);
|
||||
waitForSize(1, ourUpdatedObservations);
|
||||
|
||||
ourClient.delete().resourceById(new IdType("Subscription/" + subscription2.getId())).execute();
|
||||
|
||||
Observation observationTemp3 = sendObservation(code, "SNOMED-CT");
|
||||
|
||||
// No more matches
|
||||
Thread.sleep(1000);
|
||||
assertEquals(1, ourUpdatedObservations.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRestHookSubscriptionApplicationXmlJson() throws Exception {
|
||||
String payload = "application/fhir+xml";
|
||||
|
||||
String code = "1000000050";
|
||||
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
||||
String criteria2 = "Observation?code=SNOMED-CT|" + code + "111&_format=xml";
|
||||
|
||||
Subscription subscription1 = createSubscription(criteria1, payload);
|
||||
Subscription subscription2 = createSubscription(criteria2, payload);
|
||||
waitForActivatedSubscriptionCount(2);
|
||||
|
||||
Observation observation1 = sendObservation(code, "SNOMED-CT");
|
||||
|
||||
// Should see 1 subscription notification
|
||||
waitForQueueToDrain();
|
||||
waitForSize(0, ourCreatedObservations);
|
||||
waitForSize(1, ourUpdatedObservations);
|
||||
assertEquals(Constants.CT_FHIR_XML_NEW, ourContentTypes.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRestHookSubscriptionInvalidCriteria() throws Exception {
|
||||
String payload = "application/xml";
|
||||
|
||||
String criteria1 = "Observation?codeeeee=SNOMED-CT";
|
||||
|
||||
try {
|
||||
createSubscription(criteria1, payload);
|
||||
fail();
|
||||
} catch (UnprocessableEntityException e) {
|
||||
assertEquals("HTTP 422 Unprocessable Entity: Invalid subscription criteria submitted: Observation?codeeeee=SNOMED-CT Failed to parse match URL[Observation?codeeeee=SNOMED-CT] - Resource type Observation does not have a parameter with name: codeeeee", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubscriptionWithHeaders() throws Exception {
|
||||
String payload = "application/fhir+json";
|
||||
|
||||
String code = "1000000050";
|
||||
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
||||
|
||||
// Add some headers, and we'll also turn back to requested status for fun
|
||||
Subscription subscription = createSubscription(criteria1, payload);
|
||||
waitForActivatedSubscriptionCount(1);
|
||||
|
||||
subscription.getChannel().addHeader("X-Foo: FOO");
|
||||
subscription.getChannel().addHeader("X-Bar: BAR");
|
||||
subscription.setStatus(Subscription.SubscriptionStatus.REQUESTED);
|
||||
ourClient.update().resource(subscription).execute();
|
||||
waitForQueueToDrain();
|
||||
|
||||
sendObservation(code, "SNOMED-CT");
|
||||
|
||||
// Should see 1 subscription notification
|
||||
waitForQueueToDrain();
|
||||
waitForSize(0, ourCreatedObservations);
|
||||
waitForSize(1, ourUpdatedObservations);
|
||||
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0));
|
||||
assertThat(ourHeaders, hasItem("X-Foo: FOO"));
|
||||
assertThat(ourHeaders, hasItem("X-Bar: BAR"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDisableSubscription() throws Exception {
|
||||
String payload = "application/fhir+json";
|
||||
|
||||
String code = "1000000050";
|
||||
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
||||
|
||||
Subscription subscription = createSubscription(criteria1, payload);
|
||||
waitForActivatedSubscriptionCount(1);
|
||||
|
||||
sendObservation(code, "SNOMED-CT");
|
||||
|
||||
// Should see 1 subscription notification
|
||||
waitForQueueToDrain();
|
||||
waitForSize(0, ourCreatedObservations);
|
||||
waitForSize(1, ourUpdatedObservations);
|
||||
|
||||
// Disable
|
||||
subscription.setStatus(Subscription.SubscriptionStatus.OFF);
|
||||
ourClient.update().resource(subscription).execute();
|
||||
waitForQueueToDrain();
|
||||
|
||||
// Send another object
|
||||
sendObservation(code, "SNOMED-CT");
|
||||
|
||||
// Should see 1 subscription notification
|
||||
waitForQueueToDrain();
|
||||
waitForSize(0, ourCreatedObservations);
|
||||
waitForSize(1, ourUpdatedObservations);
|
||||
|
||||
}
|
||||
|
||||
@Test(expected = UnprocessableEntityException.class)
|
||||
public void testInvalidProvenanceParam() {
|
||||
String payload = "application/fhir+json";
|
||||
String criteriabad = "Provenance?activity=http://hl7.org/fhir/v3/DocumentCompletion%7CAU";
|
||||
Subscription subscription = newSubscription(criteriabad, payload);
|
||||
ourClient.create().resource(subscription).execute();
|
||||
}
|
||||
|
||||
@Test(expected = UnprocessableEntityException.class)
|
||||
public void testInvalidProcedureRequestParam() {
|
||||
String payload = "application/fhir+json";
|
||||
String criteriabad = "ProcedureRequest?intent=instance-order&category=Laboratory";
|
||||
Subscription subscription = newSubscription(criteriabad, payload);
|
||||
ourClient.create().resource(subscription).execute();
|
||||
}
|
||||
|
||||
@Test(expected = UnprocessableEntityException.class)
|
||||
public void testInvalidBodySiteParam() {
|
||||
String payload = "application/fhir+json";
|
||||
String criteriabad = "BodySite?accessType=Catheter";
|
||||
Subscription subscription = newSubscription(criteriabad, payload);
|
||||
ourClient.create().resource(subscription).execute();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGoodSubscriptionPersists() {
|
||||
assertEquals(0, subscriptionCount());
|
||||
String payload = "application/fhir+json";
|
||||
String criteriaGood = "Patient?gender=male";
|
||||
Subscription subscription = newSubscription(criteriaGood, payload);
|
||||
ourClient.create().resource(subscription).execute();
|
||||
assertEquals(1, subscriptionCount());
|
||||
}
|
||||
|
||||
private int subscriptionCount() {
|
||||
IBaseBundle found = ourClient.search().forResource(Subscription.class).cacheControl(new CacheControlDirective().setNoCache(true)).execute();
|
||||
return toUnqualifiedVersionlessIdValues(found).size();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubscriptionWithNoStatusIsRejected() {
|
||||
Subscription subscription = newSubscription("Observation?", "application/json");
|
||||
subscription.setStatus(null);
|
||||
|
||||
try {
|
||||
ourClient.create().resource(subscription).execute();
|
||||
fail();
|
||||
} catch (UnprocessableEntityException e) {
|
||||
assertThat(e.getMessage(), containsString("Can not process submitted Subscription - Subscription.status must be populated on this server"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testBadSubscriptionDoesntPersist() {
|
||||
assertEquals(0, subscriptionCount());
|
||||
String payload = "application/fhir+json";
|
||||
String criteriaBad = "BodySite?accessType=Catheter";
|
||||
Subscription subscription = newSubscription(criteriaBad, payload);
|
||||
try {
|
||||
ourClient.create().resource(subscription).execute();
|
||||
} catch (UnprocessableEntityException e) {
|
||||
ourLog.info("Expected exception", e);
|
||||
}
|
||||
assertEquals(0, subscriptionCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomSearchParam() throws Exception {
|
||||
String criteria = "Observation?accessType=Catheter,PD%20Catheter";
|
||||
|
||||
SearchParameter sp = new SearchParameter();
|
||||
sp.addBase("Observation");
|
||||
sp.setCode("accessType");
|
||||
sp.setType(Enumerations.SearchParamType.TOKEN);
|
||||
sp.setExpression("Observation.extension('Observation#accessType')");
|
||||
sp.setXpathUsage(SearchParameter.XPathUsageType.NORMAL);
|
||||
sp.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
mySearchParameterDao.create(sp);
|
||||
mySearchParamRegistry.forceRefresh();
|
||||
createSubscription(criteria, "application/json");
|
||||
waitForActivatedSubscriptionCount(1);
|
||||
|
||||
{
|
||||
Observation bodySite = new Observation();
|
||||
bodySite.addExtension().setUrl("Observation#accessType").setValue(new Coding().setCode("Catheter"));
|
||||
MethodOutcome methodOutcome = ourClient.create().resource(bodySite).execute();
|
||||
assertEquals(true, methodOutcome.getCreated());
|
||||
waitForQueueToDrain();
|
||||
waitForSize(1, ourUpdatedObservations);
|
||||
}
|
||||
{
|
||||
Observation observation = new Observation();
|
||||
observation.addExtension().setUrl("Observation#accessType").setValue(new Coding().setCode("PD Catheter"));
|
||||
MethodOutcome methodOutcome = ourClient.create().resource(observation).execute();
|
||||
assertEquals(true, methodOutcome.getCreated());
|
||||
waitForQueueToDrain();
|
||||
waitForSize(2, ourUpdatedObservations);
|
||||
}
|
||||
{
|
||||
Observation observation = new Observation();
|
||||
MethodOutcome methodOutcome = ourClient.create().resource(observation).execute();
|
||||
assertEquals(true, methodOutcome.getCreated());
|
||||
waitForQueueToDrain();
|
||||
waitForSize(2, ourUpdatedObservations);
|
||||
}
|
||||
{
|
||||
Observation observation = new Observation();
|
||||
observation.addExtension().setUrl("Observation#accessType").setValue(new Coding().setCode("XXX"));
|
||||
MethodOutcome methodOutcome = ourClient.create().resource(observation).execute();
|
||||
assertEquals(true, methodOutcome.getCreated());
|
||||
waitForQueueToDrain();
|
||||
waitForSize(2, ourUpdatedObservations);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -141,6 +141,41 @@
|
|||
<artifactId>commons-dbcp2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<version>42.2.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.bitbucket.b_c</groupId>
|
||||
<artifactId>jose4j</artifactId>
|
||||
<version>0.6.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.json</groupId>
|
||||
<artifactId>json</artifactId>
|
||||
<version>20180130</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
<version>2.0.2.RELEASE</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security.oauth</groupId>
|
||||
<artifactId>spring-security-oauth2</artifactId>
|
||||
<version>2.3.3.RELEASE</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-xml</artifactId>
|
||||
<version>2.9.4</version>
|
||||
</dependency>
|
||||
|
||||
<!-- This example uses Derby embedded database. If you are using another database such as Mysql or Oracle, you may omit the following dependencies and replace them with an appropriate database client
|
||||
dependency for your database platform. -->
|
||||
<dependency>
|
||||
|
@ -244,8 +279,8 @@
|
|||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
|
@ -288,8 +323,8 @@
|
|||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>integration-test</goal>
|
||||
<goal>verify</goal>
|
||||
<!-- <goal>integration-test</goal> -->
|
||||
<!-- <goal>verify</goal> -->
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.slf4j.Logger;
|
|||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.Set;
|
||||
|
||||
public class ModifyColumnTask extends BaseTableColumnTypeTask<ModifyColumnTask> {
|
||||
|
||||
|
@ -33,10 +34,17 @@ public class ModifyColumnTask extends BaseTableColumnTypeTask<ModifyColumnTask>
|
|||
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
public void execute() throws SQLException {
|
||||
|
||||
String existingType;
|
||||
boolean nullable;
|
||||
|
||||
Set<String> columnNames = JdbcUtils.getColumnNames(getConnectionProperties(), getTableName());
|
||||
if (!columnNames.contains(getColumnName())) {
|
||||
ourLog.info("Column {} doesn't exist on table {} - No action performed", getColumnName(), getTableName());
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
existingType = JdbcUtils.getColumnType(getConnectionProperties(), getTableName(), getColumnName());
|
||||
nullable = JdbcUtils.isColumnNullable(getConnectionProperties(), getTableName(), getColumnName());
|
||||
|
@ -115,5 +123,4 @@ public class ModifyColumnTask extends BaseTableColumnTypeTask<ModifyColumnTask>
|
|||
executeSql(getTableName(), sqlNotNull);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import org.junit.Test;
|
|||
|
||||
import java.sql.SQLException;
|
||||
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class ModifyColumnTest extends BaseTest {
|
||||
|
@ -111,4 +112,20 @@ public class ModifyColumnTest extends BaseTest {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testColumnDoesntAlreadyExist() throws SQLException {
|
||||
executeSql("create table SOMETABLE (PID bigint, TEXTCOL varchar(255))");
|
||||
|
||||
ModifyColumnTask task = new ModifyColumnTask();
|
||||
task.setTableName("SOMETABLE");
|
||||
task.setColumnName("SOMECOLUMN");
|
||||
task.setDescription("Make nullable");
|
||||
task.setNullable(true);
|
||||
getMigrator().addTask(task);
|
||||
|
||||
getMigrator().migrate();
|
||||
|
||||
assertThat(JdbcUtils.getColumnNames(getConnectionProperties(), "SOMETABLE"), containsInAnyOrder("PID", "TEXTCOL"));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -46,6 +46,15 @@ public class LogicalReferenceHelper {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Account for common logical references
|
||||
*/
|
||||
|
||||
if (theId.getValue().startsWith("http://fhir.org/guides/argonaut/")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -34,6 +34,8 @@ import org.hl7.fhir.instance.model.api.IBaseReference;
|
|||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
import org.hl7.fhir.r4.model.Extension;
|
||||
import org.hl7.fhir.r4.model.codesystems.SubscriptionStatus;
|
||||
import org.hl7.fhir.r5.model.Subscription;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
@ -67,6 +69,8 @@ public class SubscriptionCanonicalizer<S extends IBaseResource> {
|
|||
return canonicalizeDstu3(theSubscription);
|
||||
case R4:
|
||||
return canonicalizeR4(theSubscription);
|
||||
case R5:
|
||||
return canonicalizeR5(theSubscription);
|
||||
case DSTU2_HL7ORG:
|
||||
case DSTU2_1:
|
||||
default:
|
||||
|
@ -169,6 +173,14 @@ public class SubscriptionCanonicalizer<S extends IBaseResource> {
|
|||
.stream()
|
||||
.collect(Collectors.groupingBy(t -> t.getUrl(), mapping(t -> t.getValueAsPrimitive().getValueAsString(), toList())));
|
||||
}
|
||||
case R5: {
|
||||
org.hl7.fhir.r5.model.Subscription subscription = (org.hl7.fhir.r5.model.Subscription) theSubscription;
|
||||
return subscription
|
||||
.getChannel()
|
||||
.getExtension()
|
||||
.stream()
|
||||
.collect(Collectors.groupingBy(t -> t.getUrl(), mapping(t -> t.getValueAsPrimitive().getValueAsString(), toList())));
|
||||
}
|
||||
case DSTU2_HL7ORG:
|
||||
case DSTU2_1:
|
||||
default: {
|
||||
|
@ -232,6 +244,56 @@ public class SubscriptionCanonicalizer<S extends IBaseResource> {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
private CanonicalSubscription canonicalizeR5(IBaseResource theSubscription) {
|
||||
org.hl7.fhir.r5.model.Subscription subscription = (org.hl7.fhir.r5.model.Subscription) theSubscription;
|
||||
|
||||
CanonicalSubscription retVal = new CanonicalSubscription();
|
||||
retVal.setStatus(org.hl7.fhir.r4.model.Subscription.SubscriptionStatus.fromCode(subscription.getStatus().toCode()));
|
||||
retVal.setChannelType(CanonicalSubscriptionChannelType.fromCode(subscription.getChannel().getType().toCode()));
|
||||
retVal.setCriteriaString(subscription.getCriteria());
|
||||
retVal.setEndpointUrl(subscription.getChannel().getEndpoint());
|
||||
retVal.setHeaders(subscription.getChannel().getHeader());
|
||||
retVal.setChannelExtensions(extractExtension(subscription));
|
||||
retVal.setIdElement(subscription.getIdElement());
|
||||
retVal.setPayloadString(subscription.getChannel().getPayload());
|
||||
|
||||
if (retVal.getChannelType() == CanonicalSubscriptionChannelType.EMAIL) {
|
||||
String from;
|
||||
String subjectTemplate;
|
||||
try {
|
||||
from = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_EMAIL_FROM);
|
||||
subjectTemplate = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_SUBJECT_TEMPLATE);
|
||||
} catch (FHIRException theE) {
|
||||
throw new ConfigurationException("Failed to extract subscription extension(s): " + theE.getMessage(), theE);
|
||||
}
|
||||
retVal.getEmailDetails().setFrom(from);
|
||||
retVal.getEmailDetails().setSubjectTemplate(subjectTemplate);
|
||||
}
|
||||
|
||||
if (retVal.getChannelType() == CanonicalSubscriptionChannelType.RESTHOOK) {
|
||||
String stripVersionIds;
|
||||
String deliverLatestVersion;
|
||||
try {
|
||||
stripVersionIds = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_STRIP_VERSION_IDS);
|
||||
deliverLatestVersion = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_DELIVER_LATEST_VERSION);
|
||||
} catch (FHIRException theE) {
|
||||
throw new ConfigurationException("Failed to extract subscription extension(s): " + theE.getMessage(), theE);
|
||||
}
|
||||
retVal.getRestHookDetails().setStripVersionId(Boolean.parseBoolean(stripVersionIds));
|
||||
retVal.getRestHookDetails().setDeliverLatestVersion(Boolean.parseBoolean(deliverLatestVersion));
|
||||
}
|
||||
|
||||
List<org.hl7.fhir.r5.model.Extension> topicExts = subscription.getExtensionsByUrl("http://hl7.org/fhir/subscription/topics");
|
||||
if (topicExts.size() > 0) {
|
||||
IBaseReference ref = (IBaseReference) topicExts.get(0).getValueAsPrimitive();
|
||||
if (!"EventDefinition".equals(ref.getReferenceElement().getResourceType())) {
|
||||
throw new PreconditionFailedException("Topic reference must be an EventDefinition");
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public String getCriteria(IBaseResource theSubscription) {
|
||||
switch (myFhirContext.getVersion().getVersion()) {
|
||||
case DSTU2:
|
||||
|
@ -240,6 +302,8 @@ public class SubscriptionCanonicalizer<S extends IBaseResource> {
|
|||
return ((org.hl7.fhir.dstu3.model.Subscription) theSubscription).getCriteria();
|
||||
case R4:
|
||||
return ((org.hl7.fhir.r4.model.Subscription) theSubscription).getCriteria();
|
||||
case R5:
|
||||
return ((org.hl7.fhir.r5.model.Subscription) theSubscription).getCriteria();
|
||||
case DSTU2_1:
|
||||
case DSTU2_HL7ORG:
|
||||
default:
|
||||
|
|
|
@ -53,7 +53,7 @@ public class SubscriptionLoader {
|
|||
private ISubscriptionProvider mySubscriptionProvider;
|
||||
@Autowired
|
||||
private SubscriptionRegistry mySubscriptionRegistry;
|
||||
@Autowired
|
||||
@Autowired(required = false)
|
||||
private IDaoRegistry myDaoRegistry;
|
||||
|
||||
private final Object mySyncSubscriptionsLock = new Object();
|
||||
|
@ -65,7 +65,7 @@ public class SubscriptionLoader {
|
|||
@SuppressWarnings("unused")
|
||||
@Scheduled(fixedDelay = DateUtils.MILLIS_PER_MINUTE)
|
||||
public void syncSubscriptions() {
|
||||
if (!myDaoRegistry.isResourceTypeSupported("Subscription")) {
|
||||
if (myDaoRegistry != null && !myDaoRegistry.isResourceTypeSupported("Subscription")) {
|
||||
return;
|
||||
}
|
||||
if (!mySyncSubscriptionsSemaphore.tryAcquire()) {
|
||||
|
|
|
@ -20,9 +20,10 @@ package ca.uhn.fhir.jpa.subscription.module.config;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.jpa.searchparam.config.SearchParamDstu2Config;
|
||||
import ca.uhn.fhir.jpa.searchparam.config.SearchParamDstu3Config;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
@Import({SearchParamDstu3Config.class})
|
||||
@Import({SearchParamDstu2Config.class})
|
||||
public class SubscriptionDstu2Config extends BaseSubscriptionConfig {
|
||||
}
|
||||
|
|
|
@ -24,5 +24,5 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
|
|||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
||||
public interface IResourceRetriever {
|
||||
IBaseResource getResource(IIdType id);
|
||||
IBaseResource getResource(IIdType theId);
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ package ca.uhn.fhir.jpa.subscription.module.subscriber;
|
|||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription;
|
||||
import ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
|
@ -73,6 +74,14 @@ public class ResourceDeliveryMessage extends BaseResourceMessage implements IRes
|
|||
return retVal;
|
||||
}
|
||||
|
||||
public String getPayloadString() {
|
||||
if (this.myPayloadString != null) {
|
||||
return this.myPayloadString;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
public IIdType getPayloadId(FhirContext theCtx) {
|
||||
IIdType retVal = null;
|
||||
if (myPayloadId != null) {
|
||||
|
@ -89,9 +98,9 @@ public class ResourceDeliveryMessage extends BaseResourceMessage implements IRes
|
|||
mySubscription = theSubscription;
|
||||
}
|
||||
|
||||
public void setPayload(FhirContext theCtx, IBaseResource thePayload) {
|
||||
public void setPayload(FhirContext theCtx, IBaseResource thePayload, EncodingEnum theEncoding) {
|
||||
myPayload = thePayload;
|
||||
myPayloadString = theCtx.newJsonParser().encodeResourceToString(thePayload);
|
||||
myPayloadString = theEncoding.newParser(theCtx).encodeResourceToString(thePayload);
|
||||
myPayloadId = thePayload.getIdElement().toUnqualified().getValue();
|
||||
}
|
||||
|
||||
|
|
|
@ -57,10 +57,8 @@ public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionDe
|
|||
|
||||
protected void deliverPayload(ResourceDeliveryMessage theMsg, CanonicalSubscription theSubscription, EncodingEnum thePayloadType, IGenericClient theClient) {
|
||||
IBaseResource payloadResource = getAndMassagePayload(theMsg, theSubscription);
|
||||
if (payloadResource == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Regardless of whether we have a payload, the rest-hook should be sent.
|
||||
doDelivery(theMsg, theSubscription, thePayloadType, theClient, payloadResource);
|
||||
}
|
||||
|
||||
|
@ -117,8 +115,13 @@ public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionDe
|
|||
|
||||
if (payloadResource == null || theSubscription.getRestHookDetails().isDeliverLatestVersion()) {
|
||||
IIdType payloadId = theMsg.getPayloadId(myFhirContext);
|
||||
|
||||
try {
|
||||
payloadResource = myResourceRetriever.getResource(payloadId.toVersionless());
|
||||
if (payloadId != null) {
|
||||
payloadResource = myResourceRetriever.getResource(payloadId.toVersionless());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (ResourceGoneException e) {
|
||||
ourLog.warn("Resource {} is deleted, not going to deliver for subscription {}", payloadId.toVersionless(), theSubscription.getIdElement(myFhirContext));
|
||||
return null;
|
||||
|
@ -152,10 +155,6 @@ public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionDe
|
|||
String payloadString = subscription.getPayloadString();
|
||||
EncodingEnum payloadType = null;
|
||||
if (payloadString != null) {
|
||||
if (payloadString.contains(";")) {
|
||||
payloadString = payloadString.substring(0, payloadString.indexOf(';'));
|
||||
}
|
||||
payloadString = payloadString.trim();
|
||||
payloadType = EncodingEnum.forContentType(payloadString);
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,8 @@ import ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage;
|
|||
import ca.uhn.fhir.jpa.subscription.module.cache.ActiveSubscription;
|
||||
import ca.uhn.fhir.jpa.subscription.module.cache.SubscriptionRegistry;
|
||||
import ca.uhn.fhir.jpa.subscription.module.matcher.ISubscriptionMatcher;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
@ -24,6 +26,7 @@ import org.springframework.stereotype.Service;
|
|||
|
||||
import java.util.Collection;
|
||||
|
||||
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
/*-
|
||||
|
@ -103,6 +106,7 @@ public class SubscriptionMatchingSubscriber implements MessageHandler {
|
|||
|
||||
private void doMatchActiveSubscriptionsAndDeliver(ResourceModifiedMessage theMsg) {
|
||||
IIdType resourceId = theMsg.getId(myFhirContext);
|
||||
Boolean isText = false;
|
||||
|
||||
Collection<ActiveSubscription> subscriptions = mySubscriptionRegistry.getAll();
|
||||
|
||||
|
@ -134,15 +138,21 @@ public class SubscriptionMatchingSubscriber implements MessageHandler {
|
|||
matchResult.isInMemory() ? "in-memory" : "by querying the repository");
|
||||
|
||||
IBaseResource payload = theMsg.getNewPayload(myFhirContext);
|
||||
CanonicalSubscription subscription = nextActiveSubscription.getSubscription();
|
||||
|
||||
EncodingEnum encoding = null;
|
||||
if (subscription.getPayloadString() != null && !subscription.getPayloadString().isEmpty()) {
|
||||
encoding = EncodingEnum.forContentType(subscription.getPayloadString());
|
||||
isText = subscription.getPayloadString().equals(Constants.CT_TEXT);
|
||||
}
|
||||
encoding = defaultIfNull(encoding, EncodingEnum.JSON);
|
||||
|
||||
ResourceDeliveryMessage deliveryMsg = new ResourceDeliveryMessage();
|
||||
deliveryMsg.setPayload(myFhirContext, payload);
|
||||
deliveryMsg.setSubscription(nextActiveSubscription.getSubscription());
|
||||
|
||||
deliveryMsg.setPayload(myFhirContext, payload, encoding);
|
||||
deliveryMsg.setSubscription(subscription);
|
||||
deliveryMsg.setOperationType(theMsg.getOperationType());
|
||||
deliveryMsg.copyAdditionalPropertiesFrom(theMsg);
|
||||
if (payload == null) {
|
||||
deliveryMsg.setPayloadId(theMsg.getId(myFhirContext));
|
||||
}
|
||||
|
||||
// Interceptor call: SUBSCRIPTION_RESOURCE_MATCHED
|
||||
HookParams params = new HookParams()
|
||||
|
|
|
@ -20,10 +20,12 @@ package ca.uhn.fhir.jpa.subscription.module.subscriber.email;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
||||
import ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription;
|
||||
import ca.uhn.fhir.jpa.subscription.module.subscriber.BaseSubscriptionDeliverySubscriber;
|
||||
import ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -43,6 +45,8 @@ public class SubscriptionDeliveringEmailSubscriber extends BaseSubscriptionDeliv
|
|||
|
||||
@Autowired
|
||||
private ModelConfig myModelConfig;
|
||||
@Autowired
|
||||
private FhirContext myCtx;
|
||||
|
||||
private IEmailSender myEmailSender;
|
||||
|
||||
|
@ -66,13 +70,21 @@ public class SubscriptionDeliveringEmailSubscriber extends BaseSubscriptionDeliv
|
|||
}
|
||||
}
|
||||
|
||||
String payload = "";
|
||||
if (isNotBlank(subscription.getPayloadString())) {
|
||||
EncodingEnum encoding = EncodingEnum.forContentType(subscription.getPayloadString());
|
||||
if (encoding != null) {
|
||||
payload = theMessage.getPayloadString();
|
||||
}
|
||||
}
|
||||
|
||||
String from = processEmailAddressUri(defaultString(subscription.getEmailDetails().getFrom(), myModelConfig.getEmailFromAddress()));
|
||||
String subjectTemplate = defaultString(subscription.getEmailDetails().getSubjectTemplate(), provideDefaultSubjectTemplate());
|
||||
|
||||
EmailDetails details = new EmailDetails();
|
||||
details.setTo(destinationAddresses);
|
||||
details.setFrom(from);
|
||||
details.setBodyTemplate(subscription.getPayloadString());
|
||||
details.setBodyTemplate(payload);
|
||||
details.setSubjectTemplate(subjectTemplate);
|
||||
details.setSubscription(subscription.getIdElement(myFhirContext));
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ public class TestSubscriptionDstu3Config extends SubscriptionDstu3Config {
|
|||
|
||||
@Bean
|
||||
@Primary
|
||||
public ISubscriptionProvider subsriptionProvider() {
|
||||
public ISubscriptionProvider subscriptionProvider() {
|
||||
return new MockFhirClientSubscriptionProvider();
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue