From 0de0b88aa0f67c2b1158c23eb10210a3e65e8cc7 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Sun, 9 Aug 2020 07:06:11 -0400 Subject: [PATCH 01/21] Fix TestpageOverlay XSS Vulnerability (#2027) * Resolve XSS vulnerability * Add changelog --- example-projects/hapi-fhir-jaxrs-sse/pom.xml | 2 +- .../main/java/ca/uhn/fhir/util/UrlUtil.java | 28 +++++++++-- ...26-resolve-xss-testpage-vulnerability.yaml | 8 ++++ .../java/ca/uhn/fhir/to/BaseController.java | 47 ++++--------------- .../main/java/ca/uhn/fhir/to/Controller.java | 39 ++++++++------- .../uhn/fhir/jpa/test/FhirTesterConfig.java | 4 +- 6 files changed, 64 insertions(+), 64 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_1_0/2026-resolve-xss-testpage-vulnerability.yaml diff --git a/example-projects/hapi-fhir-jaxrs-sse/pom.xml b/example-projects/hapi-fhir-jaxrs-sse/pom.xml index d52505386c2..fe3b1066360 100644 --- a/example-projects/hapi-fhir-jaxrs-sse/pom.xml +++ b/example-projects/hapi-fhir-jaxrs-sse/pom.xml @@ -79,7 +79,7 @@ com.google.guava guava - 21.0 + 24.1.1 com.google.inject.extensions diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/UrlUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/UrlUtil.java index ecea91e02cc..56ceb01e010 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/UrlUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/UrlUtil.java @@ -16,10 +16,16 @@ import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLDecoder; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.Map.Entry; +import java.util.StringTokenizer; -import static org.apache.commons.lang3.StringUtils.*; +import static org.apache.commons.lang3.StringUtils.defaultIfBlank; +import static org.apache.commons.lang3.StringUtils.defaultString; +import static org.apache.commons.lang3.StringUtils.isBlank; /* * #%L @@ -370,7 +376,7 @@ public class UrlUtil { /** * This method specifically HTML-encodes the " and * < characters in order to prevent injection attacks. - * + *

* The following characters are escaped: *

- * */ public static String sanitizeUrlPart(CharSequence theString) { if (theString == null) { @@ -432,6 +437,21 @@ public class UrlUtil { return theString.toString(); } + /** + * Applies the same logic as {@link #sanitizeUrlPart(CharSequence)} but against an array, returning an array with the + * same strings as the input but with sanitization applied + */ + public static String[] sanitizeUrlPart(String[] theParameterValues) { + String[] retVal = null; + if (theParameterValues != null) { + retVal = new String[theParameterValues.length]; + for (int i = 0; i < theParameterValues.length; i++) { + retVal[i] = sanitizeUrlPart(theParameterValues[i]); + } + } + return retVal; + } + private static Map toQueryStringMap(HashMap> map) { HashMap retVal = new HashMap<>(); for (Entry> nextEntry : map.entrySet()) { diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_1_0/2026-resolve-xss-testpage-vulnerability.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_1_0/2026-resolve-xss-testpage-vulnerability.yaml new file mode 100644 index 00000000000..717b82c474d --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_1_0/2026-resolve-xss-testpage-vulnerability.yaml @@ -0,0 +1,8 @@ +--- +type: security +issue: 2026 +title: "An XSS vulnerability was reported in the HAPI FHIR Testpage Overlay module. Thanks to Will Davison of NCC Group (Manchester UK) for disclosing this vulnerability. + + Users of the HAPI FHIR Testpage Overlay can use a specially crafted URL to exploit an XSS vulnerability in this module, allowing arbitrary JavaScript to be executed in the user's browser. The impact of this vulnerability is believed to be low, as this module is intended for testing and not believed to be widely used for any production purposes. Nonetheless, we recommend all users of the affected module upgrade immediately. + + A complete audit of the affected codebase has been completed in order to detect and resolve any similar issues." diff --git a/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/BaseController.java b/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/BaseController.java index 3d855284db4..39647b449c8 100644 --- a/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/BaseController.java +++ b/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/BaseController.java @@ -41,6 +41,7 @@ import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.util.*; +import static ca.uhn.fhir.util.UrlUtil.sanitizeUrlPart; import static org.apache.commons.lang3.StringUtils.defaultString; public class BaseController { @@ -49,7 +50,7 @@ public class BaseController { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseController.class); @Autowired protected TesterConfig myConfig; - private Map myContexts = new HashMap(); + private final Map myContexts = new HashMap(); private List myFilterHeaders; @Autowired private ITemplateEngine myTemplateEngine; @@ -78,19 +79,6 @@ public class BaseController { return loadAndAddConf(theServletRequest, theRequest, theModel); } - private Header[] applyHeaderFilters(Header[] theAllHeaders) { - if (myFilterHeaders == null || myFilterHeaders.isEmpty()) { - return theAllHeaders; - } - ArrayList
retVal = new ArrayList
(); - for (Header next : theAllHeaders) { - if (!myFilterHeaders.contains(next.getName().toLowerCase())) { - retVal.add(next); - } - } - return retVal.toArray(new Header[retVal.size()]); - } - private Header[] applyHeaderFilters(Map> theAllHeaders) { ArrayList
retVal = new ArrayList
(); for (String nextKey : theAllHeaders.keySet()) { @@ -274,7 +262,7 @@ public class BaseController { } protected RuntimeResourceDefinition getResourceType(HomeRequest theRequest, HttpServletRequest theReq) throws ServletException { - String resourceName = StringUtils.defaultString(theReq.getParameter(PARAM_RESOURCE)); + String resourceName = sanitizeUrlPart(defaultString(theReq.getParameter(PARAM_RESOURCE))); RuntimeResourceDefinition def = getContext(theRequest).getResourceDefinition(resourceName); if (def == null) { throw new ServletException("Invalid resourceName: " + resourceName); @@ -317,7 +305,7 @@ public class BaseController { ca.uhn.fhir.model.dstu2.resource.Conformance conformance; try { - conformance = (ca.uhn.fhir.model.dstu2.resource.Conformance) client.fetchConformance().ofType(Conformance.class).execute(); + conformance = client.fetchConformance().ofType(Conformance.class).execute(); } catch (Exception e) { ourLog.warn("Failed to load conformance statement, error was: {}", e.toString()); theModel.put("errorMsg", toDisplayError("Failed to load conformance statement, error was: " + e.toString(), e)); @@ -326,7 +314,7 @@ public class BaseController { theModel.put("jsonEncodedConf", getContext(theRequest).newJsonParser().encodeResourceToString(conformance)); - Map resourceCounts = new HashMap(); + Map resourceCounts = new HashMap<>(); long total = 0; for (ca.uhn.fhir.model.dstu2.resource.Conformance.Rest nextRest : conformance.getRest()) { for (ca.uhn.fhir.model.dstu2.resource.Conformance.RestResource nextResource : nextRest.getResource()) { @@ -385,7 +373,7 @@ public class BaseController { theModel.put("jsonEncodedConf", getContext(theRequest).newJsonParser().encodeResourceToString(capabilityStatement)); - Map resourceCounts = new HashMap(); + Map resourceCounts = new HashMap<>(); long total = 0; for (CapabilityStatementRestComponent nextRest : capabilityStatement.getRest()) { @@ -446,7 +434,7 @@ public class BaseController { theModel.put("jsonEncodedConf", getContext(theRequest).newJsonParser().encodeResourceToString(capabilityStatement)); - Map resourceCounts = new HashMap(); + Map resourceCounts = new HashMap<>(); long total = 0; for (org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestComponent nextRest : capabilityStatement.getRest()) { @@ -507,7 +495,7 @@ public class BaseController { theModel.put("jsonEncodedConf", getContext(theRequest).newJsonParser().encodeResourceToString(capabilityStatement)); - Map resourceCounts = new HashMap(); + Map resourceCounts = new HashMap<>(); long total = 0; for (org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestComponent nextRest : capabilityStatement.getRest()) { @@ -614,25 +602,6 @@ public class BaseController { protected void processAndAddLastClientInvocation(GenericClient theClient, ResultType theResultType, ModelMap theModelMap, long theLatency, String outcomeDescription, CaptureInterceptor theInterceptor, HomeRequest theRequest) { try { -// ApacheHttpRequest lastRequest = theInterceptor.getLastRequest(); -// HttpResponse lastResponse = theInterceptor.getLastResponse(); -// String requestBody = null; -// String requestUrl = lastRequest != null ? lastRequest.getApacheRequest().getURI().toASCIIString() : null; -// String action = lastRequest != null ? lastRequest.getApacheRequest().getMethod() : null; -// String resultStatus = lastResponse != null ? lastResponse.getStatusLine().toString() : null; -// String resultBody = StringUtils.defaultString(theInterceptor.getLastResponseBody()); -// -// if (lastRequest instanceof HttpEntityEnclosingRequest) { -// HttpEntity entity = ((HttpEntityEnclosingRequest) lastRequest).getEntity(); -// if (entity.isRepeatable()) { -// requestBody = IOUtils.toString(entity.getContent()); -// } -// } -// -// ContentType ct = lastResponse != null ? ContentType.get(lastResponse.getEntity()) : null; -// String mimeType = ct != null ? ct.getMimeType() : null; - - IHttpRequest lastRequest = theInterceptor.getLastRequest(); IHttpResponse lastResponse = theInterceptor.getLastResponse(); String requestBody = null; diff --git a/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/Controller.java b/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/Controller.java index c0af8e3ea98..3a8031f07ca 100644 --- a/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/Controller.java +++ b/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/Controller.java @@ -26,6 +26,7 @@ import ca.uhn.fhir.rest.gclient.TokenClientParam; import ca.uhn.fhir.to.model.HomeRequest; import ca.uhn.fhir.to.model.ResourceRequest; import ca.uhn.fhir.to.model.TransactionRequest; +import ca.uhn.fhir.util.UrlUtil; import com.google.gson.stream.JsonWriter; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.dstu3.model.CapabilityStatement; @@ -49,6 +50,7 @@ import java.util.Collections; import java.util.List; import java.util.TreeSet; +import static ca.uhn.fhir.util.UrlUtil.sanitizeUrlPart; import static org.apache.commons.lang3.StringUtils.defaultIfEmpty; import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.isBlank; @@ -128,7 +130,7 @@ public class Controller extends BaseController { return "resource"; } - String id = StringUtils.defaultString(theServletRequest.getParameter("resource-delete-id")); + String id = sanitizeUrlPart(defaultString(theServletRequest.getParameter("resource-delete-id"))); if (StringUtils.isBlank(id)) { populateModelForResource(theServletRequest, theRequest, theModel); theModel.put("errorMsg", toDisplayError("No ID specified", null)); @@ -184,7 +186,7 @@ public class Controller extends BaseController { FhirContext context = getContext(theRequest); GenericClient client = theRequest.newClient(theReq, context, myConfig, interceptor); - String url = defaultString(theReq.getParameter("page-url")); + String url = sanitizeUrlPart(defaultString(theReq.getParameter("page-url"))); if (myConfig.isRefuseToFetchThirdPartyUrls()) { if (!url.startsWith(theModel.get("base").toString())) { ourLog.warn(logPrefix(theModel) + "Refusing to load page URL: {}", url); @@ -230,7 +232,7 @@ public class Controller extends BaseController { theModel.put("errorMsg", toDisplayError(e.toString(), e)); return "resource"; } - String id = StringUtils.defaultString(theServletRequest.getParameter("id")); + String id = sanitizeUrlPart(defaultString(theServletRequest.getParameter("id"))); if (StringUtils.isBlank(id)) { populateModelForResource(theServletRequest, theRequest, theModel); theModel.put("errorMsg", toDisplayError("No ID specified", null)); @@ -238,7 +240,7 @@ public class Controller extends BaseController { } ResultType returnsResource = ResultType.RESOURCE; - String versionId = StringUtils.defaultString(theServletRequest.getParameter("vid")); + String versionId = sanitizeUrlPart(defaultString(theServletRequest.getParameter("vid"))); String outcomeDescription; if (StringUtils.isBlank(versionId)) { versionId = null; @@ -353,7 +355,7 @@ public class Controller extends BaseController { return "resource"; } clientCodeJsonWriter.name("resource"); - clientCodeJsonWriter.value(theServletRequest.getParameter("resource")); + clientCodeJsonWriter.value(sanitizeUrlPart(theServletRequest.getParameter("resource"))); } else { query = search.forAllResources(); clientCodeJsonWriter.name("resource"); @@ -394,7 +396,7 @@ public class Controller extends BaseController { clientCodeJsonWriter.name("includes"); clientCodeJsonWriter.beginArray(); - String[] incValues = theServletRequest.getParameterValues(Constants.PARAM_INCLUDE); + String[] incValues = sanitizeUrlPart(theServletRequest.getParameterValues(Constants.PARAM_INCLUDE)); if (incValues != null) { for (String next : incValues) { if (isNotBlank(next)) { @@ -407,7 +409,7 @@ public class Controller extends BaseController { clientCodeJsonWriter.name("revincludes"); clientCodeJsonWriter.beginArray(); - String[] revIncValues = theServletRequest.getParameterValues(Constants.PARAM_REVINCLUDE); + String[] revIncValues = sanitizeUrlPart(theServletRequest.getParameterValues(Constants.PARAM_REVINCLUDE)); if (revIncValues != null) { for (String next : revIncValues) { if (isNotBlank(next)) { @@ -418,7 +420,7 @@ public class Controller extends BaseController { } clientCodeJsonWriter.endArray(); - String limit = theServletRequest.getParameter("resource-search-limit"); + String limit = sanitizeUrlPart(theServletRequest.getParameter("resource-search-limit")); if (isNotBlank(limit)) { if (!limit.matches("[0-9]+")) { populateModelForResource(theServletRequest, theRequest, theModel); @@ -434,13 +436,13 @@ public class Controller extends BaseController { clientCodeJsonWriter.nullValue(); } - String[] sort = theServletRequest.getParameterValues("sort_by"); + String[] sort = sanitizeUrlPart(theServletRequest.getParameterValues("sort_by")); if (sort != null) { for (String next : sort) { if (isBlank(next)) { continue; } - String direction = theServletRequest.getParameter("sort_direction"); + String direction = sanitizeUrlPart(theServletRequest.getParameter("sort_direction")); if ("asc".equals(direction)) { query.sort().ascending(new StringClientParam(next)); } else if ("desc".equals(direction)) { @@ -545,6 +547,7 @@ public class Controller extends BaseController { type = def.getImplementingClass(); } + // Don't sanitize this param, it's a raw resource body and may well be XML String body = validate ? theReq.getParameter("resource-validate-body") : theReq.getParameter("resource-create-body"); if (isBlank(body)) { theModel.put("errorMsg", toDisplayError("No message body specified", null)); @@ -583,7 +586,7 @@ public class Controller extends BaseController { outcomeDescription = "Validate Resource"; client.validate().resource(resource).prettyPrint().execute(); } else { - String id = theReq.getParameter("resource-create-id"); + String id = sanitizeUrlPart(theReq.getParameter("resource-create-id")); if ("update".equals(theMethod)) { outcomeDescription = "Update Resource"; client.update(id, resource); @@ -626,17 +629,17 @@ public class Controller extends BaseController { if ("history-type".equals(theMethod)) { RuntimeResourceDefinition def = getContext(theRequest).getResourceDefinition(theRequest.getResource()); type = def.getImplementingClass(); - id = StringUtils.defaultString(theReq.getParameter("resource-history-id")); + id = sanitizeUrlPart(defaultString(theReq.getParameter("resource-history-id"))); } DateTimeDt since = null; - String sinceStr = theReq.getParameter("since"); + String sinceStr = sanitizeUrlPart(theReq.getParameter("since")); if (isNotBlank(sinceStr)) { since = new DateTimeDt(sinceStr); } Integer limit = null; - String limitStr = theReq.getParameter("limit"); + String limitStr = sanitizeUrlPart(theReq.getParameter("limit")); if (isNotBlank(limitStr)) { limit = Integer.parseInt(limitStr); } @@ -811,17 +814,17 @@ public class Controller extends BaseController { } private boolean handleSearchParam(String paramIdxString, HttpServletRequest theReq, IQuery theQuery, JsonWriter theClientCodeJsonWriter) throws IOException { - String nextName = theReq.getParameter("param." + paramIdxString + ".name"); + String nextName = sanitizeUrlPart(theReq.getParameter("param." + paramIdxString + ".name")); if (isBlank(nextName)) { return false; } - String nextQualifier = StringUtils.defaultString(theReq.getParameter("param." + paramIdxString + ".qualifier")); - String nextType = theReq.getParameter("param." + paramIdxString + ".type"); + String nextQualifier = sanitizeUrlPart(defaultString(theReq.getParameter("param." + paramIdxString + ".qualifier"))); + String nextType = sanitizeUrlPart(theReq.getParameter("param." + paramIdxString + ".type")); List parts = new ArrayList(); for (int i = 0; i < 5; i++) { - parts.add(defaultString(theReq.getParameter("param." + paramIdxString + "." + i))); + parts.add(sanitizeUrlPart(defaultString(theReq.getParameter("param." + paramIdxString + "." + i)))); } List values; diff --git a/hapi-fhir-testpage-overlay/src/test/java/ca/uhn/fhir/jpa/test/FhirTesterConfig.java b/hapi-fhir-testpage-overlay/src/test/java/ca/uhn/fhir/jpa/test/FhirTesterConfig.java index 035ff2caa16..8df0cd3f1f1 100644 --- a/hapi-fhir-testpage-overlay/src/test/java/ca/uhn/fhir/jpa/test/FhirTesterConfig.java +++ b/hapi-fhir-testpage-overlay/src/test/java/ca/uhn/fhir/jpa/test/FhirTesterConfig.java @@ -48,13 +48,13 @@ public class FhirTesterConfig { .addServer() .withId("hapi") .withFhirVersion(FhirVersionEnum.DSTU2) - .withBaseUrl("http://fhirtest.uhn.ca/baseDstu2") + .withBaseUrl("http://hapi.fhir.org/baseDstu2") .withName("Public HAPI Test Server") .allowsApiKey() .addServer() .withId("home3") .withFhirVersion(FhirVersionEnum.DSTU3) - .withBaseUrl("http://fhirtest.uhn.ca/baseDstu3") + .withBaseUrl("http://hapi.fhir.org/baseDstu3") .withName("Public HAPI Test Server (STU3)") .addServer() .withId("home") From 8e0023c385fec3f0478f681ebcc06a3358dcb403 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Sun, 9 Aug 2020 07:09:37 -0400 Subject: [PATCH 02/21] Disable referential integrity for some paths (#2025) * Disable referential integrity for some paths * Add changelog * Remove unneeded file --- ...-referential-integrity-for-some-paths.yaml | 5 + .../built_in_server_interceptors.md | 16 ++ .../jpa/api/model/DeleteConflictList.java | 15 ++ .../ca/uhn/fhir/jpa/config/BaseConfig.java | 15 ++ .../jpa/delete/DeleteConflictService.java | 35 +++-- .../CascadingDeleteInterceptor.java | 9 +- ...rentialIntegrityForDeletesInterceptor.java | 107 +++++++++++++ .../dao/r4/FhirResourceDaoR4DeleteTest.java | 10 +- .../CascadingDeleteInterceptorTest.java} | 51 +++++-- ...ialIntegrityForDeletesInterceptorTest.java | 141 ++++++++++++++++++ 10 files changed, 374 insertions(+), 30 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_1_0/2025-disable-referential-integrity-for-some-paths.yaml create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/OverridePathBasedReferentialIntegrityForDeletesInterceptor.java rename hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/{provider/r4/CascadingDeleteInterceptorR4Test.java => interceptor/CascadingDeleteInterceptorTest.java} (82%) create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/interceptor/OverridePathBasedReferentialIntegrityForDeletesInterceptorTest.java diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_1_0/2025-disable-referential-integrity-for-some-paths.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_1_0/2025-disable-referential-integrity-for-some-paths.yaml new file mode 100644 index 00000000000..e70f23e29b8 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_1_0/2025-disable-referential-integrity-for-some-paths.yaml @@ -0,0 +1,5 @@ +--- +type: add +issue: 2025 +title: "A new interceptor has been added for the JPA server that selectively allows resource deletions to proceed even if + there are valid references to the candidate for deletion from other resources that are not being deleted." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/interceptors/built_in_server_interceptors.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/interceptors/built_in_server_interceptors.md index 09b2bd28452..46fe776ec67 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/interceptors/built_in_server_interceptors.md +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/interceptors/built_in_server_interceptors.md @@ -180,8 +180,24 @@ The ResponseSizeCapturingInterceptor can be used to capture the number of charac # JPA Server: Allow Cascading Deletes +* [CascadingDeleteInterceptor JavaDoc](/apidocs/hapi-fhir-jpaserver-base/ca/uhn/fhir/jpa/interceptor/CascadingDeleteInterceptor.html) +* [CascadingDeleteInterceptor Source](https://github.com/jamesagnew/hapi-fhir/blob/master/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/CascadingDeleteInterceptor.java) + The CascadingDeleteInterceptor allows clients to request deletes be cascaded to other resources that contain incoming references. See [Cascading Deletes](/docs/server_jpa/configuration.html#cascading-deletes) for more information. + + + +# JPA Server: Disable Referential Integrity for Some Paths + +* [OverridePathBasedReferentialIntegrityForDeletesInterceptor JavaDoc](/apidocs/hapi-fhir-jpaserver-base/ca/uhn/fhir/jpa/interceptor/OverridePathBasedReferentialIntegrityForDeletesInterceptor.html) +* [OverridePathBasedReferentialIntegrityForDeletesInterceptor Source](https://github.com/jamesagnew/hapi-fhir/blob/master/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/OverridePathBasedReferentialIntegrityForDeletesInterceptor.java) + +The OverridePathBasedReferentialIntegrityForDeletesInterceptor can be registered and configured to allow resources to be deleted even if other resources have outgoing references to the deleted resource. While it is generally a bad idea to allow deletion of resources that are referred to from other resources, there are circumstances where it is desirable. For example, if you have Provenance or AuditEvent resources that refer to a Patient resource that was created in error, you might want to alow the Patient to be deleted while leaving the Provenance and AuditEvent resources intact (including the now-invalid outgoing references to that Patient). + +This interceptor uses FHIRPath expressions to indicate the resource paths that should not have referential integrity applied to them. For example, if this interceptor is configured with a path of `AuditEvent.agent.who`, a Patient resource would be allowed to be deleted even if one or more AuditEvents had references in that path to the given Patient (unless other resources also had references to the Patient). + + # JPA Server: Retry on Version Conflicts The UserRequestRetryVersionConflictsInterceptor allows clients to request that the server avoid version conflicts (HTTP 409) when two concurrent client requests attempt to modify the same resource. See [Version Conflicts](/docs/server_jpa/configuration.html#retry-on-version-conflict) for more information. diff --git a/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/model/DeleteConflictList.java b/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/model/DeleteConflictList.java index 74e40219307..84c2030a82d 100644 --- a/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/model/DeleteConflictList.java +++ b/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/model/DeleteConflictList.java @@ -34,6 +34,7 @@ import java.util.function.Predicate; public class DeleteConflictList implements Iterable { private final List myList = new ArrayList<>(); private final Set myResourceIdsMarkedForDeletion; + private final Set myResourceIdsToIgnoreConflict; private int myRemoveModCount; /** @@ -41,6 +42,7 @@ public class DeleteConflictList implements Iterable { */ public DeleteConflictList() { myResourceIdsMarkedForDeletion = new HashSet<>(); + myResourceIdsToIgnoreConflict = new HashSet<>(); } /** @@ -49,6 +51,7 @@ public class DeleteConflictList implements Iterable { */ public DeleteConflictList(DeleteConflictList theParentList) { myResourceIdsMarkedForDeletion = theParentList.myResourceIdsMarkedForDeletion; + myResourceIdsToIgnoreConflict = theParentList.myResourceIdsToIgnoreConflict; } @@ -64,6 +67,18 @@ public class DeleteConflictList implements Iterable { myResourceIdsMarkedForDeletion.add(theIdType.toUnqualifiedVersionless().getValue()); } + public boolean isResourceIdToIgnoreConflict(IIdType theIdType) { + Validate.notNull(theIdType); + Validate.notBlank(theIdType.toUnqualifiedVersionless().getValue()); + return myResourceIdsToIgnoreConflict.contains(theIdType.toUnqualifiedVersionless().getValue()); + } + + public void setResourceIdToIgnoreConflict(IIdType theIdType) { + Validate.notNull(theIdType); + Validate.notBlank(theIdType.toUnqualifiedVersionless().getValue()); + myResourceIdsToIgnoreConflict.add(theIdType.toUnqualifiedVersionless().getValue()); + } + public void add(DeleteConflict theDeleteConflict) { myList.add(theDeleteConflict); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseConfig.java index 36a02f2c08e..d6fc005a7c3 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseConfig.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseConfig.java @@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.config; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.i18n.HapiLocalizer; +import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; import ca.uhn.fhir.interceptor.api.IInterceptorService; import ca.uhn.fhir.interceptor.executor.InterceptorService; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; @@ -24,7 +25,9 @@ import ca.uhn.fhir.jpa.dao.index.DaoResourceLinkResolver; import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService; import ca.uhn.fhir.jpa.entity.Search; import ca.uhn.fhir.jpa.graphql.JpaStorageServices; +import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor; import ca.uhn.fhir.jpa.interceptor.JpaConsentContextServices; +import ca.uhn.fhir.jpa.interceptor.OverridePathBasedReferentialIntegrityForDeletesInterceptor; import ca.uhn.fhir.jpa.model.sched.ISchedulerService; import ca.uhn.fhir.jpa.packages.IHapiPackageCacheManager; import ca.uhn.fhir.jpa.packages.IPackageInstallerSvc; @@ -167,6 +170,12 @@ public abstract class BaseConfig { return new BatchJobSubmitterImpl(); } + @Lazy + @Bean + public CascadingDeleteInterceptor cascadingDeleteInterceptor(FhirContext theFhirContext, DaoRegistry theDaoRegistry, IInterceptorBroadcaster theInterceptorBroadcaster) { + return new CascadingDeleteInterceptor(theFhirContext, theDaoRegistry, theInterceptorBroadcaster); + } + /** * This method should be overridden to provide an actual completed * bean, but it provides a partially completed entity manager @@ -295,6 +304,12 @@ public abstract class BaseConfig { return new HapiFhirHibernateJpaDialect(fhirContext().getLocalizer()); } + @Bean + @Lazy + public OverridePathBasedReferentialIntegrityForDeletesInterceptor overridePathBasedReferentialIntegrityForDeletesInterceptor() { + return new OverridePathBasedReferentialIntegrityForDeletesInterceptor(); + } + @Bean public IRequestPartitionHelperSvc requestPartitionHelperService() { return new RequestPartitionHelperSvc(); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/delete/DeleteConflictService.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/delete/DeleteConflictService.java index f7f1fdbc687..2cbbaafa165 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/delete/DeleteConflictService.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/delete/DeleteConflictService.java @@ -44,27 +44,25 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import org.springframework.transaction.support.TransactionSynchronizationManager; import java.util.List; @Service public class DeleteConflictService { - private static final Logger ourLog = LoggerFactory.getLogger(DeleteConflictService.class); public static final int FIRST_QUERY_RESULT_COUNT = 1; + private static final Logger ourLog = LoggerFactory.getLogger(DeleteConflictService.class); public static int MAX_RETRY_ATTEMPTS = 10; public static String MAX_RETRY_ATTEMPTS_EXCEEDED_MSG = "Requested delete operation stopped before all conflicts were handled. May need to increase the configured Maximum Delete Conflict Query Count."; - + @Autowired + protected IResourceLinkDao myResourceLinkDao; + @Autowired + protected IInterceptorBroadcaster myInterceptorBroadcaster; @Autowired DeleteConflictFinderService myDeleteConflictFinderService; @Autowired DaoConfig myDaoConfig; @Autowired - protected IResourceLinkDao myResourceLinkDao; - @Autowired private FhirContext myFhirContext; - @Autowired - protected IInterceptorBroadcaster myInterceptorBroadcaster; public int validateOkToDelete(DeleteConflictList theDeleteConflicts, ResourceTable theEntity, boolean theForValidate, RequestDetails theRequest, TransactionDetails theTransactionDetails) { @@ -87,9 +85,9 @@ public class DeleteConflictService { ++retryCount; } theDeleteConflicts.addAll(newConflicts); - if(retryCount >= MAX_RETRY_ATTEMPTS && !theDeleteConflicts.isEmpty()) { + if (retryCount >= MAX_RETRY_ATTEMPTS && !theDeleteConflicts.isEmpty()) { IBaseOperationOutcome oo = OperationOutcomeUtil.newInstance(myFhirContext); - OperationOutcomeUtil.addIssue(myFhirContext, oo, BaseHapiFhirDao.OO_SEVERITY_ERROR, MAX_RETRY_ATTEMPTS_EXCEEDED_MSG,null, "processing"); + OperationOutcomeUtil.addIssue(myFhirContext, oo, BaseHapiFhirDao.OO_SEVERITY_ERROR, MAX_RETRY_ATTEMPTS_EXCEEDED_MSG, null, "processing"); throw new ResourceVersionConflictException(MAX_RETRY_ATTEMPTS_EXCEEDED_MSG, oo); } return retryCount; @@ -123,7 +121,7 @@ public class DeleteConflictService { .add(RequestDetails.class, theRequest) .addIfMatchesType(ServletRequestDetails.class, theRequest) .add(TransactionDetails.class, theTransactionDetails); - return (DeleteConflictOutcome)JpaInterceptorBroadcaster.doCallHooksAndReturnObject(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PRESTORAGE_DELETE_CONFLICTS, hooks); + return (DeleteConflictOutcome) JpaInterceptorBroadcaster.doCallHooksAndReturnObject(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PRESTORAGE_DELETE_CONFLICTS, hooks); } private void addConflictsToList(DeleteConflictList theDeleteConflicts, ResourceTable theEntity, List theResultList) { @@ -142,26 +140,33 @@ public class DeleteConflictService { } public static void validateDeleteConflictsEmptyOrThrowException(FhirContext theFhirContext, DeleteConflictList theDeleteConflicts) { - if (theDeleteConflicts.isEmpty()) { - return; - } - - IBaseOperationOutcome oo = OperationOutcomeUtil.newInstance(theFhirContext); + IBaseOperationOutcome oo = null; String firstMsg = null; for (DeleteConflict next : theDeleteConflicts) { + + if (theDeleteConflicts.isResourceIdToIgnoreConflict(next.getTargetId())) { + continue; + } + String msg = "Unable to delete " + next.getTargetId().toUnqualifiedVersionless().getValue() + " because at least one resource has a reference to this resource. First reference found was resource " + next.getSourceId().toUnqualifiedVersionless().getValue() + " in path " + next.getSourcePath(); + if (firstMsg == null) { firstMsg = msg; + oo = OperationOutcomeUtil.newInstance(theFhirContext); } OperationOutcomeUtil.addIssue(theFhirContext, oo, BaseHapiFhirDao.OO_SEVERITY_ERROR, msg, null, "processing"); } + if (firstMsg == null) { + return; + } + throw new ResourceVersionConflictException(firstMsg, oo); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/CascadingDeleteInterceptor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/CascadingDeleteInterceptor.java index fc8aa12388c..a2875454e06 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/CascadingDeleteInterceptor.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/CascadingDeleteInterceptor.java @@ -71,6 +71,13 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; @Interceptor public class CascadingDeleteInterceptor { + /* + * We keep the orders for the various handlers of {@link Pointcut#STORAGE_PRESTORAGE_DELETE_CONFLICTS} in one place + * so it's easy to compare them + */ + public static final int OVERRIDE_PATH_BASED_REF_INTEGRITY_INTERCEPTOR_ORDER = 0; + public static final int CASCADING_DELETE_INTERCEPTOR_ORDER = 1; + private static final Logger ourLog = LoggerFactory.getLogger(CascadingDeleteInterceptor.class); private static final String CASCADED_DELETES_KEY = CascadingDeleteInterceptor.class.getName() + "_CASCADED_DELETES_KEY"; private static final String CASCADED_DELETES_FAILED_KEY = CascadingDeleteInterceptor.class.getName() + "_CASCADED_DELETES_FAILED_KEY"; @@ -94,7 +101,7 @@ public class CascadingDeleteInterceptor { myFhirContext = theFhirContext; } - @Hook(Pointcut.STORAGE_PRESTORAGE_DELETE_CONFLICTS) + @Hook(value = Pointcut.STORAGE_PRESTORAGE_DELETE_CONFLICTS, order = CASCADING_DELETE_INTERCEPTOR_ORDER) public DeleteConflictOutcome handleDeleteConflicts(DeleteConflictList theConflictList, RequestDetails theRequest, TransactionDetails theTransactionDetails) { ourLog.debug("Have delete conflicts: {}", theConflictList); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/OverridePathBasedReferentialIntegrityForDeletesInterceptor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/OverridePathBasedReferentialIntegrityForDeletesInterceptor.java new file mode 100644 index 00000000000..e89261cb1b7 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/OverridePathBasedReferentialIntegrityForDeletesInterceptor.java @@ -0,0 +1,107 @@ +package ca.uhn.fhir.jpa.interceptor; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.fhirpath.IFhirPath; +import ca.uhn.fhir.interceptor.api.Hook; +import ca.uhn.fhir.interceptor.api.Interceptor; +import ca.uhn.fhir.interceptor.api.Pointcut; +import ca.uhn.fhir.jpa.api.dao.DaoRegistry; +import ca.uhn.fhir.jpa.api.model.DeleteConflict; +import ca.uhn.fhir.jpa.api.model.DeleteConflictList; +import ca.uhn.fhir.model.primitive.IdDt; +import org.hl7.fhir.instance.model.api.IBaseReference; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +/** + * This JPA interceptor can be configured with a collection of FHIRPath expressions, and will disable + * referential integrity for target resources at those paths. + *

+ * For example, suppose this interceptor is configured with a path of AuditEvent.entity.what, + * and an AuditEvent resource exists in the repository that has a reference in that path to resource + * Patient/123. Normally this reference would prevent the Patient resource from being deleted unless + * the AuditEvent was first deleted as well (or a cascading delete was used). + * With this interceptor in place, the Patient resource could be deleted, and the AuditEvent would remain intact. + *

+ */ +@Interceptor +public class OverridePathBasedReferentialIntegrityForDeletesInterceptor { + + private static final Logger ourLog = LoggerFactory.getLogger(OverridePathBasedReferentialIntegrityForDeletesInterceptor.class); + private final Set myPaths = new HashSet<>(); + + @Autowired + private FhirContext myFhirContext; + @Autowired + private DaoRegistry myDaoRegistry; + + /** + * Constructor + */ + public OverridePathBasedReferentialIntegrityForDeletesInterceptor() { + super(); + } + + /** + * Adds a FHIRPath expression indicating a resource path that should be ignored when considering referential + * integrity for deletes. + * + * @param thePath The FHIRPath expression, e.g. AuditEvent.agent.who + */ + public void addPath(String thePath) { + getPaths().add(thePath); + } + + /** + * Remove all paths registered to this interceptor + */ + public void clearPaths() { + getPaths().clear(); + } + + /** + * Returns the paths that will be considered by this interceptor + * + * @see #addPath(String) + */ + private Set getPaths() { + return myPaths; + } + + /** + * Interceptor hook method. Do not invoke directly. + */ + @Hook(value = Pointcut.STORAGE_PRESTORAGE_DELETE_CONFLICTS, order = CascadingDeleteInterceptor.OVERRIDE_PATH_BASED_REF_INTEGRITY_INTERCEPTOR_ORDER) + public void handleDeleteConflicts(DeleteConflictList theDeleteConflictList) { + for (DeleteConflict nextConflict : theDeleteConflictList) { + ourLog.info("Ignoring referential integrity deleting {} - Referred to from {} at path {}", nextConflict.getTargetId(), nextConflict.getSourceId(), nextConflict.getSourcePath()); + + IdDt sourceId = nextConflict.getSourceId(); + IdDt targetId = nextConflict.getTargetId(); + String targetIdValue = targetId.toVersionless().getValue(); + + IBaseResource sourceResource = myDaoRegistry.getResourceDao(sourceId.getResourceType()).read(sourceId); + + IFhirPath fhirPath = myFhirContext.newFhirPath(); + for (String nextPath : myPaths) { + List selections = fhirPath.evaluate(sourceResource, nextPath, IBaseReference.class); + for (IBaseReference nextSelection : selections) { + String selectionTargetValue = nextSelection.getReferenceElement().toVersionless().getValue(); + if (Objects.equals(targetIdValue, selectionTargetValue)) { + theDeleteConflictList.setResourceIdToIgnoreConflict(nextConflict.getTargetId()); + break; + } + } + + } + + } + } +} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4DeleteTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4DeleteTest.java index 97cc8d26b42..1cac6951212 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4DeleteTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4DeleteTest.java @@ -99,7 +99,7 @@ public class FhirResourceDaoR4DeleteTest extends BaseJpaR4Test { @Test - public void testDeleteCircularReferenceInTransaction() throws IOException { + public void testDeleteCircularReferenceInTransaction() { // Create two resources with a circular reference Organization org1 = new Organization(); @@ -221,4 +221,12 @@ public class FhirResourceDaoR4DeleteTest extends BaseJpaR4Test { } + @Test + public void testDeleteIgnoreReferentialIntegrityForPaths() { + + + + } + + } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/CascadingDeleteInterceptorR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/interceptor/CascadingDeleteInterceptorTest.java similarity index 82% rename from hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/CascadingDeleteInterceptorR4Test.java rename to hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/interceptor/CascadingDeleteInterceptorTest.java index 231d1ebc2de..afa85cc0522 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/CascadingDeleteInterceptorR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/interceptor/CascadingDeleteInterceptorTest.java @@ -1,8 +1,8 @@ -package ca.uhn.fhir.jpa.provider.r4; +package ca.uhn.fhir.jpa.interceptor; import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; -import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor; +import ca.uhn.fhir.jpa.provider.r4.BaseResourceProviderR4Test; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; @@ -20,7 +20,6 @@ import org.hl7.fhir.r4.model.Organization; import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.Reference; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -31,9 +30,9 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; -public class CascadingDeleteInterceptorR4Test extends BaseResourceProviderR4Test { +public class CascadingDeleteInterceptorTest extends BaseResourceProviderR4Test { - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CascadingDeleteInterceptorR4Test.class); + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CascadingDeleteInterceptorTest.class); private IIdType myDiagnosticReportId; @Autowired @@ -42,18 +41,13 @@ public class CascadingDeleteInterceptorR4Test extends BaseResourceProviderR4Test private IInterceptorBroadcaster myInterceptorBroadcaster; private IIdType myPatientId; + @Autowired private CascadingDeleteInterceptor myDeleteInterceptor; private IIdType myObservationId; private IIdType myConditionId; private IIdType myEncounterId; - - @Override - @BeforeEach - public void before() throws Exception { - super.before(); - - myDeleteInterceptor = new CascadingDeleteInterceptor(myFhirCtx, myDaoRegistry, myInterceptorBroadcaster); - } + @Autowired + private OverridePathBasedReferentialIntegrityForDeletesInterceptor myOverridePathBasedReferentialIntegrityForDeletesInterceptor; @Override @AfterEach @@ -161,6 +155,37 @@ public class CascadingDeleteInterceptorR4Test extends BaseResourceProviderR4Test } } + @Test + public void testDeleteCascadingWithOverridePathBasedReferentialIntegrityForDeletesInterceptorAlsoRegistered() throws IOException { + ourRestServer.getInterceptorService().registerInterceptor(myOverridePathBasedReferentialIntegrityForDeletesInterceptor); + try { + + createResources(); + + ourRestServer.getInterceptorService().registerInterceptor(myDeleteInterceptor); + + HttpDelete delete = new HttpDelete(ourServerBase + "/" + myPatientId.getValue() + "?" + Constants.PARAMETER_CASCADE_DELETE + "=" + Constants.CASCADE_DELETE + "&_pretty=true"); + delete.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_JSON_NEW); + try (CloseableHttpResponse response = ourHttpClient.execute(delete)) { + assertEquals(200, response.getStatusLine().getStatusCode()); + String deleteResponse = IOUtils.toString(response.getEntity().getContent(), Charsets.UTF_8); + ourLog.info("Response: {}", deleteResponse); + assertThat(deleteResponse, containsString("Cascaded delete to ")); + } + + try { + ourLog.info("Reading {}", myPatientId); + myClient.read().resource(Patient.class).withId(myPatientId).execute(); + fail(); + } catch (ResourceGoneException e) { + // good + } + + } finally { + ourRestServer.getInterceptorService().unregisterInterceptor(myOverridePathBasedReferentialIntegrityForDeletesInterceptor); + } + } + @Test public void testDeleteCascadingWithCircularReference() throws IOException { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/interceptor/OverridePathBasedReferentialIntegrityForDeletesInterceptorTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/interceptor/OverridePathBasedReferentialIntegrityForDeletesInterceptorTest.java new file mode 100644 index 00000000000..14363d30a1a --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/interceptor/OverridePathBasedReferentialIntegrityForDeletesInterceptorTest.java @@ -0,0 +1,141 @@ +package ca.uhn.fhir.jpa.interceptor; + +import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test; +import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.rest.param.ReferenceParam; +import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; +import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; +import org.hl7.fhir.r4.model.AuditEvent; +import org.hl7.fhir.r4.model.IdType; +import org.hl7.fhir.r4.model.Patient; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +public class OverridePathBasedReferentialIntegrityForDeletesInterceptorTest extends BaseJpaR4Test { + + @Autowired + private OverridePathBasedReferentialIntegrityForDeletesInterceptor mySvc; + + @Autowired + private CascadingDeleteInterceptor myCascadingDeleteInterceptor; + + @AfterEach + public void after() { + myInterceptorRegistry.unregisterInterceptor(mySvc); + mySvc.clearPaths(); + } + + @Test + public void testDeleteBlockedIfNoInterceptorInPlace() { + Patient patient = new Patient(); + patient.setId("P"); + patient.setActive(true); + myPatientDao.update(patient); + + AuditEvent audit = new AuditEvent(); + audit.setId("A"); + audit.addAgent().getWho().setReference("Patient/P"); + myAuditEventDao.update(audit); + + try { + myPatientDao.delete(new IdType("Patient/P")); + fail(); + } catch (ResourceVersionConflictException e) { + // good + } + } + + + @Test + public void testAllowDelete() { + mySvc.addPath("AuditEvent.agent.who"); + myInterceptorRegistry.registerInterceptor(mySvc); + + Patient patient = new Patient(); + patient.setId("P"); + patient.setActive(true); + myPatientDao.update(patient); + + AuditEvent audit = new AuditEvent(); + audit.setId("A"); + audit.addAgent().getWho().setReference("Patient/P"); + myAuditEventDao.update(audit); + + // Delete should proceed + myPatientDao.delete(new IdType("Patient/P")); + + // Make sure we're deleted + try { + myPatientDao.read(new IdType("Patient/P")); + fail(); + } catch (ResourceGoneException e) { + // good + } + + // Search should still work + IBundleProvider searchOutcome = myAuditEventDao.search(SearchParameterMap.newSynchronous(AuditEvent.SP_AGENT, new ReferenceParam("Patient/P"))); + assertEquals(1, searchOutcome.size()); + + + } + + @Test + public void testWrongPath() { + mySvc.addPath("AuditEvent.identifier"); + mySvc.addPath("Patient.agent.who"); + myInterceptorRegistry.registerInterceptor(mySvc); + + Patient patient = new Patient(); + patient.setId("P"); + patient.setActive(true); + myPatientDao.update(patient); + + AuditEvent audit = new AuditEvent(); + audit.setId("A"); + audit.addAgent().getWho().setReference("Patient/P"); + myAuditEventDao.update(audit); + + // Delete should proceed + try { + myPatientDao.delete(new IdType("Patient/P")); + fail(); + } catch (ResourceVersionConflictException e) { + // good + } + + + } + + @Test + public void testCombineWithCascadeDeleteInterceptor() { + try { + myInterceptorRegistry.registerInterceptor(myCascadingDeleteInterceptor); + + mySvc.addPath("AuditEvent.agent.who"); + myInterceptorRegistry.registerInterceptor(mySvc); + + Patient patient = new Patient(); + patient.setId("P"); + patient.setActive(true); + myPatientDao.update(patient); + + AuditEvent audit = new AuditEvent(); + audit.setId("A"); + audit.addAgent().getWho().setReference("Patient/P"); + myAuditEventDao.update(audit); + + // Delete should proceed + myPatientDao.delete(new IdType("Patient/P")); + + } finally { + myInterceptorRegistry.unregisterInterceptor(myCascadingDeleteInterceptor); + } + + } + +} From 7f693526271a928e1eb6324e7c041e07b2150ee3 Mon Sep 17 00:00:00 2001 From: Jonathan Leitschuh Date: Sun, 9 Aug 2020 13:04:16 -0400 Subject: [PATCH 03/21] Use HTTPS instead of HTTP to resolve dependencies (#1708) This fixes a security vulnerability in this project where the `pom.xml` files were configuring Maven to resolve dependencies over HTTP instead of HTTPS. Signed-off-by: Jonathan Leitschuh From 49f4f3ef629bfe9afee75810777e12f34f30c73e Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Sun, 9 Aug 2020 13:23:45 -0400 Subject: [PATCH 04/21] Add license headers --- ...rentialIntegrityForDeletesInterceptor.java | 20 +++++++++++++++++++ .../empi/svc/EmpiResourceFilteringSvc.java | 20 +++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/OverridePathBasedReferentialIntegrityForDeletesInterceptor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/OverridePathBasedReferentialIntegrityForDeletesInterceptor.java index e89261cb1b7..4c3242bcc4f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/OverridePathBasedReferentialIntegrityForDeletesInterceptor.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/OverridePathBasedReferentialIntegrityForDeletesInterceptor.java @@ -1,5 +1,25 @@ package ca.uhn.fhir.jpa.interceptor; +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2020 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.FhirContext; import ca.uhn.fhir.fhirpath.IFhirPath; import ca.uhn.fhir.interceptor.api.Hook; diff --git a/hapi-fhir-jpaserver-empi/src/main/java/ca/uhn/fhir/jpa/empi/svc/EmpiResourceFilteringSvc.java b/hapi-fhir-jpaserver-empi/src/main/java/ca/uhn/fhir/jpa/empi/svc/EmpiResourceFilteringSvc.java index f1b425ebc03..eddf6d8e439 100644 --- a/hapi-fhir-jpaserver-empi/src/main/java/ca/uhn/fhir/jpa/empi/svc/EmpiResourceFilteringSvc.java +++ b/hapi-fhir-jpaserver-empi/src/main/java/ca/uhn/fhir/jpa/empi/svc/EmpiResourceFilteringSvc.java @@ -1,5 +1,25 @@ package ca.uhn.fhir.jpa.empi.svc; +/*- + * #%L + * HAPI FHIR JPA Server - Enterprise Master Patient Index + * %% + * Copyright (C) 2014 - 2020 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.FhirContext; import ca.uhn.fhir.empi.api.IEmpiSettings; import ca.uhn.fhir.empi.log.Logs; From 8f6d08dd58d9da6dcc62583741451be97ef03b3d Mon Sep 17 00:00:00 2001 From: James Agnew Date: Sun, 9 Aug 2020 14:14:28 -0400 Subject: [PATCH 05/21] Fix #2022 - Invalidate caches on expunge (#2029) * Fix #2022 - Invalidate caches on expunge * Rename changelog file --- .../uhn/fhir/jpa/demo/FhirServerConfig.java | 4 +- .../fhir/jpa/demo/FhirServerConfigDstu3.java | 7 +- .../ca/uhn/fhir/jpa/demo/JpaServerDemo.java | 4 + .../2022-invalidate-caches-on-expunge.yaml | 6 ++ .../dao/expunge/ResourceExpungeService.java | 21 ++++ .../fhir/jpa/dao/index/IdHelperService.java | 47 +++++---- .../r4/BinaryAccessProviderR4Test.java | 73 ++++++++++++++ .../fhir/jpa/provider/r4/ExpungeR4Test.java | 99 +++++++++++++++++-- 8 files changed, 228 insertions(+), 33 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_1_0/2022-invalidate-caches-on-expunge.yaml diff --git a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java index 1fdd38b4c75..d402c32bf43 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java +++ b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java @@ -33,6 +33,7 @@ import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.Primary; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.transaction.annotation.EnableTransactionManagement; @@ -106,8 +107,9 @@ public class FhirServerConfig extends BaseJavaConfigDstu2 { return retVal; } + @Primary @Bean - public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) { + public JpaTransactionManager hapiTransactionManager(EntityManagerFactory entityManagerFactory) { JpaTransactionManager retVal = new JpaTransactionManager(); retVal.setEntityManagerFactory(entityManagerFactory); return retVal; diff --git a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigDstu3.java b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigDstu3.java index 27c4da9cfed..f96ce78a509 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigDstu3.java +++ b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigDstu3.java @@ -20,6 +20,8 @@ package ca.uhn.fhir.jpa.demo; * #L% */ +import ca.uhn.fhir.jpa.binstore.DatabaseBlobBinaryStorageSvcImpl; +import ca.uhn.fhir.jpa.binstore.IBinaryStorageSvc; import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu3; import ca.uhn.fhir.jpa.util.SubscriptionsRequireManualActivationInterceptorDstu3; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; @@ -31,6 +33,7 @@ import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.Primary; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.transaction.annotation.EnableTransactionManagement; @@ -97,10 +100,12 @@ public class FhirServerConfigDstu3 extends BaseJavaConfigDstu3 { return retVal; } + @Primary @Bean - public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) { + public JpaTransactionManager hapiTransactionManager(EntityManagerFactory entityManagerFactory) { JpaTransactionManager retVal = new JpaTransactionManager(); retVal.setEntityManagerFactory(entityManagerFactory); return retVal; } + } diff --git a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java index 41da2296758..bb5a2674bd0 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java +++ b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java @@ -26,6 +26,8 @@ import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; import ca.uhn.fhir.jpa.api.config.DaoConfig; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao; +import ca.uhn.fhir.jpa.binstore.BinaryAccessProvider; +import ca.uhn.fhir.jpa.binstore.BinaryStorageInterceptor; import ca.uhn.fhir.jpa.config.BaseConfig; import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor; import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2; @@ -180,6 +182,8 @@ public class JpaServerDemo extends RestfulServer { getInterceptorService().registerInterceptor(new ResponseHighlighterInterceptor()); + registerProvider(myAppCtx.getBean(BinaryAccessProvider.class)); + } } diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_1_0/2022-invalidate-caches-on-expunge.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_1_0/2022-invalidate-caches-on-expunge.yaml new file mode 100644 index 00000000000..692ace07dc4 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_1_0/2022-invalidate-caches-on-expunge.yaml @@ -0,0 +1,6 @@ +--- +type: fix +issue: 2022 +title: When performing a resource $expunge in the JPA server, in-memory caches caused issues if a + forced ID was reused quickly enough (as can be the case in some testing scenarios). Thanks to + GitHub user @janvdpol for reporting!" diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ResourceExpungeService.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ResourceExpungeService.java index 44b1148fa81..23c1fa34316 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ResourceExpungeService.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ResourceExpungeService.java @@ -46,6 +46,7 @@ import ca.uhn.fhir.jpa.model.entity.ForcedId; import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster; +import ca.uhn.fhir.jpa.util.MemoryCacheService; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; @@ -60,7 +61,11 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.data.domain.SliceImpl; import org.springframework.stereotype.Service; +import org.springframework.transaction.TransactionManager; import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.TransactionSynchronization; +import org.springframework.transaction.support.TransactionSynchronizationAdapter; +import org.springframework.transaction.support.TransactionSynchronizationManager; import java.util.Collections; import java.util.List; @@ -108,6 +113,8 @@ public class ResourceExpungeService implements IResourceExpungeService { private ISearchParamPresentDao mySearchParamPresentDao; @Autowired private DaoConfig myDaoConfig; + @Autowired + private MemoryCacheService myMemoryCacheService; @Override @Transactional @@ -158,6 +165,20 @@ public class ResourceExpungeService implements IResourceExpungeService { return; } } + + /* + * Once this transaction is committed, we will invalidate all memory caches + * in order to avoid any caches having references to things that no longer + * exist. This is a pretty brute-force way of addressing this, and could probably + * be optimized, but expunge is hopefully not frequently called on busy servers + * so it shouldn't be too big a deal. + */ + TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization(){ + @Override + public void afterCommit() { + myMemoryCacheService.invalidateAllCaches(); + } + }); } private void expungeHistoricalVersion(RequestDetails theRequestDetails, Long theNextVersionId, AtomicInteger theRemainingCount) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/IdHelperService.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/IdHelperService.java index 8ff45e38dba..55c1c9482e9 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/IdHelperService.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/IdHelperService.java @@ -100,6 +100,8 @@ public class IdHelperService { private IInterceptorBroadcaster myInterceptorBroadcaster; @Autowired private FhirContext myFhirCtx; + @Autowired + private MemoryCacheService myMemoryCacheService; public void delete(ForcedId forcedId) { myForcedIdDao.deleteByPid(forcedId.getId()); @@ -123,9 +125,6 @@ public class IdHelperService { return matches.iterator().next(); } - @Autowired - private MemoryCacheService myMemoryCacheService; - /** * Given a resource type and ID, determines the internal persistent ID for the resource. * @@ -389,7 +388,7 @@ public class IdHelperService { lookup .stream() .map(t -> new ResourceLookup((String) t[0], (Long) t[1], (Date) t[2])) - .forEach(t->{ + .forEach(t -> { theTarget.add(t); if (!myDaoConfig.isDeleteEnabled()) { String nextKey = Long.toString(t.getResourceId()); @@ -432,19 +431,6 @@ public class IdHelperService { return retVal; } - public static boolean isValidPid(IIdType theId) { - if (theId == null) { - return false; - } - - String idPart = theId.getIdPart(); - return isValidPid(idPart); - } - - public static boolean isValidPid(String theIdPart) { - return StringUtils.isNumeric(theIdPart); - } - @Nullable public Long getPidOrNull(IBaseResource theResource) { IAnyResource anyResource = (IAnyResource) theResource; @@ -481,11 +467,24 @@ public class IdHelperService { return theIds.stream().collect(Collectors.toMap(this::getPidOrThrowException, Function.identity())); } - public IIdType resourceIdFromPidOrThrowException(Long thePid) { - Optional optionalResource = myResourceTableDao.findById(thePid); - if (!optionalResource.isPresent()) { - throw new ResourceNotFoundException("Requested resource not found"); - } - return optionalResource.get().getIdDt().toVersionless(); - } + public IIdType resourceIdFromPidOrThrowException(Long thePid) { + Optional optionalResource = myResourceTableDao.findById(thePid); + if (!optionalResource.isPresent()) { + throw new ResourceNotFoundException("Requested resource not found"); + } + return optionalResource.get().getIdDt().toVersionless(); + } + + public static boolean isValidPid(IIdType theId) { + if (theId == null) { + return false; + } + + String idPart = theId.getIdPart(); + return isValidPid(idPart); + } + + public static boolean isValidPid(String theIdPart) { + return StringUtils.isNumeric(theIdPart); + } } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/BinaryAccessProviderR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/BinaryAccessProviderR4Test.java index f815d4cad34..a3832ebdb74 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/BinaryAccessProviderR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/BinaryAccessProviderR4Test.java @@ -413,6 +413,8 @@ public class BinaryAccessProviderR4Test extends BaseResourceProviderR4Test { } + + /** * Stores a binary large enough that it should live in binary storage */ @@ -469,6 +471,77 @@ public class BinaryAccessProviderR4Test extends BaseResourceProviderR4Test { } + + @Test + public void testWriteLargeBinaryToDocumentReference() throws IOException { + byte[] bytes = new byte[134696]; + for (int i = 0; i < bytes.length; i++) { + bytes[i] = (byte) (((float)Byte.MAX_VALUE) * Math.random()); + } + + DocumentReference dr = new DocumentReference(); + dr.addContent().getAttachment() + .setContentType("application/pdf") + .setSize(12345) + .setTitle("hello") + .setCreationElement(new DateTimeType("2002")); + IIdType id = myClient.create().resource(dr).execute().getId().toUnqualifiedVersionless(); + + IAnonymousInterceptor interceptor = mock(IAnonymousInterceptor.class); + myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.STORAGE_PRESHOW_RESOURCES, interceptor); + myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.STORAGE_PRESTORAGE_RESOURCE_CREATED, interceptor); + myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.STORAGE_PRESTORAGE_RESOURCE_UPDATED, interceptor); + + // Write using the operation + + String path = ourServerBase + + "/DocumentReference/" + id.getIdPart() + "/" + + JpaConstants.OPERATION_BINARY_ACCESS_WRITE + + "?path=DocumentReference.content.attachment"; + HttpPost post = new HttpPost(path); + post.setEntity(new ByteArrayEntity(bytes, ContentType.IMAGE_JPEG)); + post.addHeader("Accept", "application/fhir+json; _pretty=true"); + String attachmentId; + try (CloseableHttpResponse resp = ourHttpClient.execute(post)) { + assertEquals(200, resp.getStatusLine().getStatusCode()); + assertThat(resp.getEntity().getContentType().getValue(), containsString("application/fhir+json")); + + String response = IOUtils.toString(resp.getEntity().getContent(), Constants.CHARSET_UTF8); + ourLog.info("Response: {}", response); + + DocumentReference target = myFhirCtx.newJsonParser().parseResource(DocumentReference.class, response); + + assertEquals(null, target.getContentFirstRep().getAttachment().getData()); + assertEquals("2", target.getMeta().getVersionId()); + attachmentId = target.getContentFirstRep().getAttachment().getDataElement().getExtensionString(HapiExtensions.EXT_EXTERNALIZED_BINARY_ID); + assertThat(attachmentId, matchesPattern("[a-zA-Z0-9]{100}")); + + } + + verify(interceptor, timeout(5000).times(1)).invoke(eq(Pointcut.STORAGE_PRESHOW_RESOURCES), any()); + verify(interceptor, timeout(5000).times(1)).invoke(eq(Pointcut.STORAGE_PRESTORAGE_RESOURCE_UPDATED), any()); + verifyNoMoreInteractions(interceptor); + + // Read it back using the operation + + path = ourServerBase + + "/DocumentReference/" + id.getIdPart() + "/" + + JpaConstants.OPERATION_BINARY_ACCESS_READ + + "?path=DocumentReference.content.attachment"; + HttpGet get = new HttpGet(path); + try (CloseableHttpResponse resp = ourHttpClient.execute(get)) { + + assertEquals(200, resp.getStatusLine().getStatusCode()); + assertEquals("image/jpeg", resp.getEntity().getContentType().getValue()); + assertEquals(bytes.length, resp.getEntity().getContentLength()); + + byte[] actualBytes = IOUtils.toByteArray(resp.getEntity().getContent()); + assertArrayEquals(bytes, actualBytes); + } + + } + + private IIdType createDocumentReference(boolean theSetData) { DocumentReference documentReference = new DocumentReference(); Attachment attachment = documentReference diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ExpungeR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ExpungeR4Test.java index ababc396273..2df8b6570a3 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ExpungeR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ExpungeR4Test.java @@ -8,11 +8,12 @@ import ca.uhn.fhir.jpa.dao.data.ISearchResultDao; import ca.uhn.fhir.jpa.search.PersistedJpaSearchFirstPageBundleProvider; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.rest.param.ReferenceParam; +import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.util.HapiExtensions; -import ca.uhn.fhir.util.TestUtil; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.BooleanType; @@ -23,7 +24,6 @@ import org.hl7.fhir.r4.model.Observation; import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.SearchParameter; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; @@ -36,7 +36,9 @@ import static org.awaitility.Awaitility.await; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.not; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; public class ExpungeR4Test extends BaseResourceProviderR4Test { @@ -410,16 +412,16 @@ public class ExpungeR4Test extends BaseResourceProviderR4Test { public void testExpungeEverythingWhereResourceInSearchResults() { createStandardPatients(); - await().until(()-> runInTransaction(() -> mySearchEntityDao.count() == 0)); - await().until(()-> runInTransaction(() -> mySearchResultDao.count() == 0)); + await().until(() -> runInTransaction(() -> mySearchEntityDao.count() == 0)); + await().until(() -> runInTransaction(() -> mySearchResultDao.count() == 0)); PersistedJpaSearchFirstPageBundleProvider search = (PersistedJpaSearchFirstPageBundleProvider) myPatientDao.search(new SearchParameterMap()); assertEquals(PersistedJpaSearchFirstPageBundleProvider.class, search.getClass()); assertEquals(2, search.size().intValue()); assertEquals(2, search.getResources(0, 2).size()); - await().until(()-> runInTransaction(() -> mySearchEntityDao.count() == 1)); - await().until(()-> runInTransaction(() -> mySearchResultDao.count() == 2)); + await().until(() -> runInTransaction(() -> mySearchEntityDao.count() == 1)); + await().until(() -> runInTransaction(() -> mySearchResultDao.count() == 2)); mySystemDao.expunge(new ExpungeOptions() .setExpungeEverything(true), null); @@ -465,4 +467,87 @@ public class ExpungeR4Test extends BaseResourceProviderR4Test { } + @Test + public void testExpungeForcedIdAndThenReuseIt() { + // Create with forced ID, and an Observation that links to it + Patient p = new Patient(); + p.setId("TEST"); + p.setActive(true); + p.addName().setFamily("FOO"); + myPatientDao.update(p); + + Observation obs = new Observation(); + obs.setId("OBS"); + obs.getSubject().setReference("Patient/TEST"); + myObservationDao.update(obs); + + // Make sure read works + p = myPatientDao.read(new IdType("Patient/TEST")); + assertTrue(p.getActive()); + + // Make sure search by ID works + IBundleProvider outcome = myPatientDao.search(SearchParameterMap.newSynchronous("_id", new TokenParam("Patient/TEST"))); + p = (Patient) outcome.getResources(0, 1).get(0); + assertTrue(p.getActive()); + + // Make sure search by Reference works + outcome = myObservationDao.search(SearchParameterMap.newSynchronous(Observation.SP_SUBJECT, new ReferenceParam("Patient/TEST"))); + obs = (Observation) outcome.getResources(0, 1).get(0); + assertEquals("OBS", obs.getIdElement().getIdPart()); + + // Delete and expunge + myObservationDao.delete(new IdType("Observation/OBS")); + myPatientDao.delete(new IdType("Patient/TEST")); + myPatientDao.expunge(new ExpungeOptions() + .setExpungeDeletedResources(true) + .setExpungeOldVersions(true), null); + myObservationDao.expunge(new ExpungeOptions() + .setExpungeDeletedResources(true) + .setExpungeOldVersions(true), null); + runInTransaction(() -> assertThat(myResourceTableDao.findAll(), empty())); + runInTransaction(() -> assertThat(myResourceHistoryTableDao.findAll(), empty())); + runInTransaction(() -> assertThat(myForcedIdDao.findAll(), empty())); + + // Create again with the same forced ID + p = new Patient(); + p.setId("TEST"); + p.setActive(true); + p.addName().setFamily("FOO"); + myPatientDao.update(p); + + obs = new Observation(); + obs.setId("OBS"); + obs.getSubject().setReference("Patient/TEST"); + myObservationDao.update(obs); + + // Make sure read works + p = myPatientDao.read(new IdType("Patient/TEST")); + assertTrue(p.getActive()); + + // Make sure search works + outcome = myPatientDao.search(SearchParameterMap.newSynchronous("_id", new TokenParam("Patient/TEST"))); + p = (Patient) outcome.getResources(0, 1).get(0); + assertTrue(p.getActive()); + + // Make sure search by Reference works + outcome = myObservationDao.search(SearchParameterMap.newSynchronous(Observation.SP_SUBJECT, new ReferenceParam("Patient/TEST"))); + obs = (Observation) outcome.getResources(0, 1).get(0); + assertEquals("OBS", obs.getIdElement().getIdPart()); + + // Delete and expunge + myObservationDao.delete(new IdType("Observation/OBS")); + myPatientDao.delete(new IdType("Patient/TEST")); + myPatientDao.expunge(new ExpungeOptions() + .setExpungeDeletedResources(true) + .setExpungeOldVersions(true), null); + myObservationDao.expunge(new ExpungeOptions() + .setExpungeDeletedResources(true) + .setExpungeOldVersions(true), null); + runInTransaction(() -> assertThat(myResourceTableDao.findAll(), empty())); + runInTransaction(() -> assertThat(myResourceHistoryTableDao.findAll(), empty())); + runInTransaction(() -> assertThat(myForcedIdDao.findAll(), empty())); + + } + + } From 096f310caec2c4d2a6c21754403385b48315b5fe Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Sun, 9 Aug 2020 14:22:53 -0400 Subject: [PATCH 06/21] Increase visibility --- ...rridePathBasedReferentialIntegrityForDeletesInterceptor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/OverridePathBasedReferentialIntegrityForDeletesInterceptor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/OverridePathBasedReferentialIntegrityForDeletesInterceptor.java index 4c3242bcc4f..e3e778cbb72 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/OverridePathBasedReferentialIntegrityForDeletesInterceptor.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/OverridePathBasedReferentialIntegrityForDeletesInterceptor.java @@ -91,7 +91,7 @@ public class OverridePathBasedReferentialIntegrityForDeletesInterceptor { * * @see #addPath(String) */ - private Set getPaths() { + public Set getPaths() { return myPaths; } From e61b39fc30d3ec191818f4353f66ad7d99b1c1f4 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Sun, 9 Aug 2020 19:30:59 -0400 Subject: [PATCH 07/21] Force a flush of the SearchParameter cache sooner when SPs change (#2030) * Force a flush of the SearchParameter cache sooner when SPs change * Add changelog * Test fixes --- .../ca/uhn/fhir/rest/param/NumberParam.java | 2 +- .../2023-force-sp-cache-flush-sooner.yaml | 7 ++ .../FhirResourceDaoSearchParameterDstu2.java | 1 + ...ceDaoDstu2SearchCustomSearchParamTest.java | 44 ++++++++ ...ourceDaoR4SearchCustomSearchParamTest.java | 45 ++++++++ .../registry/SearchParamRegistryImpl.java | 104 ++++++++++++------ .../SearchParameterCanonicalizer.java | 52 ++++----- .../registry/SearchParamRegistryImplTest.java | 3 +- 8 files changed, 197 insertions(+), 61 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_1_0/2023-force-sp-cache-flush-sooner.yaml diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/NumberParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/NumberParam.java index 3dc08ef5d1c..fc5003111e2 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/NumberParam.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/NumberParam.java @@ -56,7 +56,7 @@ public class NumberParam extends BaseParamWithPrefix implements IQu * Constructor * * @param theValue - * A string value, e.g. ">5.0" + * A string value, e.g. "gt5.0" */ public NumberParam(String theValue) { setValueAsQueryToken(null, null, null, theValue); diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_1_0/2023-force-sp-cache-flush-sooner.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_1_0/2023-force-sp-cache-flush-sooner.yaml new file mode 100644 index 00000000000..fc5968f2043 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_1_0/2023-force-sp-cache-flush-sooner.yaml @@ -0,0 +1,7 @@ +--- +type: add +issue: 2023 +title: "The JPA server maintains a cache of active SearchParameeter resources that can cause misleading results + if a SearchParameter is changed and other resources that would be indexed by the changed SearchParameter are updated + before the cache refreshes. A new interceptor has been added that should force a refresh sooner, especially on + non-clustered systems." diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoSearchParameterDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoSearchParameterDstu2.java index 4672d60f996..c0ae4a899aa 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoSearchParameterDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoSearchParameterDstu2.java @@ -51,6 +51,7 @@ public class FhirResourceDaoSearchParameterDstu2 extends BaseHapiFhirResourceDao protected void postPersist(ResourceTable theEntity, SearchParameter theResource) { super.postPersist(theEntity, theResource); markAffectedResources(theResource); + } @Override diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SearchCustomSearchParamTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SearchCustomSearchParamTest.java index bdeb2e803ca..8687c414f01 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SearchCustomSearchParamTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SearchCustomSearchParamTest.java @@ -1,5 +1,6 @@ package ca.uhn.fhir.jpa.dao.dstu2; +import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.api.config.DaoConfig; import ca.uhn.fhir.jpa.model.entity.ModelConfig; import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken; @@ -28,6 +29,8 @@ import ca.uhn.fhir.model.primitive.DecimalDt; import ca.uhn.fhir.model.primitive.IntegerDt; import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.rest.client.api.IGenericClient; +import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; import ca.uhn.fhir.rest.param.DateParam; import ca.uhn.fhir.rest.param.NumberParam; import ca.uhn.fhir.rest.param.ReferenceParam; @@ -232,6 +235,47 @@ public class FhirResourceDaoDstu2SearchCustomSearchParamTest extends BaseJpaDstu } + /** + * See #2023 + */ + @Test + public void testNumberSearchParam() { + SearchParameter numberParameter = new ca.uhn.fhir.model.dstu2.resource.SearchParameter(); + numberParameter.setId("future-appointment-count"); + numberParameter.setName("Future Appointment Count"); + numberParameter.setCode("future-appointment-count"); + numberParameter.setDescription("Count of future appointments for the patient"); + numberParameter.setUrl("http://integer"); + numberParameter.setStatus(ca.uhn.fhir.model.dstu2.valueset.ConformanceResourceStatusEnum.ACTIVE); + numberParameter.setBase(ca.uhn.fhir.model.dstu2.valueset.ResourceTypeEnum.PATIENT); + numberParameter.setType(ca.uhn.fhir.model.dstu2.valueset.SearchParamTypeEnum.NUMBER); + numberParameter.setXpathUsage(XPathUsageTypeEnum.NORMAL); + numberParameter.setXpath("Patient.extension('http://integer')"); + mySearchParameterDao.update(numberParameter); + + // This fires every 10 seconds + mySearchParamRegistry.refreshCacheIfNecessary(); + + Patient patient = new Patient(); + patient.setId("future-appointment-count-pt"); + patient.setActive(true); + patient.addUndeclaredExtension(false, "http://integer", new IntegerDt(1)); + myPatientDao.update(patient); + + IBundleProvider search; + + search = myPatientDao.search(SearchParameterMap.newSynchronous("future-appointment-count", new NumberParam(1))); + assertEquals(1, search.size()); + + search = myPatientDao.search(SearchParameterMap.newSynchronous("future-appointment-count", new NumberParam("gt0"))); + assertEquals(1, search.size()); + + search = myPatientDao.search(SearchParameterMap.newSynchronous("future-appointment-count", new NumberParam("lt0"))); + assertEquals(0, search.size()); + + } + + @Test public void testIncludeExtensionReferenceAsRecurse() { SearchParameter attendingSp = new SearchParameter(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchCustomSearchParamTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchCustomSearchParamTest.java index 7b935e91508..398f6aae40f 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchCustomSearchParamTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchCustomSearchParamTest.java @@ -1,5 +1,6 @@ package ca.uhn.fhir.jpa.dao.r4; +import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.interceptor.api.HookParams; import ca.uhn.fhir.interceptor.api.IAnonymousInterceptor; import ca.uhn.fhir.interceptor.api.Pointcut; @@ -9,7 +10,11 @@ import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken; import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.model.api.Include; +import ca.uhn.fhir.model.dstu2.valueset.XPathUsageTypeEnum; +import ca.uhn.fhir.model.primitive.IntegerDt; import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.rest.client.api.IGenericClient; +import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; import ca.uhn.fhir.rest.param.DateParam; import ca.uhn.fhir.rest.param.NumberParam; import ca.uhn.fhir.rest.param.ReferenceOrListParam; @@ -112,6 +117,46 @@ public class FhirResourceDaoR4SearchCustomSearchParamTest extends BaseJpaR4Test mySearchParamRegistry.forceRefresh(); } + /** + * See #2023 + */ + @Test + public void testNumberSearchParam() { + SearchParameter numberParameter = new SearchParameter(); + numberParameter.setId("future-appointment-count"); + numberParameter.setName("Future Appointment Count"); + numberParameter.setCode("future-appointment-count"); + numberParameter.setDescription("Count of future appointments for the patient"); + numberParameter.setUrl("http://integer"); + numberParameter.setStatus(Enumerations.PublicationStatus.ACTIVE); + numberParameter.addBase("Patient"); + numberParameter.setType(Enumerations.SearchParamType.NUMBER); + numberParameter.setExpression("Patient.extension('http://integer')"); + mySearchParameterDao.update(numberParameter); + + // This fires every 10 seconds + mySearchParamRegistry.refreshCacheIfNecessary(); + + Patient patient = new Patient(); + patient.setId("future-appointment-count-pt"); + patient.setActive(true); + patient.addExtension( "http://integer", new IntegerType(1)); + myPatientDao.update(patient); + + IBundleProvider search; + + search = myPatientDao.search(SearchParameterMap.newSynchronous("future-appointment-count", new NumberParam(1))); + assertEquals(1, search.size()); + + search = myPatientDao.search(SearchParameterMap.newSynchronous("future-appointment-count", new NumberParam("gt0"))); + assertEquals(1, search.size()); + + search = myPatientDao.search(SearchParameterMap.newSynchronous("future-appointment-count", new NumberParam("lt0"))); + assertEquals(0, search.size()); + + } + + /** * Draft search parameters should be ok even if they aren't completely valid */ diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParamRegistryImpl.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParamRegistryImpl.java index 46b41128f83..54844451975 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParamRegistryImpl.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParamRegistryImpl.java @@ -24,8 +24,10 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeSearchParam; import ca.uhn.fhir.context.phonetic.IPhoneticEncoder; +import ca.uhn.fhir.interceptor.api.Hook; import ca.uhn.fhir.interceptor.api.HookParams; -import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; +import ca.uhn.fhir.interceptor.api.IInterceptorService; +import ca.uhn.fhir.interceptor.api.Interceptor; import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.jpa.model.entity.ModelConfig; import ca.uhn.fhir.jpa.model.sched.HapiJob; @@ -38,8 +40,6 @@ import ca.uhn.fhir.jpa.searchparam.retry.Retrier; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; -import ca.uhn.fhir.util.DatatypeUtil; -import ca.uhn.fhir.util.HapiExtensions; import ca.uhn.fhir.util.SearchParameterUtil; import ca.uhn.fhir.util.StopWatch; import org.apache.commons.lang3.StringUtils; @@ -51,6 +51,7 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -89,7 +90,8 @@ public class SearchParamRegistryImpl implements ISearchParamRegistry { private volatile long myLastRefresh; @Autowired - private IInterceptorBroadcaster myInterceptorBroadcaster; + private IInterceptorService myInterceptorBroadcaster; + private RefreshSearchParameterCacheOnUpdate myInterceptor; @Override public RuntimeSearchParam getActiveSearchParam(String theResourceName, String theParamName) { @@ -236,8 +238,16 @@ public class SearchParamRegistryImpl implements ISearchParamRegistry { } @PostConstruct - public void postConstruct() { + public void start() { myBuiltInSearchParams = createBuiltInSearchParamMap(myFhirContext); + + myInterceptor = new RefreshSearchParameterCacheOnUpdate(); + myInterceptorBroadcaster.registerInterceptor(myInterceptor); + } + + @PreDestroy + public void stop() { + myInterceptorBroadcaster.unregisterInterceptor(myInterceptor); } public int doRefresh(long theRefreshInterval) { @@ -376,16 +386,6 @@ public class SearchParamRegistryImpl implements ISearchParamRegistry { mySchedulerService.scheduleLocalJob(10 * DateUtils.MILLIS_PER_SECOND, jobDetail); } - public static class Job implements HapiJob { - @Autowired - private ISearchParamRegistry myTarget; - - @Override - public void execute(JobExecutionContext theContext) { - myTarget.refreshCacheIfNecessary(); - } - } - @Override public boolean refreshCacheIfNecessary() { if (myActiveSearchParams == null || System.currentTimeMillis() - REFRESH_INTERVAL > myLastRefresh) { @@ -402,30 +402,12 @@ public class SearchParamRegistryImpl implements ISearchParamRegistry { return Collections.unmodifiableMap(myActiveSearchParams); } - public static Map> createBuiltInSearchParamMap(FhirContext theFhirContext) { - Map> resourceNameToSearchParams = new HashMap<>(); - - Set resourceNames = theFhirContext.getResourceTypes(); - - for (String resourceName : resourceNames) { - RuntimeResourceDefinition nextResDef = theFhirContext.getResourceDefinition(resourceName); - String nextResourceName = nextResDef.getName(); - HashMap nameToParam = new HashMap<>(); - resourceNameToSearchParams.put(nextResourceName, nameToParam); - - for (RuntimeSearchParam nextSp : nextResDef.getSearchParams()) { - nameToParam.put(nextSp.getName(), nextSp); - } - } - return Collections.unmodifiableMap(resourceNameToSearchParams); - } - /** * All SearchParameters with the name "phonetic" encode the normalized index value using this phonetic encoder. * * @since 5.1.0 */ - + @Override public void setPhoneticEncoder(IPhoneticEncoder thePhoneticEncoder) { myPhoneticEncoder = thePhoneticEncoder; @@ -446,4 +428,58 @@ public class SearchParamRegistryImpl implements ISearchParamRegistry { searchParam.setPhoneticEncoder(myPhoneticEncoder); } } + + @Interceptor + public class RefreshSearchParameterCacheOnUpdate { + + @Hook(Pointcut.STORAGE_PRECOMMIT_RESOURCE_CREATED) + public void created(IBaseResource theResource) { + handle(theResource); + } + + @Hook(Pointcut.STORAGE_PRECOMMIT_RESOURCE_DELETED) + public void deleted(IBaseResource theResource) { + handle(theResource); + } + + @Hook(Pointcut.STORAGE_PRECOMMIT_RESOURCE_UPDATED) + public void updated(IBaseResource theResource) { + handle(theResource); + } + + private void handle(IBaseResource theResource) { + if (theResource != null && myFhirContext.getResourceType(theResource).equals("SearchParameter")) { + requestRefresh(); + } + } + + } + + public static class Job implements HapiJob { + @Autowired + private ISearchParamRegistry myTarget; + + @Override + public void execute(JobExecutionContext theContext) { + myTarget.refreshCacheIfNecessary(); + } + } + + public static Map> createBuiltInSearchParamMap(FhirContext theFhirContext) { + Map> resourceNameToSearchParams = new HashMap<>(); + + Set resourceNames = theFhirContext.getResourceTypes(); + + for (String resourceName : resourceNames) { + RuntimeResourceDefinition nextResDef = theFhirContext.getResourceDefinition(resourceName); + String nextResourceName = nextResDef.getName(); + HashMap nameToParam = new HashMap<>(); + resourceNameToSearchParams.put(nextResourceName, nameToParam); + + for (RuntimeSearchParam nextSp : nextResDef.getSearchParams()) { + nameToParam.put(nextSp.getName(), nextSp); + } + } + return Collections.unmodifiableMap(resourceNameToSearchParams); + } } diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParameterCanonicalizer.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParameterCanonicalizer.java index 0ce4a925e79..87f8ec0bc7d 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParameterCanonicalizer.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParameterCanonicalizer.java @@ -96,31 +96,33 @@ public class SearchParameterCanonicalizer { String path = theNextSp.getXpath(); RestSearchParameterTypeEnum paramType = null; RuntimeSearchParam.RuntimeSearchParamStatusEnum status = null; - switch (theNextSp.getTypeElement().getValueAsEnum()) { - case COMPOSITE: - paramType = RestSearchParameterTypeEnum.COMPOSITE; - break; - case DATE_DATETIME: - paramType = RestSearchParameterTypeEnum.DATE; - break; - case NUMBER: - paramType = RestSearchParameterTypeEnum.NUMBER; - break; - case QUANTITY: - paramType = RestSearchParameterTypeEnum.QUANTITY; - break; - case REFERENCE: - paramType = RestSearchParameterTypeEnum.REFERENCE; - break; - case STRING: - paramType = RestSearchParameterTypeEnum.STRING; - break; - case TOKEN: - paramType = RestSearchParameterTypeEnum.TOKEN; - break; - case URI: - paramType = RestSearchParameterTypeEnum.URI; - break; + if (theNextSp.getTypeElement().getValueAsEnum() != null) { + switch (theNextSp.getTypeElement().getValueAsEnum()) { + case COMPOSITE: + paramType = RestSearchParameterTypeEnum.COMPOSITE; + break; + case DATE_DATETIME: + paramType = RestSearchParameterTypeEnum.DATE; + break; + case NUMBER: + paramType = RestSearchParameterTypeEnum.NUMBER; + break; + case QUANTITY: + paramType = RestSearchParameterTypeEnum.QUANTITY; + break; + case REFERENCE: + paramType = RestSearchParameterTypeEnum.REFERENCE; + break; + case STRING: + paramType = RestSearchParameterTypeEnum.STRING; + break; + case TOKEN: + paramType = RestSearchParameterTypeEnum.TOKEN; + break; + case URI: + paramType = RestSearchParameterTypeEnum.URI; + break; + } } if (theNextSp.getStatus() != null) { switch (theNextSp.getStatusElement().getValueAsEnum()) { diff --git a/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParamRegistryImplTest.java b/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParamRegistryImplTest.java index 43eab94aa59..789c6716570 100644 --- a/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParamRegistryImplTest.java +++ b/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParamRegistryImplTest.java @@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.searchparam.registry; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.RuntimeSearchParam; import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; +import ca.uhn.fhir.interceptor.api.IInterceptorService; import ca.uhn.fhir.jpa.model.entity.ModelConfig; import ca.uhn.fhir.jpa.model.sched.ISchedulerService; import ca.uhn.fhir.rest.server.SimpleBundleProvider; @@ -46,7 +47,7 @@ public class SearchParamRegistryImplTest { @MockBean private ModelConfig myModelConfig; @MockBean - private IInterceptorBroadcaster myInterceptorBroadcaster; + private IInterceptorService myInterceptorBroadcaster; @Configuration static class SpringConfig { From f0ed640e24a6d8041514580db98ad6f4ed6b5d97 Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Mon, 10 Aug 2020 09:05:04 -0400 Subject: [PATCH 08/21] Upgrade core library dependency version --- .../dao/r4/FhirResourceDaoR4ValidateTest.java | 2 +- .../fhir/r5/hapi/ctx/HapiWorkerContext.java | 18 ++++++++++++++- .../VersionSpecificWorkerContextWrapper.java | 23 +++++++++++++++++-- .../FhirInstanceValidatorDstu3Test.java | 5 +++- .../FhirInstanceValidatorR4Test.java | 4 ++-- .../FhirInstanceValidatorR5Test.java | 4 ++-- .../resources/bug872-ext-with-hl7-url.json | 2 +- pom.xml | 2 +- 8 files changed, 49 insertions(+), 11 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java index 732b0e451ae..1f98f4434ad 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java @@ -625,7 +625,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { " },\n" + " \"text\": {\n" + " \"status\": \"generated\",\n" + - " \"div\": \"
\"\n" + + " \"div\": \"
HELLO
\"\n" + " },\n" + " \"url\": \"https://foo/bb\",\n" + " \"name\": \"BBBehaviourType\",\n" + diff --git a/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/ctx/HapiWorkerContext.java b/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/ctx/HapiWorkerContext.java index 897de87cd86..3bbc52fb524 100644 --- a/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/ctx/HapiWorkerContext.java +++ b/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/ctx/HapiWorkerContext.java @@ -33,6 +33,7 @@ import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.r5.terminologies.ValueSetExpander; import org.hl7.fhir.r5.utils.IResourceValidator; import org.hl7.fhir.utilities.TranslationServices; +import org.hl7.fhir.utilities.cache.BasePackageCacheManager; import org.hl7.fhir.utilities.cache.NpmPackage; import org.hl7.fhir.utilities.i18n.I18nBase; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; @@ -171,6 +172,11 @@ public final class HapiWorkerContext extends I18nBase implements IWorkerContext return validateCode(theOptions, system, code, display, theVs); } + @Override + public void validateCodeBatch(ValidationOptions options, List codes, ValueSet vs) { + throw new UnsupportedOperationException(); + } + @Override public ValidationResult validateCode(ValidationOptions theOptions, String theSystem, String theCode, String theDisplay) { IValidationSupport.CodeValidationResult result = myValidationSupport.validateCode(new ValidationSupportContext(myValidationSupport), convertConceptValidationOptions(theOptions), theSystem, theCode, theDisplay, null); @@ -406,7 +412,17 @@ public final class HapiWorkerContext extends I18nBase implements IWorkerContext } @Override - public void loadFromPackage(NpmPackage pi, IContextResourceLoader loader, String[] types) throws FHIRException { + public int loadFromPackage(NpmPackage pi, IContextResourceLoader loader) throws FHIRException { + throw new UnsupportedOperationException(); + } + + @Override + public int loadFromPackage(NpmPackage pi, IContextResourceLoader loader, String[] types) throws FHIRException { + throw new UnsupportedOperationException(); + } + + @Override + public int loadFromPackageAndDependencies(NpmPackage pi, IContextResourceLoader loader, BasePackageCacheManager pcm) throws FHIRException { throw new UnsupportedOperationException(); } diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/VersionSpecificWorkerContextWrapper.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/VersionSpecificWorkerContextWrapper.java index 848ab9e1d5c..0d4ea9f735c 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/VersionSpecificWorkerContextWrapper.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/VersionSpecificWorkerContextWrapper.java @@ -29,6 +29,7 @@ import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.terminologies.ValueSetExpander; import org.hl7.fhir.r5.utils.IResourceValidator; import org.hl7.fhir.utilities.TranslationServices; +import org.hl7.fhir.utilities.cache.BasePackageCacheManager; import org.hl7.fhir.utilities.cache.NpmPackage; import org.hl7.fhir.utilities.i18n.I18nBase; import org.hl7.fhir.utilities.validation.ValidationMessage; @@ -56,8 +57,8 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo private static final FhirContext ourR5Context = FhirContext.forR5(); private final ValidationSupportContext myValidationSupportContext; private final IVersionTypeConverter myModelConverter; - private volatile List myAllStructures; private final LoadingCache myFetchResourceCache; + private volatile List myAllStructures; private org.hl7.fhir.r5.model.Parameters myExpansionProfile; public VersionSpecificWorkerContextWrapper(ValidationSupportContext theValidationSupportContext, IVersionTypeConverter theModelConverter) { @@ -122,8 +123,18 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo } @Override - public void loadFromPackage(NpmPackage pi, IContextResourceLoader loader, String[] types) throws FHIRException { + public int loadFromPackage(NpmPackage pi, IContextResourceLoader loader) throws FileNotFoundException, IOException, FHIRException { + throw new UnsupportedOperationException(); + } + @Override + public int loadFromPackage(NpmPackage pi, IContextResourceLoader loader, String[] types) throws FHIRException { + throw new UnsupportedOperationException(); + } + + @Override + public int loadFromPackageAndDependencies(NpmPackage pi, IContextResourceLoader loader, BasePackageCacheManager pcm) throws FileNotFoundException, IOException, FHIRException { + throw new UnsupportedOperationException(); } @Override @@ -529,6 +540,14 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo return doValidation(convertedVs, validationOptions, system, code, display); } + @Override + public void validateCodeBatch(ValidationOptions options, List codes, ValueSet vs) { + for (CodingValidationRequest next : codes) { + ValidationResult outcome = validateCode(options, next.getCoding(), vs); + next.setResult(outcome); + } + } + @Nonnull private ValidationResult doValidation(IBaseResource theValueSet, ConceptValidationOptions theValidationOptions, String theSystem, String theCode, String theDisplay) { IValidationSupport.CodeValidationResult result; diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidatorDstu3Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidatorDstu3Test.java index 26524f3f3dd..a9363d83c37 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidatorDstu3Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidatorDstu3Test.java @@ -643,6 +643,9 @@ public class FhirInstanceValidatorDstu3Test { ourLog.info("Skipping logical type: {}", next.getId()); continue; } + if (sd.getUrl().equals("http://hl7.org/fhir/StructureDefinition/Resource")) { + continue; + } } ourLog.info("Validating {}", next.getId()); @@ -762,7 +765,7 @@ public class FhirInstanceValidatorDstu3Test { Patient resource = loadResource("/dstu3/nl/nl-core-patient-01.json", Patient.class); ValidationResult results = myVal.validateWithResult(resource); List outcome = logResultsAndReturnNonInformationalOnes(results); - assertThat(outcome.toString(), containsString("The Coding provided is not in the value set http://decor.nictiz.nl/fhir/ValueSet/2.16.840.1.113883.2.4.3.11.60.40.2.20.5.1--20171231000000")); + assertThat(outcome.toString(), containsString("The Coding provided (urn:oid:2.16.840.1.113883.2.4.4.16.34#6030) is not in the value set http://decor.nictiz.nl/fhir/ValueSet/2.16.840.1.113883.2.4.3.11.60.40.2.20.5.1--20171231000000")); } private void loadNL() throws IOException { diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java index 72c38ab01c7..ab2e1e19192 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java @@ -826,7 +826,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest { " \"resourceType\":\"Patient\"," + " \"text\": {\n" + " \"status\": \"generated\",\n" + - " \"div\": \"
\"\n" + + " \"div\": \"
HELLO
\"\n" + " },\n" + " \"id\":\"123\"" + "}"; @@ -842,7 +842,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest { "\"resourceType\":\"Patient\"," + " \"text\": {\n" + " \"status\": \"generated\",\n" + - " \"div\": \"
\"\n" + + " \"div\": \"
HELLO
\"\n" + " },\n" + "\"id\":\"123\"," + "\"foo\":\"123\"" + diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/FhirInstanceValidatorR5Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/FhirInstanceValidatorR5Test.java index 23433a1018a..f35132feda5 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/FhirInstanceValidatorR5Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/FhirInstanceValidatorR5Test.java @@ -451,7 +451,7 @@ public class FhirInstanceValidatorR5Test { " \"resourceType\":\"Patient\"," + " \"text\": {\n" + " \"status\": \"generated\",\n" + - " \"div\": \"
\"\n" + + " \"div\": \"
HELLO
\"\n" + " },\n" + " \"id\":\"123\"" + "}"; @@ -467,7 +467,7 @@ public class FhirInstanceValidatorR5Test { "\"resourceType\":\"Patient\"," + " \"text\": {\n" + " \"status\": \"generated\",\n" + - " \"div\": \"
\"\n" + + " \"div\": \"
HELLO
\"\n" + " },\n" + "\"id\":\"123\"," + "\"foo\":\"123\"" + diff --git a/hapi-fhir-validation/src/test/resources/bug872-ext-with-hl7-url.json b/hapi-fhir-validation/src/test/resources/bug872-ext-with-hl7-url.json index f05cd34e26d..0aa5d2e6ebf 100644 --- a/hapi-fhir-validation/src/test/resources/bug872-ext-with-hl7-url.json +++ b/hapi-fhir-validation/src/test/resources/bug872-ext-with-hl7-url.json @@ -2,7 +2,7 @@ "resourceType": "Patient", "text": { "status": "generated", - "div": "
" + "div": "
HELLO
" }, "extension": [ { diff --git a/pom.xml b/pom.xml index ddc34694707..d9700f35eee 100644 --- a/pom.xml +++ b/pom.xml @@ -678,7 +678,7 @@ - 5.0.9 + 5.0.22 1.0.2 -Dfile.encoding=UTF-8 -Xmx2048m From 39eea88b47c44e3ce4d898a3ca7a0e91a596fd97 Mon Sep 17 00:00:00 2001 From: Jens Kristian Villadsen <46567685+jvitrifork@users.noreply.github.com> Date: Tue, 11 Aug 2020 11:25:36 +0200 Subject: [PATCH 09/21] Update interceptors.md (#2033) Added the mention of public requirement! --- .../ca/uhn/hapi/fhir/docs/interceptors/interceptors.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/interceptors/interceptors.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/interceptors/interceptors.md index 20b86b004ba..c44e517a534 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/interceptors/interceptors.md +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/interceptors/interceptors.md @@ -32,6 +32,8 @@ Creating your own interceptors is easy. Custom interceptor classes do not need t * The method may have any of the parameters specified for the given [Pointcut](/apidocs/hapi-fhir-base/ca/uhn/fhir/interceptor/api/Pointcut.html). +* The method must be public. + The following example shows a simple request counter interceptor. ```java From f3fbad63e71dc225389766ffe07695c12ee30285 Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Tue, 11 Aug 2020 08:30:27 -0400 Subject: [PATCH 10/21] Avoid a LOB error when expanding a valueset on Postgres --- .../ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java index 812f47b1dbf..5f34c1661ba 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java @@ -994,22 +994,25 @@ public abstract class BaseHapiFhirResourceDao extends B } @Override - @Transactional public T read(IIdType theId) { return read(theId, null); } @Override - @Transactional public T read(IIdType theId, RequestDetails theRequestDetails) { return read(theId, theRequestDetails, false); } @Override - @Transactional public T read(IIdType theId, RequestDetails theRequest, boolean theDeletedOk) { validateResourceTypeAndThrowInvalidRequestException(theId); + return myTransactionService.execute(theRequest, tx-> doRead(theId, theRequest, theDeletedOk)); + } + + public T doRead(IIdType theId, RequestDetails theRequest, boolean theDeletedOk) { + assert TransactionSynchronizationManager.isActualTransactionActive(); + // Notify interceptors if (theRequest != null) { ActionRequestDetails requestDetails = new ActionRequestDetails(theRequest, getResourceName(), theId); From 90ebaf8549aa73cbe753058badb2105b1bb836ce Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Tue, 11 Aug 2020 08:54:03 -0400 Subject: [PATCH 11/21] Add one more test for #2023 --- .../fhir/rest/gclient/NumberClientParam.java | 26 +++++----- .../provider/ResourceProviderDstu2Test.java | 52 +++++++++++++++++++ 2 files changed, 65 insertions(+), 13 deletions(-) diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/NumberClientParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/NumberClientParam.java index c0d68e96b7a..715e8784bf0 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/NumberClientParam.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/NumberClientParam.java @@ -27,7 +27,7 @@ import ca.uhn.fhir.rest.param.ParamPrefixEnum; */ public class NumberClientParam extends BaseClientParam implements IParam { - private String myParamName; + private final String myParamName; public NumberClientParam(String theParamName) { myParamName = theParamName; @@ -37,12 +37,12 @@ public class NumberClientParam extends BaseClientParam implements IParam { return new IMatches>() { @Override public ICriterion number(long theNumber) { - return new StringCriterion(getParamName(), Long.toString(theNumber)); + return new StringCriterion<>(getParamName(), Long.toString(theNumber)); } @Override public ICriterion number(String theNumber) { - return new StringCriterion(getParamName(), (theNumber)); + return new StringCriterion<>(getParamName(), (theNumber)); } }; } @@ -56,12 +56,12 @@ public class NumberClientParam extends BaseClientParam implements IParam { return new IMatches>() { @Override public ICriterion number(long theNumber) { - return new StringCriterion(getParamName(), ParamPrefixEnum.GREATERTHAN, Long.toString(theNumber)); + return new StringCriterion<>(getParamName(), ParamPrefixEnum.GREATERTHAN, Long.toString(theNumber)); } @Override public ICriterion number(String theNumber) { - return new StringCriterion(getParamName(), ParamPrefixEnum.GREATERTHAN, (theNumber)); + return new StringCriterion<>(getParamName(), ParamPrefixEnum.GREATERTHAN, (theNumber)); } }; } @@ -70,12 +70,12 @@ public class NumberClientParam extends BaseClientParam implements IParam { return new IMatches>() { @Override public ICriterion number(long theNumber) { - return new StringCriterion(getParamName(), ParamPrefixEnum.GREATERTHAN_OR_EQUALS, Long.toString(theNumber)); + return new StringCriterion<>(getParamName(), ParamPrefixEnum.GREATERTHAN_OR_EQUALS, Long.toString(theNumber)); } @Override public ICriterion number(String theNumber) { - return new StringCriterion(getParamName(), ParamPrefixEnum.GREATERTHAN_OR_EQUALS, (theNumber)); + return new StringCriterion<>(getParamName(), ParamPrefixEnum.GREATERTHAN_OR_EQUALS, (theNumber)); } }; } @@ -84,12 +84,12 @@ public class NumberClientParam extends BaseClientParam implements IParam { return new IMatches>() { @Override public ICriterion number(long theNumber) { - return new StringCriterion(getParamName(), ParamPrefixEnum.LESSTHAN, Long.toString(theNumber)); + return new StringCriterion<>(getParamName(), ParamPrefixEnum.LESSTHAN, Long.toString(theNumber)); } @Override public ICriterion number(String theNumber) { - return new StringCriterion(getParamName(), ParamPrefixEnum.LESSTHAN, (theNumber)); + return new StringCriterion<>(getParamName(), ParamPrefixEnum.LESSTHAN, (theNumber)); } }; } @@ -98,12 +98,12 @@ public class NumberClientParam extends BaseClientParam implements IParam { return new IMatches>() { @Override public ICriterion number(long theNumber) { - return new StringCriterion(getParamName(), ParamPrefixEnum.LESSTHAN_OR_EQUALS, Long.toString(theNumber)); + return new StringCriterion<>(getParamName(), ParamPrefixEnum.LESSTHAN_OR_EQUALS, Long.toString(theNumber)); } @Override public ICriterion number(String theNumber) { - return new StringCriterion(getParamName(), ParamPrefixEnum.LESSTHAN_OR_EQUALS, (theNumber)); + return new StringCriterion<>(getParamName(), ParamPrefixEnum.LESSTHAN_OR_EQUALS, (theNumber)); } }; } @@ -112,12 +112,12 @@ public class NumberClientParam extends BaseClientParam implements IParam { return new IMatches>() { @Override public ICriterion number(long theNumber) { - return new StringCriterion(getParamName(), thePrefix, Long.toString(theNumber)); + return new StringCriterion<>(getParamName(), thePrefix, Long.toString(theNumber)); } @Override public ICriterion number(String theNumber) { - return new StringCriterion(getParamName(), thePrefix, (theNumber)); + return new StringCriterion<>(getParamName(), thePrefix, (theNumber)); } }; } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java index 77c3c89e41d..76c30864d4a 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java @@ -35,6 +35,12 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import ca.uhn.fhir.model.dstu2.resource.SearchParameter; +import ca.uhn.fhir.model.dstu2.valueset.XPathUsageTypeEnum; +import ca.uhn.fhir.model.primitive.IntegerDt; +import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.rest.gclient.NumberClientParam; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.http.client.methods.CloseableHttpResponse; @@ -168,6 +174,52 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test { } } + /** + * See #2023 + */ + @Test + public void testCustomNumberSearchParam() { + SearchParameter numberParameter = new SearchParameter(); + numberParameter.setId("future-appointment-count"); + numberParameter.setName("Future Appointment Count"); + numberParameter.setCode("future-appointment-count"); + numberParameter.setDescription("Count of future appointments for the patient"); + numberParameter.setUrl("http://integer"); + numberParameter.setStatus(ca.uhn.fhir.model.dstu2.valueset.ConformanceResourceStatusEnum.ACTIVE); + numberParameter.setBase(ca.uhn.fhir.model.dstu2.valueset.ResourceTypeEnum.PATIENT); + numberParameter.setType(ca.uhn.fhir.model.dstu2.valueset.SearchParamTypeEnum.NUMBER); + numberParameter.setXpathUsage(XPathUsageTypeEnum.NORMAL); + numberParameter.setXpath("Patient.extension('http://integer')"); + ourClient.update().resource(numberParameter).execute(); + + // This fires every 10 seconds + mySearchParamRegistry.refreshCacheIfNecessary(); + + Patient patient = new Patient(); + patient.setId("future-appointment-count-pt"); + patient.setActive(true); + patient.addUndeclaredExtension(false, "http://integer", new IntegerDt(2)); + ourClient.update().resource(patient).execute(); + + Bundle futureAppointmentCountBundle2 = ourClient + .search() + .forResource(Patient.class) + .where(new NumberClientParam("future-appointment-count").greaterThan().number(1)) + .returnBundle(Bundle.class) + .execute(); + assertEquals(futureAppointmentCountBundle2.getTotal().intValue(), 1); + + Bundle futureAppointmentCountBundle3 = ourClient + .search() + .forResource(Patient.class) + .where(new NumberClientParam("future-appointment-count").exactly().number(2)) + .returnBundle(Bundle.class) + .execute(); + assertEquals(futureAppointmentCountBundle3.getTotal().intValue(), 1); + + } + + /** * See #484 */ From aed97ad1cb28e3883c1334f0969f801c5dc96932 Mon Sep 17 00:00:00 2001 From: Ken Stevens Date: Tue, 11 Aug 2020 15:33:33 -0400 Subject: [PATCH 12/21] added EMPI change log (#2036) --- .../resources/ca/uhn/hapi/fhir/changelog/5_1_0/2021-empi | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_1_0/2021-empi diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_1_0/2021-empi b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_1_0/2021-empi new file mode 100644 index 00000000000..5e2757c426a --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_1_0/2021-empi @@ -0,0 +1,9 @@ +--- +type: add +issue: 2021 +title: "Added [EMPI](https://hapifhir.io/hapi-fhir/docs/server_jpa_empi/empi.html) functionality, including phonetic +indexing, asynchronous rules-based patient and practitioner matching when resources are created and updated. A number of +[EMPI Operations](https://hapifhir.io/hapi-fhir/docs/server_jpa_empi/empi_operations.html) are provided to +maintain EMPI links (e.g. resolving possible matches and possible duplicates). Also batch operations +are provided to identify links in existing patients and practitioners, and to 'wipe clean' all EMPI data and re-run +the batch as the empi matching rules are refined." From 5fb7bd5156fc645bd60d6b5fde86306718826665 Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Tue, 11 Aug 2020 17:43:13 -0400 Subject: [PATCH 13/21] Version bump to 5.1.0 --- hapi-deployable-pom/pom.xml | 2 +- hapi-fhir-android/pom.xml | 2 +- hapi-fhir-base/pom.xml | 2 +- hapi-fhir-bom/pom.xml | 4 ++-- hapi-fhir-cli/hapi-fhir-cli-api/pom.xml | 2 +- hapi-fhir-cli/hapi-fhir-cli-app/pom.xml | 2 +- hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml | 2 +- hapi-fhir-cli/pom.xml | 2 +- hapi-fhir-client-okhttp/pom.xml | 2 +- hapi-fhir-client/pom.xml | 2 +- hapi-fhir-converter/pom.xml | 2 +- hapi-fhir-dist/pom.xml | 2 +- hapi-fhir-docs/pom.xml | 8 +++---- hapi-fhir-elasticsearch-6/pom.xml | 2 +- hapi-fhir-igpacks/pom.xml | 2 +- hapi-fhir-jacoco/pom.xml | 2 +- hapi-fhir-jaxrsserver-base/pom.xml | 2 +- hapi-fhir-jaxrsserver-example/pom.xml | 2 +- hapi-fhir-jpaserver-api/pom.xml | 2 +- hapi-fhir-jpaserver-base/pom.xml | 2 +- .../FhirResourceDaoDstu2ValidateTest.java | 2 +- .../FhirResourceDaoDstu3ValidateTest.java | 2 +- .../dao/r4/FhirResourceDaoR4ValidateTest.java | 18 +++++++-------- .../provider/r4/ResourceProviderR4Test.java | 2 +- hapi-fhir-jpaserver-batch/pom.xml | 2 +- hapi-fhir-jpaserver-empi/pom.xml | 6 ++--- hapi-fhir-jpaserver-migrate/pom.xml | 2 +- hapi-fhir-jpaserver-model/pom.xml | 2 +- hapi-fhir-jpaserver-searchparam/pom.xml | 2 +- hapi-fhir-jpaserver-subscription/pom.xml | 2 +- hapi-fhir-jpaserver-test-utilities/pom.xml | 2 +- hapi-fhir-jpaserver-uhnfhirtest/pom.xml | 4 ++-- hapi-fhir-server-empi/pom.xml | 2 +- hapi-fhir-server/pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../hapi-fhir-spring-boot-samples/pom.xml | 2 +- .../hapi-fhir-spring-boot-starter/pom.xml | 2 +- hapi-fhir-spring-boot/pom.xml | 2 +- hapi-fhir-structures-dstu2.1/pom.xml | 2 +- hapi-fhir-structures-dstu2/pom.xml | 2 +- hapi-fhir-structures-dstu3/pom.xml | 2 +- hapi-fhir-structures-hl7org-dstu2/pom.xml | 2 +- hapi-fhir-structures-r4/pom.xml | 2 +- hapi-fhir-structures-r5/pom.xml | 2 +- hapi-fhir-test-utilities/pom.xml | 2 +- hapi-fhir-testpage-overlay/pom.xml | 2 +- .../pom.xml | 2 +- hapi-fhir-validation-resources-dstu2/pom.xml | 2 +- hapi-fhir-validation-resources-dstu3/pom.xml | 2 +- hapi-fhir-validation-resources-r4/pom.xml | 2 +- hapi-fhir-validation-resources-r5/pom.xml | 2 +- hapi-fhir-validation/pom.xml | 2 +- .../validator/ValidatorWrapper.java | 2 +- .../FhirInstanceValidatorDstu3Test.java | 16 +++++++------- ...estionnaireResponseValidatorDstu3Test.java | 18 +++++++-------- .../FhirInstanceValidatorR4Test.java | 22 +++++++++---------- .../QuestionnaireResponseValidatorR4Test.java | 12 +++++----- .../FhirInstanceValidatorR5Test.java | 16 +++++++------- .../QuestionnaireResponseValidatorR5Test.java | 12 +++++----- hapi-tinder-plugin/pom.xml | 16 +++++++------- hapi-tinder-test/pom.xml | 2 +- pom.xml | 4 ++-- restful-server-example/pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- 70 files changed, 135 insertions(+), 135 deletions(-) diff --git a/hapi-deployable-pom/pom.xml b/hapi-deployable-pom/pom.xml index 2759c7efb46..b054346937e 100644 --- a/hapi-deployable-pom/pom.xml +++ b/hapi-deployable-pom/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.1.0-SNAPSHOT + 5.1.0 ../pom.xml diff --git a/hapi-fhir-android/pom.xml b/hapi-fhir-android/pom.xml index 0c9490e8fa9..4de342280da 100644 --- a/hapi-fhir-android/pom.xml +++ b/hapi-fhir-android/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0-SNAPSHOT + 5.1.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml index 7e7c7779ce8..c46695a0609 100644 --- a/hapi-fhir-base/pom.xml +++ b/hapi-fhir-base/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0-SNAPSHOT + 5.1.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-bom/pom.xml b/hapi-fhir-bom/pom.xml index 1029fa41c2a..d8994ffa015 100644 --- a/hapi-fhir-bom/pom.xml +++ b/hapi-fhir-bom/pom.xml @@ -3,14 +3,14 @@ 4.0.0 ca.uhn.hapi.fhir hapi-fhir-bom - 5.1.0-SNAPSHOT + 5.1.0 pom HAPI FHIR BOM ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0-SNAPSHOT + 5.1.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml index ddd8a7cb709..3c442ca1ea2 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0-SNAPSHOT + 5.1.0 ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml index 3b8220a067b..69a80ad2e60 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir-cli - 5.1.0-SNAPSHOT + 5.1.0 ../pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml index 500d69a18c5..a2396064cee 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0-SNAPSHOT + 5.1.0 ../../hapi-deployable-pom diff --git a/hapi-fhir-cli/pom.xml b/hapi-fhir-cli/pom.xml index 32bb08fbd6a..70e01ef3a02 100644 --- a/hapi-fhir-cli/pom.xml +++ b/hapi-fhir-cli/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.1.0-SNAPSHOT + 5.1.0 ../pom.xml diff --git a/hapi-fhir-client-okhttp/pom.xml b/hapi-fhir-client-okhttp/pom.xml index 888527bfe95..94f0320bfaf 100644 --- a/hapi-fhir-client-okhttp/pom.xml +++ b/hapi-fhir-client-okhttp/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0-SNAPSHOT + 5.1.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-client/pom.xml b/hapi-fhir-client/pom.xml index af66c3e54e3..eced316e8dc 100644 --- a/hapi-fhir-client/pom.xml +++ b/hapi-fhir-client/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0-SNAPSHOT + 5.1.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-converter/pom.xml b/hapi-fhir-converter/pom.xml index d8cb452596d..b4223f3ae3b 100644 --- a/hapi-fhir-converter/pom.xml +++ b/hapi-fhir-converter/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0-SNAPSHOT + 5.1.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-dist/pom.xml b/hapi-fhir-dist/pom.xml index 6d75c54b1cb..28aea3eabbb 100644 --- a/hapi-fhir-dist/pom.xml +++ b/hapi-fhir-dist/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.1.0-SNAPSHOT + 5.1.0 ../pom.xml diff --git a/hapi-fhir-docs/pom.xml b/hapi-fhir-docs/pom.xml index b68ee82abb6..26d3b777506 100644 --- a/hapi-fhir-docs/pom.xml +++ b/hapi-fhir-docs/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0-SNAPSHOT + 5.1.0 ../hapi-deployable-pom/pom.xml @@ -73,13 +73,13 @@ ca.uhn.hapi.fhir hapi-fhir-structures-dstu2 - 5.1.0-SNAPSHOT + 5.1.0 compile ca.uhn.hapi.fhir hapi-fhir-jpaserver-subscription - 5.1.0-SNAPSHOT + 5.1.0 compile @@ -96,7 +96,7 @@ ca.uhn.hapi.fhir hapi-fhir-testpage-overlay - 5.1.0-SNAPSHOT + 5.1.0 classes diff --git a/hapi-fhir-elasticsearch-6/pom.xml b/hapi-fhir-elasticsearch-6/pom.xml index e56854ab3bb..ef9b8bcf6ea 100644 --- a/hapi-fhir-elasticsearch-6/pom.xml +++ b/hapi-fhir-elasticsearch-6/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0-SNAPSHOT + 5.1.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-igpacks/pom.xml b/hapi-fhir-igpacks/pom.xml index d9e352d6413..236f4946ac8 100644 --- a/hapi-fhir-igpacks/pom.xml +++ b/hapi-fhir-igpacks/pom.xml @@ -5,7 +5,7 @@ hapi-deployable-pom ca.uhn.hapi.fhir - 5.1.0-SNAPSHOT + 5.1.0 ../hapi-deployable-pom/pom.xml 4.0.0 diff --git a/hapi-fhir-jacoco/pom.xml b/hapi-fhir-jacoco/pom.xml index 1c0564d16b9..93477cc6493 100644 --- a/hapi-fhir-jacoco/pom.xml +++ b/hapi-fhir-jacoco/pom.xml @@ -11,7 +11,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0-SNAPSHOT + 5.1.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jaxrsserver-base/pom.xml b/hapi-fhir-jaxrsserver-base/pom.xml index ade618da234..f8dd624af87 100644 --- a/hapi-fhir-jaxrsserver-base/pom.xml +++ b/hapi-fhir-jaxrsserver-base/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0-SNAPSHOT + 5.1.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jaxrsserver-example/pom.xml b/hapi-fhir-jaxrsserver-example/pom.xml index a751d170992..8f899ec3d4b 100644 --- a/hapi-fhir-jaxrsserver-example/pom.xml +++ b/hapi-fhir-jaxrsserver-example/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.1.0-SNAPSHOT + 5.1.0 ../pom.xml diff --git a/hapi-fhir-jpaserver-api/pom.xml b/hapi-fhir-jpaserver-api/pom.xml index e1e5748d398..af4ff0c90dc 100644 --- a/hapi-fhir-jpaserver-api/pom.xml +++ b/hapi-fhir-jpaserver-api/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0-SNAPSHOT + 5.1.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-base/pom.xml b/hapi-fhir-jpaserver-base/pom.xml index 5dd85caa300..afaae1146df 100644 --- a/hapi-fhir-jpaserver-base/pom.xml +++ b/hapi-fhir-jpaserver-base/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0-SNAPSHOT + 5.1.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2ValidateTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2ValidateTest.java index 620be635195..53da7983125 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2ValidateTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2ValidateTest.java @@ -144,7 +144,7 @@ public class FhirResourceDaoDstu2ValidateTest extends BaseJpaDstu2Test { } catch (PreconditionFailedException e) { String ooString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(e.getOperationOutcome()); ourLog.info(ooString); - assertThat(ooString, containsString("Profile reference \\\"http://example.com/StructureDefinition/testValidateResourceContainingProfileDeclarationInvalid\\\" could not be resolved, so has not been checked")); + assertThat(ooString, containsString("Profile reference 'http://example.com/StructureDefinition/testValidateResourceContainingProfileDeclarationInvalid' could not be resolved, so has not been checked")); } } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValidateTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValidateTest.java index 3745e2f2b53..350bf1aa0ab 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValidateTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValidateTest.java @@ -340,7 +340,7 @@ public class FhirResourceDaoDstu3ValidateTest extends BaseJpaDstu3Test { OperationOutcome oo = (OperationOutcome) e.getOperationOutcome(); String outputString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo); ourLog.info(outputString); - assertThat(outputString, containsString("Profile reference \\\"http://example.com/StructureDefinition/testValidateResourceContainingProfileDeclarationInvalid\\\" could not be resolved, so has not been checked")); + assertThat(outputString, containsString("Profile reference 'http://example.com/StructureDefinition/testValidateResourceContainingProfileDeclarationInvalid' could not be resolved, so has not been checked")); } } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java index 1f98f4434ad..9a6804f4020 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java @@ -314,7 +314,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { outcome = (OperationOutcome) e.getOperationOutcome(); String outcomeStr = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome); ourLog.info("Validation outcome: {}", outcomeStr); - assertThat(outcomeStr, containsString("The Profile \\\"https://bb/StructureDefinition/BBDemographicAge\\\" definition allows for the type Quantity but found type string")); + assertThat(outcomeStr, containsString("The Profile 'https://bb/StructureDefinition/BBDemographicAge' definition allows for the type Quantity but found type string")); } } @@ -466,7 +466,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { obs.setSubject(new Reference("Group/123")); OperationOutcome oo = validateAndReturnOutcome(obs); ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo)); - assertEquals("Unable to resolve resource \"Group/123\"", oo.getIssueFirstRep().getDiagnostics(), encode(oo)); + assertEquals("Unable to resolve resource 'Group/123'", oo.getIssueFirstRep().getDiagnostics(), encode(oo)); // Target of wrong type obs.setSubject(new Reference("Group/ABC")); @@ -530,7 +530,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { obs.setSubject(new Reference("Group/123")); OperationOutcome oo = validateAndReturnOutcome(obs); ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo)); - assertEquals("Unable to resolve resource \"Group/123\"", oo.getIssueFirstRep().getDiagnostics(), encode(oo)); + assertEquals("Unable to resolve resource 'Group/123'", oo.getIssueFirstRep().getDiagnostics(), encode(oo)); // Target of wrong type obs.setSubject(new Reference("Group/ABC")); @@ -595,7 +595,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { obs.setSubject(new Reference("Group/123")); OperationOutcome oo = validateAndReturnOutcome(obs); ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo)); - assertEquals("Unable to resolve resource \"Group/123\"", oo.getIssueFirstRep().getDiagnostics(), encode(oo)); + assertEquals("Unable to resolve resource 'Group/123'", oo.getIssueFirstRep().getDiagnostics(), encode(oo)); // Target of wrong type obs.setSubject(new Reference("Group/ABC")); @@ -1134,7 +1134,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { org.hl7.fhir.r4.model.OperationOutcome oo = (org.hl7.fhir.r4.model.OperationOutcome) e.getOperationOutcome(); String outputString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo); ourLog.info(outputString); - assertThat(outputString, containsString("Profile reference \\\"http://example.com/StructureDefinition/testValidateResourceContainingProfileDeclarationInvalid\\\" could not be resolved, so has not been checked")); + assertThat(outputString, containsString("Profile reference 'http://example.com/StructureDefinition/testValidateResourceContainingProfileDeclarationInvalid' could not be resolved, so has not been checked")); } } @@ -1170,7 +1170,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { org.hl7.fhir.r4.model.OperationOutcome oo = (org.hl7.fhir.r4.model.OperationOutcome) e.getOperationOutcome(); String outputString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo); ourLog.info(outputString); - assertThat(outputString, containsString("Profile reference \\\"http://example.com/StructureDefinition/testValidateResourceContainingProfileDeclarationInvalid\\\" could not be resolved, so has not been checked")); + assertThat(outputString, containsString("Profile reference 'http://example.com/StructureDefinition/testValidateResourceContainingProfileDeclarationInvalid' could not be resolved, so has not been checked")); } } @@ -1453,7 +1453,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { fail("Didn't fail- response was " + encode); } catch (PreconditionFailedException e) { OperationOutcome oo = (OperationOutcome) e.getOperationOutcome(); - assertEquals("No response answer found for required item \"link0\"", oo.getIssueFirstRep().getDiagnostics()); + assertEquals("No response answer found for required item 'link0'", oo.getIssueFirstRep().getDiagnostics()); } } @@ -1482,7 +1482,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { fail("Didn't fail- response was " + encode); } catch (PreconditionFailedException e) { OperationOutcome oo = (OperationOutcome) e.getOperationOutcome(); - assertEquals("No response answer found for required item \"link0\"", oo.getIssueFirstRep().getDiagnostics()); + assertEquals("No response answer found for required item 'link0'", oo.getIssueFirstRep().getDiagnostics()); } } @@ -1506,7 +1506,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { try { MethodOutcome validationOutcome = myQuestionnaireResponseDao.validate(qa, null, null, null, null, null, null); OperationOutcome oo = (OperationOutcome) validationOutcome.getOperationOutcome(); - assertEquals("The questionnaire \"http://foo/Questionnaire/DOES_NOT_EXIST\" could not be resolved, so no validation can be performed against the base questionnaire", oo.getIssueFirstRep().getDiagnostics()); + assertEquals("The questionnaire 'http://foo/Questionnaire/DOES_NOT_EXIST' could not be resolved, so no validation can be performed against the base questionnaire", oo.getIssueFirstRep().getDiagnostics()); } catch (PreconditionFailedException e) { fail(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(e.getOperationOutcome())); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java index 5e9159f4c8f..5298e159403 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java @@ -2359,7 +2359,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { String respString = IOUtils.toString(resp.getEntity().getContent(), Charsets.UTF_8); ourLog.info(respString); assertEquals(412, resp.getStatusLine().getStatusCode()); - assertThat(respString, containsString("Profile reference \\\"http://foo/structuredefinition/myprofile\\\" could not be resolved, so has not been checked")); + assertThat(respString, containsString("Profile reference 'http://foo/structuredefinition/myprofile' could not be resolved, so has not been checked")); } } diff --git a/hapi-fhir-jpaserver-batch/pom.xml b/hapi-fhir-jpaserver-batch/pom.xml index 17d3851f73d..a4a9292e818 100644 --- a/hapi-fhir-jpaserver-batch/pom.xml +++ b/hapi-fhir-jpaserver-batch/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0-SNAPSHOT + 5.1.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-empi/pom.xml b/hapi-fhir-jpaserver-empi/pom.xml index 7fcd22c274e..f3ccc7da449 100644 --- a/hapi-fhir-jpaserver-empi/pom.xml +++ b/hapi-fhir-jpaserver-empi/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0-SNAPSHOT + 5.1.0 ../hapi-deployable-pom/pom.xml @@ -55,13 +55,13 @@ ca.uhn.hapi.fhir hapi-fhir-test-utilities - 5.1.0-SNAPSHOT + 5.1.0 test ca.uhn.hapi.fhir hapi-fhir-jpaserver-test-utilities - 5.1.0-SNAPSHOT + 5.1.0 test diff --git a/hapi-fhir-jpaserver-migrate/pom.xml b/hapi-fhir-jpaserver-migrate/pom.xml index b01c017ec53..9779c2a6830 100644 --- a/hapi-fhir-jpaserver-migrate/pom.xml +++ b/hapi-fhir-jpaserver-migrate/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0-SNAPSHOT + 5.1.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-model/pom.xml b/hapi-fhir-jpaserver-model/pom.xml index 0d091ad7b4d..e5f567e1fc9 100644 --- a/hapi-fhir-jpaserver-model/pom.xml +++ b/hapi-fhir-jpaserver-model/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0-SNAPSHOT + 5.1.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-searchparam/pom.xml b/hapi-fhir-jpaserver-searchparam/pom.xml index aca48a8eda0..5daf00da514 100755 --- a/hapi-fhir-jpaserver-searchparam/pom.xml +++ b/hapi-fhir-jpaserver-searchparam/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0-SNAPSHOT + 5.1.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-subscription/pom.xml b/hapi-fhir-jpaserver-subscription/pom.xml index 49cdbd5a904..940d2a36063 100644 --- a/hapi-fhir-jpaserver-subscription/pom.xml +++ b/hapi-fhir-jpaserver-subscription/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0-SNAPSHOT + 5.1.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-utilities/pom.xml b/hapi-fhir-jpaserver-test-utilities/pom.xml index 22d8cf83731..e06b8cb655d 100644 --- a/hapi-fhir-jpaserver-test-utilities/pom.xml +++ b/hapi-fhir-jpaserver-test-utilities/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0-SNAPSHOT + 5.1.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml index 6da16e6154b..a63771d518b 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml +++ b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.1.0-SNAPSHOT + 5.1.0 ../pom.xml @@ -157,7 +157,7 @@ ca.uhn.hapi.fhir hapi-fhir-converter - 5.1.0-SNAPSHOT + 5.1.0 diff --git a/hapi-fhir-server-empi/pom.xml b/hapi-fhir-server-empi/pom.xml index 3a00cbe9979..5ae13568421 100644 --- a/hapi-fhir-server-empi/pom.xml +++ b/hapi-fhir-server-empi/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0-SNAPSHOT + 5.1.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server/pom.xml b/hapi-fhir-server/pom.xml index 2fb5c5bfc41..f15f3b77085 100644 --- a/hapi-fhir-server/pom.xml +++ b/hapi-fhir-server/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0-SNAPSHOT + 5.1.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml index 20b28d9f538..2d5a11dfe06 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0-SNAPSHOT + 5.1.0 ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml index 2aa29a39cf4..f05e1f154b6 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 5.1.0-SNAPSHOT + 5.1.0 hapi-fhir-spring-boot-sample-client-apache diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml index 172179d3278..9b5b91f81d6 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 5.1.0-SNAPSHOT + 5.1.0 hapi-fhir-spring-boot-sample-client-okhttp diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml index f82983eca27..08dd28eed34 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 5.1.0-SNAPSHOT + 5.1.0 hapi-fhir-spring-boot-sample-server-jersey diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jpa/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jpa/pom.xml index 1e775b24432..e836f0b9980 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jpa/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jpa/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 5.1.0-SNAPSHOT + 5.1.0 hapi-fhir-spring-boot-sample-server-jpa diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml index 313bbc066b5..1ffefc56198 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot - 5.1.0-SNAPSHOT + 5.1.0 hapi-fhir-spring-boot-samples diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml index 52916fdbdec..32a32dde31e 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0-SNAPSHOT + 5.1.0 ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/pom.xml b/hapi-fhir-spring-boot/pom.xml index 31be0e2d12f..87069fdbb0c 100644 --- a/hapi-fhir-spring-boot/pom.xml +++ b/hapi-fhir-spring-boot/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.1.0-SNAPSHOT + 5.1.0 ../pom.xml diff --git a/hapi-fhir-structures-dstu2.1/pom.xml b/hapi-fhir-structures-dstu2.1/pom.xml index 50855a66acf..380725a74a4 100644 --- a/hapi-fhir-structures-dstu2.1/pom.xml +++ b/hapi-fhir-structures-dstu2.1/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0-SNAPSHOT + 5.1.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu2/pom.xml b/hapi-fhir-structures-dstu2/pom.xml index 6b7d221dcf4..ebc8e726074 100644 --- a/hapi-fhir-structures-dstu2/pom.xml +++ b/hapi-fhir-structures-dstu2/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0-SNAPSHOT + 5.1.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu3/pom.xml b/hapi-fhir-structures-dstu3/pom.xml index 48a45e42d2a..f0e8bf36afc 100644 --- a/hapi-fhir-structures-dstu3/pom.xml +++ b/hapi-fhir-structures-dstu3/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0-SNAPSHOT + 5.1.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-hl7org-dstu2/pom.xml b/hapi-fhir-structures-hl7org-dstu2/pom.xml index 715cb77cfbf..b37bce27968 100644 --- a/hapi-fhir-structures-hl7org-dstu2/pom.xml +++ b/hapi-fhir-structures-hl7org-dstu2/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0-SNAPSHOT + 5.1.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4/pom.xml b/hapi-fhir-structures-r4/pom.xml index f20bd38071d..df6fa24f72a 100644 --- a/hapi-fhir-structures-r4/pom.xml +++ b/hapi-fhir-structures-r4/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0-SNAPSHOT + 5.1.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r5/pom.xml b/hapi-fhir-structures-r5/pom.xml index 974483c63aa..894a268f782 100644 --- a/hapi-fhir-structures-r5/pom.xml +++ b/hapi-fhir-structures-r5/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0-SNAPSHOT + 5.1.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-test-utilities/pom.xml b/hapi-fhir-test-utilities/pom.xml index 974b9e9257c..9cda17b9a7e 100644 --- a/hapi-fhir-test-utilities/pom.xml +++ b/hapi-fhir-test-utilities/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0-SNAPSHOT + 5.1.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-testpage-overlay/pom.xml b/hapi-fhir-testpage-overlay/pom.xml index 28ef8c7af91..b27ff765464 100644 --- a/hapi-fhir-testpage-overlay/pom.xml +++ b/hapi-fhir-testpage-overlay/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.1.0-SNAPSHOT + 5.1.0 ../pom.xml diff --git a/hapi-fhir-validation-resources-dstu2.1/pom.xml b/hapi-fhir-validation-resources-dstu2.1/pom.xml index dd5f6eea395..23eda4e7ecd 100644 --- a/hapi-fhir-validation-resources-dstu2.1/pom.xml +++ b/hapi-fhir-validation-resources-dstu2.1/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0-SNAPSHOT + 5.1.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-dstu2/pom.xml b/hapi-fhir-validation-resources-dstu2/pom.xml index 2970965dec0..61a332c3f9e 100644 --- a/hapi-fhir-validation-resources-dstu2/pom.xml +++ b/hapi-fhir-validation-resources-dstu2/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0-SNAPSHOT + 5.1.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-dstu3/pom.xml b/hapi-fhir-validation-resources-dstu3/pom.xml index c269362f608..281134c803d 100644 --- a/hapi-fhir-validation-resources-dstu3/pom.xml +++ b/hapi-fhir-validation-resources-dstu3/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0-SNAPSHOT + 5.1.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-r4/pom.xml b/hapi-fhir-validation-resources-r4/pom.xml index 4c821400648..8652a978c5d 100644 --- a/hapi-fhir-validation-resources-r4/pom.xml +++ b/hapi-fhir-validation-resources-r4/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0-SNAPSHOT + 5.1.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-r5/pom.xml b/hapi-fhir-validation-resources-r5/pom.xml index 344f6ba2ca4..ddb281a6782 100644 --- a/hapi-fhir-validation-resources-r5/pom.xml +++ b/hapi-fhir-validation-resources-r5/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0-SNAPSHOT + 5.1.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml index 44818476928..0421ac5061b 100644 --- a/hapi-fhir-validation/pom.xml +++ b/hapi-fhir-validation/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0-SNAPSHOT + 5.1.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/ValidatorWrapper.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/ValidatorWrapper.java index 243fb93814e..9452feb433a 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/ValidatorWrapper.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/ValidatorWrapper.java @@ -179,7 +179,7 @@ class ValidatorWrapper { i--; } - if (message.endsWith("\" could not be resolved, so has not been checked") && next.getLevel() == ValidationMessage.IssueSeverity.WARNING) { + if (message.endsWith("' could not be resolved, so has not been checked") && next.getLevel() == ValidationMessage.IssueSeverity.WARNING) { next.setLevel(ValidationMessage.IssueSeverity.ERROR); } diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidatorDstu3Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidatorDstu3Test.java index a9363d83c37..35265595c7c 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidatorDstu3Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidatorDstu3Test.java @@ -352,7 +352,7 @@ public class FhirInstanceValidatorDstu3Test { List all = logResultsAndReturnAll(result); assertEquals(1, all.size()); assertEquals(ResultSeverityEnum.ERROR, all.get(0).getSeverity()); - assertEquals("Unknown code 'urn:iso:std:iso:3166#QQ' for \"urn:iso:std:iso:3166#QQ\"", all.get(0).getMessage()); + assertEquals("Unknown code 'urn:iso:std:iso:3166#QQ' for 'urn:iso:std:iso:3166#QQ'", all.get(0).getMessage()); } } @@ -477,7 +477,7 @@ public class FhirInstanceValidatorDstu3Test { QuestionnaireResponse qr = loadResource("/dstu3/fmc02-questionnaireresponse-01.json", QuestionnaireResponse.class); ValidationResult result = myVal.validateWithResult(qr); List errors = logResultsAndReturnNonInformationalOnes(result); - assertThat(errors.get(0).getMessage(), containsString("Item has answer, even though it is not enabled (item id = \"BO_ConsDrop\")")); + assertThat(errors.get(0).getMessage(), containsString("Item has answer, even though it is not enabled (item id = 'BO_ConsDrop')")); assertEquals(1, errors.size()); } @@ -728,7 +728,7 @@ public class FhirInstanceValidatorDstu3Test { ValidationResult results = myVal.validateWithResult(is); List outcome = logResultsAndReturnNonInformationalOnes(results); assertEquals(1, outcome.size()); - assertEquals("Unknown code 'http://dicom.nema.org/resources/ontology/DCM#BAR' for \"http://dicom.nema.org/resources/ontology/DCM#BAR\"", outcome.get(0).getMessage()); + assertEquals("Unknown code 'http://dicom.nema.org/resources/ontology/DCM#BAR' for 'http://dicom.nema.org/resources/ontology/DCM#BAR'", outcome.get(0).getMessage()); // assertEquals("The Coding provided is not in the value set http://hl7.org/fhir/ValueSet/dicom-cid29, and a code should come from this value set unless it has no suitable code. (error message = Unknown code[BAR] in system[http://dicom.nema.org/resources/ontology/DCM])", outcome.get(1).getMessage()); } @@ -1087,7 +1087,7 @@ public class FhirInstanceValidatorDstu3Test { List errors = logResultsAndReturnAll(output); assertEquals(ResultSeverityEnum.ERROR, errors.get(0).getSeverity()); - assertEquals("Unknown code for \"http://loinc.org#12345\"", errors.get(0).getMessage()); + assertEquals("Unknown code for 'http://loinc.org#12345'", errors.get(0).getMessage()); } @Test @@ -1124,7 +1124,7 @@ public class FhirInstanceValidatorDstu3Test { myInstanceVal.setValidationSupport(myValidationSupport); ValidationResult output = myVal.validateWithResult(input); List errors = logResultsAndReturnNonInformationalOnes(output); - assertThat(errors.toString(), containsString("Profile reference \"http://foo/structuredefinition/myprofile\" could not be resolved, so has not been checked")); + assertThat(errors.toString(), containsString("Profile reference 'http://foo/structuredefinition/myprofile' could not be resolved, so has not been checked")); } @Test @@ -1168,7 +1168,7 @@ public class FhirInstanceValidatorDstu3Test { ValidationResult output = myVal.validateWithResult(input); logResultsAndReturnAll(output); assertEquals( - "The value provided (\"notvalidcode\") is not in the value set http://hl7.org/fhir/ValueSet/observation-status (http://hl7.org/fhir/ValueSet/observation-status), and a code is required from this value set) (error message = Unknown code 'notvalidcode')", + "The value provided ('notvalidcode') is not in the value set http://hl7.org/fhir/ValueSet/observation-status (http://hl7.org/fhir/ValueSet/observation-status), and a code is required from this value set) (error message = Unknown code 'notvalidcode')", output.getMessages().get(0).getMessage()); } @@ -1200,7 +1200,7 @@ public class FhirInstanceValidatorDstu3Test { ValidationResult output = myVal.validateWithResult(input); List errors = logResultsAndReturnAll(output); assertThat(errors.toString(), errors.size(), greaterThan(0)); - assertEquals("Unknown code for \"http://acme.org#9988877\"", errors.get(0).getMessage()); + assertEquals("Unknown code for 'http://acme.org#9988877'", errors.get(0).getMessage()); } @@ -1236,7 +1236,7 @@ public class FhirInstanceValidatorDstu3Test { ValidationResult output = myVal.validateWithResult(input); List errors = logResultsAndReturnNonInformationalOnes(output); assertEquals(1, errors.size()); - assertEquals("Unknown code for \"http://loinc.org#1234\"", errors.get(0).getMessage()); + assertEquals("Unknown code for 'http://loinc.org#1234'", errors.get(0).getMessage()); } @Test diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireResponseValidatorDstu3Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireResponseValidatorDstu3Test.java index 89b7d2dd3eb..95aecce3537 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireResponseValidatorDstu3Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireResponseValidatorDstu3Test.java @@ -364,7 +364,7 @@ public class QuestionnaireResponseValidatorDstu3Test { ValidationResult errors = myVal.validateWithResult(qa); ourLog.info(errors.toString()); - assertThat(errors.toString(), containsString("No response answer found for required item \"link0\"")); + assertThat(errors.toString(), containsString("No response answer found for required item 'link0'")); } @Test @@ -490,7 +490,7 @@ public class QuestionnaireResponseValidatorDstu3Test { ValidationResult errors = myVal.validateWithResult(qa); ourLog.info(errors.toString()); - assertThat(errors.toString(), containsString(" No response answer found for required item \"link1\"")); + assertThat(errors.toString(), containsString(" No response answer found for required item 'link1'")); } @Test @@ -532,7 +532,7 @@ public class QuestionnaireResponseValidatorDstu3Test { qa.addItem().setLinkId("link1").addAnswer().setValue(new StringType("HELLO")); errors = myVal.validateWithResult(qa); ourLog.info(errors.toString()); - assertThat(errors.toString(), containsString("Item has answer, even though it is not enabled (item id = \"link1\")")); + assertThat(errors.toString(), containsString("Item has answer, even though it is not enabled (item id = 'link1')")); // link0 has an answer, and it's the right one qa = new QuestionnaireResponse(); @@ -541,7 +541,7 @@ public class QuestionnaireResponseValidatorDstu3Test { qa.addItem().setLinkId("link0").addAnswer().setValue(new Coding("http://foo", "YES", null)); errors = myVal.validateWithResult(qa); ourLog.info(errors.toString()); - assertThat(errors.toString(), containsString("No response answer found for required item \"link1\"")); + assertThat(errors.toString(), containsString("No response answer found for required item 'link1'")); } @Test @@ -669,7 +669,7 @@ public class QuestionnaireResponseValidatorDstu3Test { // Without an answer ValidationResult errors = myVal.validateWithResult(qr); - assertThat(errors.toString(), containsString("No response answer found for required item \"link2\"")); + assertThat(errors.toString(), containsString("No response answer found for required item 'link2'")); // With an answer qr.getItem().get(2).addAnswer().setValue(new StringType("AAA")); @@ -1064,7 +1064,7 @@ public class QuestionnaireResponseValidatorDstu3Test { errors = myVal.validateWithResult(qa); errors = stripBindingHasNoSourceMessage(errors); ourLog.info(errors.toString()); - assertThat(errors.toString(), containsString("Unknown code for \"http://codesystems.com/system#code1\"")); + assertThat(errors.toString(), containsString("Unknown code for 'http://codesystems.com/system#code1'")); assertThat(errors.toString(), containsString("QuestionnaireResponse.item[0].answer[0]")); // Partial code @@ -1132,7 +1132,7 @@ public class QuestionnaireResponseValidatorDstu3Test { qa.addItem().setLinkId("link0").addAnswer().setValue(new Coding().setDisplay("")); errors = myVal.validateWithResult(qa); ourLog.info(errors.toString()); - assertThat(errors.toString(), containsString("No response answer found for required item \"link0\"")); + assertThat(errors.toString(), containsString("No response answer found for required item 'link0'")); } @Test @@ -1150,7 +1150,7 @@ public class QuestionnaireResponseValidatorDstu3Test { ourLog.info(errors.toString()); assertThat(errors.toString(), containsString(" - QuestionnaireResponse")); - assertThat(errors.toString(), containsString("LinkId \"link1\" not found in questionnaire")); + assertThat(errors.toString(), containsString("LinkId 'link1' not found in questionnaire")); } @Test @@ -1168,7 +1168,7 @@ public class QuestionnaireResponseValidatorDstu3Test { ourLog.info(errors.toString()); assertThat(errors.toString(), containsString(" - QuestionnaireResponse")); - assertThat(errors.toString(), containsString("LinkId \"link1\" not found in questionnaire")); + assertThat(errors.toString(), containsString("LinkId 'link1' not found in questionnaire")); } @BeforeAll diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java index ab2e1e19192..48d5cb31eb7 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java @@ -320,7 +320,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest { ValidationResult result = val.validateWithResult(p); List all = logResultsAndReturnErrorOnes(result); assertFalse(result.isSuccessful()); - assertEquals("The code \"AA \" is not valid (whitespace rules)", all.get(0).getMessage()); + assertEquals("The code 'AA ' is not valid (whitespace rules)", all.get(0).getMessage()); } @@ -524,7 +524,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest { ValidationResult output = myVal.validateWithResult(encoded); List errors = logResultsAndReturnNonInformationalOnes(output); assertEquals(1, errors.size()); - assertEquals("The value \"%%%2@()()\" is not a valid Base64 value", errors.get(0).getMessage()); + assertEquals("The value '%%%2@()()' is not a valid Base64 value", errors.get(0).getMessage()); } @@ -1111,7 +1111,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest { List errors = logResultsAndReturnAll(output); assertEquals(ResultSeverityEnum.ERROR, errors.get(0).getSeverity()); - assertEquals("Unknown code for \"http://loinc.org#12345\"", errors.get(0).getMessage()); + assertEquals("Unknown code for 'http://loinc.org#12345'", errors.get(0).getMessage()); } @Test @@ -1153,7 +1153,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest { List errors = logResultsAndReturnNonInformationalOnes(output); assertEquals(1, errors.size()); - assertEquals("Profile reference \"http://foo/structuredefinition/myprofile\" could not be resolved, so has not been checked", errors.get(0).getMessage()); + assertEquals("Profile reference 'http://foo/structuredefinition/myprofile' could not be resolved, so has not been checked", errors.get(0).getMessage()); assertEquals(ResultSeverityEnum.ERROR, errors.get(0).getSeverity()); } @@ -1202,7 +1202,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest { ValidationResult output = myVal.validateWithResult(input); logResultsAndReturnAll(output); assertEquals( - "The value provided (\"notvalidcode\") is not in the value set http://hl7.org/fhir/ValueSet/observation-status|4.0.1 (http://hl7.org/fhir/ValueSet/observation-status), and a code is required from this value set) (error message = Unknown code 'notvalidcode')", + "The value provided ('notvalidcode') is not in the value set http://hl7.org/fhir/ValueSet/observation-status|4.0.1 (http://hl7.org/fhir/ValueSet/observation-status), and a code is required from this value set) (error message = Unknown code 'notvalidcode')", output.getMessages().get(0).getMessage()); } @@ -1266,7 +1266,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest { ValidationResult output = myVal.validateWithResult(input); List errors = logResultsAndReturnAll(output); assertThat(errors.toString(), errors.size(), greaterThan(0)); - assertEquals("Unknown code for \"http://acme.org#9988877\"", errors.get(0).getMessage()); + assertEquals("Unknown code for 'http://acme.org#9988877'", errors.get(0).getMessage()); } @@ -1304,7 +1304,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest { ValidationResult output = myVal.validateWithResult(input); List errors = logResultsAndReturnNonInformationalOnes(output); assertEquals(1, errors.size()); - assertEquals("Unknown code for \"http://loinc.org#1234\"", errors.get(0).getMessage()); + assertEquals("Unknown code for 'http://loinc.org#1234'", errors.get(0).getMessage()); } @Test @@ -1354,8 +1354,8 @@ public class FhirInstanceValidatorR4Test extends BaseTest { output = myVal.validateWithResult(input); all = logResultsAndReturnNonInformationalOnes(output); assertEquals(2, all.size()); - assertThat(all.get(0).getMessage(), containsString("Validation failed for \"http://unitsofmeasure.org#Heck\"")); - assertThat(all.get(1).getMessage(), containsString("The value provided (\"Heck\") is not in the value set http://hl7.org/fhir/ValueSet/ucum-bodytemp")); + assertThat(all.get(0).getMessage(), containsString("Validation failed for 'http://unitsofmeasure.org#Heck'")); + assertThat(all.get(1).getMessage(), containsString("The value provided ('Heck') is not in the value set http://hl7.org/fhir/ValueSet/ucum-bodytemp")); } @@ -1453,7 +1453,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest { ValidationResult output = myVal.validateWithResult(input); List errors = logResultsAndReturnNonInformationalOnes(output); assertEquals(1, errors.size(), errors.toString()); - assertThat(errors.get(0).getMessage(), containsString("The value provided (\"BLAH\") is not in the value set http://hl7.org/fhir/ValueSet/currencies")); + assertThat(errors.get(0).getMessage(), containsString("The value provided ('BLAH') is not in the value set http://hl7.org/fhir/ValueSet/currencies")); } @@ -1492,7 +1492,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest { ValidationResult output = myVal.validateWithResult(allergy); List errors = logResultsAndReturnNonInformationalOnes(output); assertEquals(0, errors.size(), errors.toString()); - assertThat(errors.get(0).getMessage(), containsString("The value provided (\"BLAH\") is not in the value set http://hl7.org/fhir/ValueSet/currencies")); + assertThat(errors.get(0).getMessage(), containsString("The value provided ('BLAH') is not in the value set http://hl7.org/fhir/ValueSet/currencies")); } diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/QuestionnaireResponseValidatorR4Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/QuestionnaireResponseValidatorR4Test.java index e126ce078a5..152e911da2a 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/QuestionnaireResponseValidatorR4Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/QuestionnaireResponseValidatorR4Test.java @@ -246,7 +246,7 @@ public class QuestionnaireResponseValidatorR4Test { errors = myVal.validateWithResult(qa); errors = stripBindingHasNoSourceMessage(errors); ourLog.info(errors.toString()); - assertThat(errors.toString(), containsString("Unknown code for \"http://codesystems.com/system#code1\"")); + assertThat(errors.toString(), containsString("Unknown code for 'http://codesystems.com/system#code1'")); assertThat(errors.toString(), containsString("QuestionnaireResponse.item[0].answer[0]")); qa = new QuestionnaireResponse(); @@ -257,7 +257,7 @@ public class QuestionnaireResponseValidatorR4Test { errors = myVal.validateWithResult(qa); errors = stripBindingHasNoSourceMessage(errors); ourLog.info(errors.toString()); - assertThat(errors.toString(), containsString("Unknown code 'http://codesystems.com/system2#code3' for \"http://codesystems.com/system2#code3\"")); + assertThat(errors.toString(), containsString("Unknown code 'http://codesystems.com/system2#code3' for 'http://codesystems.com/system2#code3'")); assertThat(errors.toString(), containsString("QuestionnaireResponse.item[0].answer[0]")); } @@ -319,7 +319,7 @@ public class QuestionnaireResponseValidatorR4Test { ValidationResult errors = myVal.validateWithResult(qa); ourLog.info(errors.toString()); - assertThat(errors.toString(), containsString("No response answer found for required item \"link0\"")); + assertThat(errors.toString(), containsString("No response answer found for required item 'link0'")); } @Test @@ -641,7 +641,7 @@ public class QuestionnaireResponseValidatorR4Test { qa.addItem().setLinkId("link0").addAnswer().setValue(new Coding().setDisplay("")); errors = myVal.validateWithResult(qa); ourLog.info(errors.toString()); - assertThat(errors.toString(), containsString("No response answer found for required item \"link0\"")); + assertThat(errors.toString(), containsString("No response answer found for required item 'link0'")); } @@ -711,7 +711,7 @@ public class QuestionnaireResponseValidatorR4Test { ourLog.info(errors.toString()); assertThat(errors.toString(), containsString(" - QuestionnaireResponse")); - assertThat(errors.toString(), containsString("LinkId \"link1\" not found in questionnaire")); + assertThat(errors.toString(), containsString("LinkId 'link1' not found in questionnaire")); } @Test @@ -729,7 +729,7 @@ public class QuestionnaireResponseValidatorR4Test { ourLog.info(errors.toString()); assertThat(errors.toString(), containsString(" - QuestionnaireResponse")); - assertThat(errors.toString(), containsString("LinkId \"link1\" not found in questionnaire")); + assertThat(errors.toString(), containsString("LinkId 'link1' not found in questionnaire")); } @Test diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/FhirInstanceValidatorR5Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/FhirInstanceValidatorR5Test.java index f35132feda5..1ba19b530a8 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/FhirInstanceValidatorR5Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/FhirInstanceValidatorR5Test.java @@ -232,7 +232,7 @@ public class FhirInstanceValidatorR5Test { ValidationResult result = val.validateWithResult(p); List all = logResultsAndReturnErrorOnes(result); assertFalse(result.isSuccessful()); - assertEquals("The code \"AA \" is not valid (whitespace rules)", all.get(0).getMessage()); + assertEquals("The code 'AA ' is not valid (whitespace rules)", all.get(0).getMessage()); } @@ -345,7 +345,7 @@ public class FhirInstanceValidatorR5Test { ValidationResult output = myVal.validateWithResult(encoded); List errors = logResultsAndReturnNonInformationalOnes(output); assertEquals(1, errors.size()); - assertEquals("The value \"%%%2@()()\" is not a valid Base64 value", errors.get(0).getMessage()); + assertEquals("The value '%%%2@()()' is not a valid Base64 value", errors.get(0).getMessage()); } @@ -673,7 +673,7 @@ public class FhirInstanceValidatorR5Test { ValidationResult output = myVal.validateWithResult(input); assertEquals(1, output.getMessages().size(), output.toString()); - assertEquals("This \"Patient\" cannot be parsed as a FHIR object (no namespace)", output.getMessages().get(0).getMessage()); + assertEquals("This 'Patient' cannot be parsed as a FHIR object (no namespace)", output.getMessages().get(0).getMessage()); ourLog.info(output.getMessages().get(0).getLocationString()); } @@ -755,7 +755,7 @@ public class FhirInstanceValidatorR5Test { List errors = logResultsAndReturnAll(output); assertEquals(ResultSeverityEnum.ERROR, errors.get(0).getSeverity()); - assertEquals("Unknown code for \"http://loinc.org#12345\"", errors.get(0).getMessage()); + assertEquals("Unknown code for 'http://loinc.org#12345'", errors.get(0).getMessage()); } @Test @@ -795,7 +795,7 @@ public class FhirInstanceValidatorR5Test { myInstanceVal.setValidationSupport(myValidationSupport); ValidationResult output = myVal.validateWithResult(input); List errors = logResultsAndReturnNonInformationalOnes(output); - assertThat(errors.toString(), containsString("Profile reference \"http://foo/structuredefinition/myprofile\" could not be resolved, so has not been checked")); + assertThat(errors.toString(), containsString("Profile reference 'http://foo/structuredefinition/myprofile' could not be resolved, so has not been checked")); } @Test @@ -843,7 +843,7 @@ public class FhirInstanceValidatorR5Test { ValidationResult output = myVal.validateWithResult(input); logResultsAndReturnAll(output); assertEquals( - "The value provided (\"notvalidcode\") is not in the value set http://hl7.org/fhir/ValueSet/observation-status|4.4.0 (http://hl7.org/fhir/ValueSet/observation-status), and a code is required from this value set) (error message = Unknown code 'notvalidcode')", + "The value provided ('notvalidcode') is not in the value set http://hl7.org/fhir/ValueSet/observation-status|4.4.0 (http://hl7.org/fhir/ValueSet/observation-status), and a code is required from this value set) (error message = Unknown code 'notvalidcode')", output.getMessages().get(0).getMessage()); } @@ -877,7 +877,7 @@ public class FhirInstanceValidatorR5Test { ValidationResult output = myVal.validateWithResult(input); List errors = logResultsAndReturnAll(output); assertThat(errors.toString(), errors.size(), greaterThan(0)); - assertEquals("Unknown code for \"http://acme.org#9988877\"", errors.get(0).getMessage()); + assertEquals("Unknown code for 'http://acme.org#9988877'", errors.get(0).getMessage()); } @@ -915,7 +915,7 @@ public class FhirInstanceValidatorR5Test { ValidationResult output = myVal.validateWithResult(input); List errors = logResultsAndReturnNonInformationalOnes(output); assertEquals(1, errors.size()); - assertEquals("Unknown code for \"http://loinc.org#1234\"", errors.get(0).getMessage()); + assertEquals("Unknown code for 'http://loinc.org#1234'", errors.get(0).getMessage()); } @Test diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/QuestionnaireResponseValidatorR5Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/QuestionnaireResponseValidatorR5Test.java index 86a33a1a248..11878ec05dd 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/QuestionnaireResponseValidatorR5Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/QuestionnaireResponseValidatorR5Test.java @@ -250,7 +250,7 @@ public class QuestionnaireResponseValidatorR5Test { errors = myVal.validateWithResult(qa); errors = stripBindingHasNoSourceMessage(errors); ourLog.info(errors.toString()); - assertThat(errors.toString(), containsString("Unknown code for \"http://codesystems.com/system#code1\"")); + assertThat(errors.toString(), containsString("Unknown code for 'http://codesystems.com/system#code1'")); assertThat(errors.toString(), containsString("QuestionnaireResponse.item[0].answer[0]")); qa = new QuestionnaireResponse(); @@ -261,7 +261,7 @@ public class QuestionnaireResponseValidatorR5Test { errors = myVal.validateWithResult(qa); errors = stripBindingHasNoSourceMessage(errors); ourLog.info(errors.toString()); - assertThat(errors.toString(), containsString("Unknown code 'http://codesystems.com/system2#code3' for \"http://codesystems.com/system2#code3\"")); + assertThat(errors.toString(), containsString("Unknown code 'http://codesystems.com/system2#code3' for 'http://codesystems.com/system2#code3'")); assertThat(errors.toString(), containsString("QuestionnaireResponse.item[0].answer[0]")); } @@ -323,7 +323,7 @@ public class QuestionnaireResponseValidatorR5Test { ValidationResult errors = myVal.validateWithResult(qa); ourLog.info(errors.toString()); - assertThat(errors.toString(), containsString("No response answer found for required item \"link0\"")); + assertThat(errors.toString(), containsString("No response answer found for required item 'link0'")); } @Test @@ -584,7 +584,7 @@ public class QuestionnaireResponseValidatorR5Test { qa.addItem().setLinkId("link0").addAnswer().setValue(new Coding().setDisplay("")); errors = myVal.validateWithResult(qa); ourLog.info(errors.toString()); - assertThat(errors.toString(), containsString("No response answer found for required item \"link0\"")); + assertThat(errors.toString(), containsString("No response answer found for required item 'link0'")); } @@ -603,7 +603,7 @@ public class QuestionnaireResponseValidatorR5Test { ourLog.info(errors.toString()); assertThat(errors.toString(), containsString(" - QuestionnaireResponse")); - assertThat(errors.toString(), containsString("LinkId \"link1\" not found in questionnaire")); + assertThat(errors.toString(), containsString("LinkId 'link1' not found in questionnaire")); } @Test @@ -621,7 +621,7 @@ public class QuestionnaireResponseValidatorR5Test { ourLog.info(errors.toString()); assertThat(errors.toString(), containsString(" - QuestionnaireResponse")); - assertThat(errors.toString(), containsString("LinkId \"link1\" not found in questionnaire")); + assertThat(errors.toString(), containsString("LinkId 'link1' not found in questionnaire")); } @Test diff --git a/hapi-tinder-plugin/pom.xml b/hapi-tinder-plugin/pom.xml index 555a59caa36..2282b5cbb50 100644 --- a/hapi-tinder-plugin/pom.xml +++ b/hapi-tinder-plugin/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.1.0-SNAPSHOT + 5.1.0 ../pom.xml @@ -58,37 +58,37 @@ ca.uhn.hapi.fhir hapi-fhir-structures-dstu3 - 5.1.0-SNAPSHOT + 5.1.0 ca.uhn.hapi.fhir hapi-fhir-structures-hl7org-dstu2 - 5.1.0-SNAPSHOT + 5.1.0 ca.uhn.hapi.fhir hapi-fhir-structures-r4 - 5.1.0-SNAPSHOT + 5.1.0 ca.uhn.hapi.fhir hapi-fhir-structures-r5 - 5.1.0-SNAPSHOT + 5.1.0 ca.uhn.hapi.fhir hapi-fhir-validation-resources-dstu2 - 5.1.0-SNAPSHOT + 5.1.0 ca.uhn.hapi.fhir hapi-fhir-validation-resources-dstu3 - 5.1.0-SNAPSHOT + 5.1.0 ca.uhn.hapi.fhir hapi-fhir-validation-resources-r4 - 5.1.0-SNAPSHOT + 5.1.0 org.apache.velocity diff --git a/hapi-tinder-test/pom.xml b/hapi-tinder-test/pom.xml index 05f230210af..cab3fe9a4da 100644 --- a/hapi-tinder-test/pom.xml +++ b/hapi-tinder-test/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.1.0-SNAPSHOT + 5.1.0 ../pom.xml diff --git a/pom.xml b/pom.xml index d9700f35eee..572797c55f0 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir pom - 5.1.0-SNAPSHOT + 5.1.0 HAPI-FHIR An open-source implementation of the FHIR specification in Java. https://hapifhir.io @@ -678,7 +678,7 @@ - 5.0.22 + 5.1.0 1.0.2 -Dfile.encoding=UTF-8 -Xmx2048m diff --git a/restful-server-example/pom.xml b/restful-server-example/pom.xml index 57d87df0199..006fd0dd584 100644 --- a/restful-server-example/pom.xml +++ b/restful-server-example/pom.xml @@ -8,7 +8,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.1.0-SNAPSHOT + 5.1.0 ../pom.xml diff --git a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml index a07fff46c3f..d87686f5550 100644 --- a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml +++ b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.1.0-SNAPSHOT + 5.1.0 ../../pom.xml diff --git a/tests/hapi-fhir-base-test-mindeps-client/pom.xml b/tests/hapi-fhir-base-test-mindeps-client/pom.xml index 19d7b614cce..cf2f2313c8d 100644 --- a/tests/hapi-fhir-base-test-mindeps-client/pom.xml +++ b/tests/hapi-fhir-base-test-mindeps-client/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.1.0-SNAPSHOT + 5.1.0 ../../pom.xml diff --git a/tests/hapi-fhir-base-test-mindeps-server/pom.xml b/tests/hapi-fhir-base-test-mindeps-server/pom.xml index 47360a5a445..1cf21b861b6 100644 --- a/tests/hapi-fhir-base-test-mindeps-server/pom.xml +++ b/tests/hapi-fhir-base-test-mindeps-server/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.1.0-SNAPSHOT + 5.1.0 ../../pom.xml From 92718c7ef2b64a0ea867d480b2d76dfdbd56d1a0 Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Wed, 12 Aug 2020 10:23:46 -0400 Subject: [PATCH 14/21] Version bump to 5.2.0-SNAPSHOT --- hapi-deployable-pom/pom.xml | 2 +- hapi-fhir-android/pom.xml | 2 +- hapi-fhir-base/pom.xml | 2 +- .../main/java/ca/uhn/fhir/util/VersionEnum.java | 3 ++- hapi-fhir-bom/pom.xml | 4 ++-- hapi-fhir-cli/hapi-fhir-cli-api/pom.xml | 2 +- hapi-fhir-cli/hapi-fhir-cli-app/pom.xml | 2 +- hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml | 2 +- hapi-fhir-cli/pom.xml | 2 +- hapi-fhir-client-okhttp/pom.xml | 2 +- hapi-fhir-client/pom.xml | 2 +- hapi-fhir-converter/pom.xml | 2 +- hapi-fhir-dist/pom.xml | 2 +- hapi-fhir-docs/pom.xml | 8 ++++---- hapi-fhir-elasticsearch-6/pom.xml | 2 +- hapi-fhir-igpacks/pom.xml | 2 +- hapi-fhir-jacoco/pom.xml | 2 +- hapi-fhir-jaxrsserver-base/pom.xml | 2 +- hapi-fhir-jaxrsserver-example/pom.xml | 2 +- hapi-fhir-jpaserver-api/pom.xml | 2 +- hapi-fhir-jpaserver-base/pom.xml | 2 +- hapi-fhir-jpaserver-batch/pom.xml | 2 +- hapi-fhir-jpaserver-empi/pom.xml | 6 +++--- hapi-fhir-jpaserver-migrate/pom.xml | 2 +- hapi-fhir-jpaserver-model/pom.xml | 2 +- hapi-fhir-jpaserver-searchparam/pom.xml | 2 +- hapi-fhir-jpaserver-subscription/pom.xml | 2 +- hapi-fhir-jpaserver-test-utilities/pom.xml | 2 +- hapi-fhir-jpaserver-uhnfhirtest/pom.xml | 4 ++-- hapi-fhir-server-empi/pom.xml | 2 +- hapi-fhir-server/pom.xml | 2 +- .../hapi-fhir-spring-boot-autoconfigure/pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../hapi-fhir-spring-boot-samples/pom.xml | 2 +- .../hapi-fhir-spring-boot-starter/pom.xml | 2 +- hapi-fhir-spring-boot/pom.xml | 2 +- hapi-fhir-structures-dstu2.1/pom.xml | 2 +- hapi-fhir-structures-dstu2/pom.xml | 2 +- hapi-fhir-structures-dstu3/pom.xml | 2 +- hapi-fhir-structures-hl7org-dstu2/pom.xml | 2 +- hapi-fhir-structures-r4/pom.xml | 2 +- hapi-fhir-structures-r5/pom.xml | 2 +- hapi-fhir-test-utilities/pom.xml | 2 +- hapi-fhir-testpage-overlay/pom.xml | 2 +- hapi-fhir-validation-resources-dstu2.1/pom.xml | 2 +- hapi-fhir-validation-resources-dstu2/pom.xml | 2 +- hapi-fhir-validation-resources-dstu3/pom.xml | 2 +- hapi-fhir-validation-resources-r4/pom.xml | 2 +- hapi-fhir-validation-resources-r5/pom.xml | 2 +- hapi-fhir-validation/pom.xml | 2 +- hapi-tinder-plugin/pom.xml | 16 ++++++++-------- hapi-tinder-test/pom.xml | 2 +- pom.xml | 2 +- restful-server-example/pom.xml | 2 +- .../pom.xml | 2 +- tests/hapi-fhir-base-test-mindeps-client/pom.xml | 2 +- tests/hapi-fhir-base-test-mindeps-server/pom.xml | 2 +- 60 files changed, 75 insertions(+), 74 deletions(-) diff --git a/hapi-deployable-pom/pom.xml b/hapi-deployable-pom/pom.xml index b054346937e..81696e34d5c 100644 --- a/hapi-deployable-pom/pom.xml +++ b/hapi-deployable-pom/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.1.0 + 5.2.0-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-android/pom.xml b/hapi-fhir-android/pom.xml index 4de342280da..12afb86318b 100644 --- a/hapi-fhir-android/pom.xml +++ b/hapi-fhir-android/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0 + 5.2.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml index c46695a0609..683e0307c87 100644 --- a/hapi-fhir-base/pom.xml +++ b/hapi-fhir-base/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0 + 5.2.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java index e6ff8222ab4..f522b95a2d2 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java @@ -64,7 +64,8 @@ public enum VersionEnum { V5_0_0, V5_0_1, V5_0_2, - V5_1_0; + V5_1_0, + V5_2_0; public static VersionEnum latestVersion() { VersionEnum[] values = VersionEnum.values(); diff --git a/hapi-fhir-bom/pom.xml b/hapi-fhir-bom/pom.xml index d8994ffa015..fb1b44a18b3 100644 --- a/hapi-fhir-bom/pom.xml +++ b/hapi-fhir-bom/pom.xml @@ -3,14 +3,14 @@ 4.0.0 ca.uhn.hapi.fhir hapi-fhir-bom - 5.1.0 + 5.2.0-SNAPSHOT pom HAPI FHIR BOM ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0 + 5.2.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml index 3c442ca1ea2..2a26f67a435 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0 + 5.2.0-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml index 69a80ad2e60..1d2cc869360 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir-cli - 5.1.0 + 5.2.0-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml index a2396064cee..912abaffdf6 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0 + 5.2.0-SNAPSHOT ../../hapi-deployable-pom diff --git a/hapi-fhir-cli/pom.xml b/hapi-fhir-cli/pom.xml index 70e01ef3a02..3034ea56ad4 100644 --- a/hapi-fhir-cli/pom.xml +++ b/hapi-fhir-cli/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.1.0 + 5.2.0-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-client-okhttp/pom.xml b/hapi-fhir-client-okhttp/pom.xml index 94f0320bfaf..d1822c5e435 100644 --- a/hapi-fhir-client-okhttp/pom.xml +++ b/hapi-fhir-client-okhttp/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0 + 5.2.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-client/pom.xml b/hapi-fhir-client/pom.xml index eced316e8dc..cc01952b9bf 100644 --- a/hapi-fhir-client/pom.xml +++ b/hapi-fhir-client/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0 + 5.2.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-converter/pom.xml b/hapi-fhir-converter/pom.xml index b4223f3ae3b..527b69b5f03 100644 --- a/hapi-fhir-converter/pom.xml +++ b/hapi-fhir-converter/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0 + 5.2.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-dist/pom.xml b/hapi-fhir-dist/pom.xml index 28aea3eabbb..f3d4517912b 100644 --- a/hapi-fhir-dist/pom.xml +++ b/hapi-fhir-dist/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.1.0 + 5.2.0-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-docs/pom.xml b/hapi-fhir-docs/pom.xml index 26d3b777506..a677e4a818f 100644 --- a/hapi-fhir-docs/pom.xml +++ b/hapi-fhir-docs/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0 + 5.2.0-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -73,13 +73,13 @@ ca.uhn.hapi.fhir hapi-fhir-structures-dstu2 - 5.1.0 + 5.2.0-SNAPSHOT compile ca.uhn.hapi.fhir hapi-fhir-jpaserver-subscription - 5.1.0 + 5.2.0-SNAPSHOT compile @@ -96,7 +96,7 @@ ca.uhn.hapi.fhir hapi-fhir-testpage-overlay - 5.1.0 + 5.2.0-SNAPSHOT classes diff --git a/hapi-fhir-elasticsearch-6/pom.xml b/hapi-fhir-elasticsearch-6/pom.xml index ef9b8bcf6ea..c6cb4b94077 100644 --- a/hapi-fhir-elasticsearch-6/pom.xml +++ b/hapi-fhir-elasticsearch-6/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0 + 5.2.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-igpacks/pom.xml b/hapi-fhir-igpacks/pom.xml index 236f4946ac8..cf3e52a767b 100644 --- a/hapi-fhir-igpacks/pom.xml +++ b/hapi-fhir-igpacks/pom.xml @@ -5,7 +5,7 @@ hapi-deployable-pom ca.uhn.hapi.fhir - 5.1.0 + 5.2.0-SNAPSHOT ../hapi-deployable-pom/pom.xml 4.0.0 diff --git a/hapi-fhir-jacoco/pom.xml b/hapi-fhir-jacoco/pom.xml index 93477cc6493..ec880668d86 100644 --- a/hapi-fhir-jacoco/pom.xml +++ b/hapi-fhir-jacoco/pom.xml @@ -11,7 +11,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0 + 5.2.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jaxrsserver-base/pom.xml b/hapi-fhir-jaxrsserver-base/pom.xml index f8dd624af87..663b4f2399c 100644 --- a/hapi-fhir-jaxrsserver-base/pom.xml +++ b/hapi-fhir-jaxrsserver-base/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0 + 5.2.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jaxrsserver-example/pom.xml b/hapi-fhir-jaxrsserver-example/pom.xml index 8f899ec3d4b..3ad4c277bc0 100644 --- a/hapi-fhir-jaxrsserver-example/pom.xml +++ b/hapi-fhir-jaxrsserver-example/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.1.0 + 5.2.0-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-jpaserver-api/pom.xml b/hapi-fhir-jpaserver-api/pom.xml index af4ff0c90dc..1b5c56ba17c 100644 --- a/hapi-fhir-jpaserver-api/pom.xml +++ b/hapi-fhir-jpaserver-api/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0 + 5.2.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-base/pom.xml b/hapi-fhir-jpaserver-base/pom.xml index afaae1146df..c7b123f83f8 100644 --- a/hapi-fhir-jpaserver-base/pom.xml +++ b/hapi-fhir-jpaserver-base/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0 + 5.2.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-batch/pom.xml b/hapi-fhir-jpaserver-batch/pom.xml index a4a9292e818..bc7b9f593d2 100644 --- a/hapi-fhir-jpaserver-batch/pom.xml +++ b/hapi-fhir-jpaserver-batch/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0 + 5.2.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-empi/pom.xml b/hapi-fhir-jpaserver-empi/pom.xml index f3ccc7da449..f657cd065d9 100644 --- a/hapi-fhir-jpaserver-empi/pom.xml +++ b/hapi-fhir-jpaserver-empi/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0 + 5.2.0-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -55,13 +55,13 @@ ca.uhn.hapi.fhir hapi-fhir-test-utilities - 5.1.0 + 5.2.0-SNAPSHOT test ca.uhn.hapi.fhir hapi-fhir-jpaserver-test-utilities - 5.1.0 + 5.2.0-SNAPSHOT test diff --git a/hapi-fhir-jpaserver-migrate/pom.xml b/hapi-fhir-jpaserver-migrate/pom.xml index 9779c2a6830..ffc9eabb3a6 100644 --- a/hapi-fhir-jpaserver-migrate/pom.xml +++ b/hapi-fhir-jpaserver-migrate/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0 + 5.2.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-model/pom.xml b/hapi-fhir-jpaserver-model/pom.xml index e5f567e1fc9..c31d6607a05 100644 --- a/hapi-fhir-jpaserver-model/pom.xml +++ b/hapi-fhir-jpaserver-model/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0 + 5.2.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-searchparam/pom.xml b/hapi-fhir-jpaserver-searchparam/pom.xml index 5daf00da514..7891d8247d5 100755 --- a/hapi-fhir-jpaserver-searchparam/pom.xml +++ b/hapi-fhir-jpaserver-searchparam/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0 + 5.2.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-subscription/pom.xml b/hapi-fhir-jpaserver-subscription/pom.xml index 940d2a36063..8958aa6ad1d 100644 --- a/hapi-fhir-jpaserver-subscription/pom.xml +++ b/hapi-fhir-jpaserver-subscription/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0 + 5.2.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-utilities/pom.xml b/hapi-fhir-jpaserver-test-utilities/pom.xml index e06b8cb655d..aa04dd0888f 100644 --- a/hapi-fhir-jpaserver-test-utilities/pom.xml +++ b/hapi-fhir-jpaserver-test-utilities/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0 + 5.2.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml index a63771d518b..2f3c11b3f7c 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml +++ b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.1.0 + 5.2.0-SNAPSHOT ../pom.xml @@ -157,7 +157,7 @@ ca.uhn.hapi.fhir hapi-fhir-converter - 5.1.0 + 5.2.0-SNAPSHOT diff --git a/hapi-fhir-server-empi/pom.xml b/hapi-fhir-server-empi/pom.xml index 5ae13568421..b9759c1dda8 100644 --- a/hapi-fhir-server-empi/pom.xml +++ b/hapi-fhir-server-empi/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0 + 5.2.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server/pom.xml b/hapi-fhir-server/pom.xml index f15f3b77085..e57afa70464 100644 --- a/hapi-fhir-server/pom.xml +++ b/hapi-fhir-server/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0 + 5.2.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml index 2d5a11dfe06..7afdd3f9976 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0 + 5.2.0-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml index f05e1f154b6..be4e5406fc7 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 5.1.0 + 5.2.0-SNAPSHOT hapi-fhir-spring-boot-sample-client-apache diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml index 9b5b91f81d6..d29270ada82 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 5.1.0 + 5.2.0-SNAPSHOT hapi-fhir-spring-boot-sample-client-okhttp diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml index 08dd28eed34..d450f6b60b5 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 5.1.0 + 5.2.0-SNAPSHOT hapi-fhir-spring-boot-sample-server-jersey diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jpa/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jpa/pom.xml index e836f0b9980..896111a3e97 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jpa/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jpa/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 5.1.0 + 5.2.0-SNAPSHOT hapi-fhir-spring-boot-sample-server-jpa diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml index 1ffefc56198..143a8cc98b0 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot - 5.1.0 + 5.2.0-SNAPSHOT hapi-fhir-spring-boot-samples diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml index 32a32dde31e..f773d09efd8 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0 + 5.2.0-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/pom.xml b/hapi-fhir-spring-boot/pom.xml index 87069fdbb0c..5231c4407b3 100644 --- a/hapi-fhir-spring-boot/pom.xml +++ b/hapi-fhir-spring-boot/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.1.0 + 5.2.0-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-structures-dstu2.1/pom.xml b/hapi-fhir-structures-dstu2.1/pom.xml index 380725a74a4..f79133f2e72 100644 --- a/hapi-fhir-structures-dstu2.1/pom.xml +++ b/hapi-fhir-structures-dstu2.1/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0 + 5.2.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu2/pom.xml b/hapi-fhir-structures-dstu2/pom.xml index ebc8e726074..0b2352a3ffa 100644 --- a/hapi-fhir-structures-dstu2/pom.xml +++ b/hapi-fhir-structures-dstu2/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0 + 5.2.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu3/pom.xml b/hapi-fhir-structures-dstu3/pom.xml index f0e8bf36afc..7e6a2c78f03 100644 --- a/hapi-fhir-structures-dstu3/pom.xml +++ b/hapi-fhir-structures-dstu3/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0 + 5.2.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-hl7org-dstu2/pom.xml b/hapi-fhir-structures-hl7org-dstu2/pom.xml index b37bce27968..d12aa7952d5 100644 --- a/hapi-fhir-structures-hl7org-dstu2/pom.xml +++ b/hapi-fhir-structures-hl7org-dstu2/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0 + 5.2.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4/pom.xml b/hapi-fhir-structures-r4/pom.xml index df6fa24f72a..37bbf0c32d1 100644 --- a/hapi-fhir-structures-r4/pom.xml +++ b/hapi-fhir-structures-r4/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0 + 5.2.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r5/pom.xml b/hapi-fhir-structures-r5/pom.xml index 894a268f782..7f631e12f32 100644 --- a/hapi-fhir-structures-r5/pom.xml +++ b/hapi-fhir-structures-r5/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0 + 5.2.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-test-utilities/pom.xml b/hapi-fhir-test-utilities/pom.xml index 9cda17b9a7e..0838d2e7d1a 100644 --- a/hapi-fhir-test-utilities/pom.xml +++ b/hapi-fhir-test-utilities/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0 + 5.2.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-testpage-overlay/pom.xml b/hapi-fhir-testpage-overlay/pom.xml index b27ff765464..59023100bcb 100644 --- a/hapi-fhir-testpage-overlay/pom.xml +++ b/hapi-fhir-testpage-overlay/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.1.0 + 5.2.0-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-validation-resources-dstu2.1/pom.xml b/hapi-fhir-validation-resources-dstu2.1/pom.xml index 23eda4e7ecd..ac6d12d1177 100644 --- a/hapi-fhir-validation-resources-dstu2.1/pom.xml +++ b/hapi-fhir-validation-resources-dstu2.1/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0 + 5.2.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-dstu2/pom.xml b/hapi-fhir-validation-resources-dstu2/pom.xml index 61a332c3f9e..36edd948d51 100644 --- a/hapi-fhir-validation-resources-dstu2/pom.xml +++ b/hapi-fhir-validation-resources-dstu2/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0 + 5.2.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-dstu3/pom.xml b/hapi-fhir-validation-resources-dstu3/pom.xml index 281134c803d..15b320aded4 100644 --- a/hapi-fhir-validation-resources-dstu3/pom.xml +++ b/hapi-fhir-validation-resources-dstu3/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0 + 5.2.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-r4/pom.xml b/hapi-fhir-validation-resources-r4/pom.xml index 8652a978c5d..2a60ccd16c3 100644 --- a/hapi-fhir-validation-resources-r4/pom.xml +++ b/hapi-fhir-validation-resources-r4/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0 + 5.2.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-r5/pom.xml b/hapi-fhir-validation-resources-r5/pom.xml index ddb281a6782..72a869a7a59 100644 --- a/hapi-fhir-validation-resources-r5/pom.xml +++ b/hapi-fhir-validation-resources-r5/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0 + 5.2.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml index 0421ac5061b..8f4c0c9cf63 100644 --- a/hapi-fhir-validation/pom.xml +++ b/hapi-fhir-validation/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.1.0 + 5.2.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-tinder-plugin/pom.xml b/hapi-tinder-plugin/pom.xml index 2282b5cbb50..650f877c7df 100644 --- a/hapi-tinder-plugin/pom.xml +++ b/hapi-tinder-plugin/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.1.0 + 5.2.0-SNAPSHOT ../pom.xml @@ -58,37 +58,37 @@ ca.uhn.hapi.fhir hapi-fhir-structures-dstu3 - 5.1.0 + 5.2.0-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-structures-hl7org-dstu2 - 5.1.0 + 5.2.0-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-structures-r4 - 5.1.0 + 5.2.0-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-structures-r5 - 5.1.0 + 5.2.0-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-validation-resources-dstu2 - 5.1.0 + 5.2.0-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-validation-resources-dstu3 - 5.1.0 + 5.2.0-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-validation-resources-r4 - 5.1.0 + 5.2.0-SNAPSHOT org.apache.velocity diff --git a/hapi-tinder-test/pom.xml b/hapi-tinder-test/pom.xml index cab3fe9a4da..8c83b8dacbd 100644 --- a/hapi-tinder-test/pom.xml +++ b/hapi-tinder-test/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.1.0 + 5.2.0-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index 572797c55f0..f19cc7e4789 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir pom - 5.1.0 + 5.2.0-SNAPSHOT HAPI-FHIR An open-source implementation of the FHIR specification in Java. https://hapifhir.io diff --git a/restful-server-example/pom.xml b/restful-server-example/pom.xml index 006fd0dd584..647c5676335 100644 --- a/restful-server-example/pom.xml +++ b/restful-server-example/pom.xml @@ -8,7 +8,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.1.0 + 5.2.0-SNAPSHOT ../pom.xml diff --git a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml index d87686f5550..54aae250dc9 100644 --- a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml +++ b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.1.0 + 5.2.0-SNAPSHOT ../../pom.xml diff --git a/tests/hapi-fhir-base-test-mindeps-client/pom.xml b/tests/hapi-fhir-base-test-mindeps-client/pom.xml index cf2f2313c8d..ccffbd5aa4e 100644 --- a/tests/hapi-fhir-base-test-mindeps-client/pom.xml +++ b/tests/hapi-fhir-base-test-mindeps-client/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.1.0 + 5.2.0-SNAPSHOT ../../pom.xml diff --git a/tests/hapi-fhir-base-test-mindeps-server/pom.xml b/tests/hapi-fhir-base-test-mindeps-server/pom.xml index 1cf21b861b6..a22c7e9f6fb 100644 --- a/tests/hapi-fhir-base-test-mindeps-server/pom.xml +++ b/tests/hapi-fhir-base-test-mindeps-server/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.1.0 + 5.2.0-SNAPSHOT ../../pom.xml From d62e70c209a8b71b980e6d640ccacf42cba51e24 Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Thu, 13 Aug 2020 05:45:49 -0400 Subject: [PATCH 15/21] Add test --- .../rest/server/ServerExceptionDstu3Test.java | 92 +++++++++++++++++-- 1 file changed, 82 insertions(+), 10 deletions(-) diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ServerExceptionDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ServerExceptionDstu3Test.java index 0d9eb02415c..86163674490 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ServerExceptionDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ServerExceptionDstu3Test.java @@ -1,12 +1,14 @@ package ca.uhn.fhir.rest.server; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.interceptor.api.HookParams; +import ca.uhn.fhir.interceptor.api.IAnonymousInterceptor; +import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.rest.annotation.Create; import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; -import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.test.utilities.JettyUtil; @@ -29,11 +31,13 @@ import org.hl7.fhir.dstu3.model.OperationOutcome.IssueType; import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; @@ -44,11 +48,17 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class ServerExceptionDstu3Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerExceptionDstu3Test.class); - public static BaseServerResponseException ourException; + public static Exception ourException; private static CloseableHttpClient ourClient; private static FhirContext ourCtx = FhirContext.forDstu3(); private static int ourPort; private static Server ourServer; + private static RestfulServer ourServlet; + + @AfterEach + public void after() { + ourException = null; + } @Test public void testAddHeadersNotFound() throws Exception { @@ -56,8 +66,8 @@ public class ServerExceptionDstu3Test { OperationOutcome operationOutcome = new OperationOutcome(); operationOutcome.addIssue().setCode(IssueType.BUSINESSRULE); - ourException = new ResourceNotFoundException("SOME MESSAGE"); - ourException.addResponseHeader("X-Foo", "BAR BAR"); + ourException = new ResourceNotFoundException("SOME MESSAGE") + .addResponseHeader("X-Foo", "BAR BAR"); HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); @@ -99,6 +109,65 @@ public class ServerExceptionDstu3Test { } + @Test + public void testMethodThrowsNonHapiUncheckedExceptionHandledCleanly() throws Exception { + + ourException = new NullPointerException("Hello"); + + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=json"); + try (CloseableHttpResponse status = ourClient.execute(httpGet)) { + assertEquals(500, status.getStatusLine().getStatusCode()); + byte[] responseContentBytes = IOUtils.toByteArray(status.getEntity().getContent()); + String responseContent = new String(responseContentBytes, Charsets.UTF_8); + ourLog.info(status.getStatusLine().toString()); + ourLog.info(responseContent); + assertThat(responseContent, containsString("\"diagnostics\":\"Failed to call access method: java.lang.NullPointerException: Hello\"")); + } + + } + + @Test + public void testMethodThrowsNonHapiCheckedExceptionHandledCleanly() throws Exception { + + ourException = new IOException("Hello"); + + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=json"); + try (CloseableHttpResponse status = ourClient.execute(httpGet)) { + assertEquals(500, status.getStatusLine().getStatusCode()); + byte[] responseContentBytes = IOUtils.toByteArray(status.getEntity().getContent()); + String responseContent = new String(responseContentBytes, Charsets.UTF_8); + ourLog.info(status.getStatusLine().toString()); + ourLog.info(responseContent); + assertThat(responseContent, containsString("\"diagnostics\":\"Failed to call access method: java.io.IOException: Hello\"")); + } + + } + + @Test + public void testInterceptorThrowsNonHapiUncheckedExceptionHandledCleanly() throws Exception { + + ourServlet.getInterceptorService().registerAnonymousInterceptor(Pointcut.SERVER_INCOMING_REQUEST_PRE_HANDLED, new IAnonymousInterceptor() { + @Override + public void invoke(Pointcut thePointcut, HookParams theArgs) { + throw new NullPointerException("Hello"); + } + }); + + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=json"); + try (CloseableHttpResponse status = ourClient.execute(httpGet)) { + assertEquals(500, status.getStatusLine().getStatusCode()); + byte[] responseContentBytes = IOUtils.toByteArray(status.getEntity().getContent()); + String responseContent = new String(responseContentBytes, Charsets.UTF_8); + ourLog.info(status.getStatusLine().toString()); + ourLog.info(responseContent); + assertThat(responseContent, containsString("\"diagnostics\":\"Hello\"")); + } + + ourServlet.getInterceptorService().unregisterAllInterceptors(); + + } + + @Test public void testPostWithNoBody() throws IOException { @@ -143,7 +212,10 @@ public class ServerExceptionDstu3Test { } @Search() - public List search() { + public List search() throws Exception { + if (ourException == null) { + return Collections.emptyList(); + } throw ourException; } @@ -168,15 +240,15 @@ public class ServerExceptionDstu3Test { DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); + ourServlet = new RestfulServer(ourCtx); + ourServlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); + ourServlet.setResourceProviders(patientProvider); + ServletHolder servletHolder = new ServletHolder(ourServlet); proxyHandler.addServletWithMapping(servletHolder, "/*"); ourServer.setHandler(proxyHandler); JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); + ourPort = JettyUtil.getPortForStartedServer(ourServer); PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); HttpClientBuilder builder = HttpClientBuilder.create(); From f3d5b69da028bdc36129cc4584d1e222814fcb44 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Aug 2020 05:47:43 -0400 Subject: [PATCH 16/21] Bump flyway-core from 6.4.1 to 6.5.4 (#2039) Bumps [flyway-core](https://github.com/flyway/flyway) from 6.4.1 to 6.5.4. - [Release notes](https://github.com/flyway/flyway/releases) - [Commits](https://github.com/flyway/flyway/compare/flyway-6.4.1...flyway-6.5.4) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f19cc7e4789..502cc36b392 100644 --- a/pom.xml +++ b/pom.xml @@ -717,7 +717,7 @@ 9.4.30.v20200611 3.0.2 - 6.4.1 + 6.5.4 5.4.14.Final From 89f68353eed8f6c13311fa2c29921c80c242701e Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Thu, 13 Aug 2020 05:47:56 -0400 Subject: [PATCH 17/21] Add changelog --- .../ca/uhn/hapi/fhir/changelog/5_2_0/changes.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_2_0/changes.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_2_0/changes.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_2_0/changes.yaml new file mode 100644 index 00000000000..2b3be2df14c --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_2_0/changes.yaml @@ -0,0 +1,8 @@ +--- +- item: + type: "add" + title: "The version of a few dependencies have been bumped to the latest versions + (dependent HAPI modules listed in brackets): +
    +
  • Flyway (JPA): 6.4.1-> 6.5.4
  • +
" From a3951b551fd72c32f5f52a09f8f4da68a1d45220 Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Thu, 13 Aug 2020 17:36:04 -0400 Subject: [PATCH 18/21] Add release details for 5.1.0 --- .../resources/ca/uhn/hapi/fhir/changelog/5_1_0/version.yaml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_1_0/version.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_1_0/version.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_1_0/version.yaml new file mode 100644 index 00000000000..8178f8fe4fb --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_1_0/version.yaml @@ -0,0 +1,3 @@ +--- +release-date: "2020-08-13" +codename: "Manticore" From cd65c298fe4d55d21dfaffc6e94a84f359867a9d Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Sun, 16 Aug 2020 09:01:42 -0400 Subject: [PATCH 19/21] Fix some changelog typos --- .../1856-avoid-xml-seserialization-issue-for-subs-delivery.yaml | 2 +- ...extension-metyods.yaml => 237-update-extension-methods.yaml} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_1_0/{237-update-extension-metyods.yaml => 237-update-extension-methods.yaml} (94%) diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_1_0/1856-avoid-xml-seserialization-issue-for-subs-delivery.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_1_0/1856-avoid-xml-seserialization-issue-for-subs-delivery.yaml index 77964969282..21e1630f358 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_1_0/1856-avoid-xml-seserialization-issue-for-subs-delivery.yaml +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_1_0/1856-avoid-xml-seserialization-issue-for-subs-delivery.yaml @@ -1,6 +1,6 @@ --- type: fix issue: 1856 -title: "The subscription delivery queue in the JPA server was erroniously keeping both a copy of the serialized and the +title: "The subscription delivery queue in the JPA server was erroneously keeping both a copy of the serialized and the deserialized payload in memory for each entry in the queue, doubling the memory requirements. This also caused failures when delivering XML payloads in some configurations. This has been corrected." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_1_0/237-update-extension-metyods.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_1_0/237-update-extension-methods.yaml similarity index 94% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_1_0/237-update-extension-metyods.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_1_0/237-update-extension-methods.yaml index 7b56f3c2a43..83e0e1a2296 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_1_0/237-update-extension-metyods.yaml +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_1_0/237-update-extension-methods.yaml @@ -1,7 +1,7 @@ --- type: change issue: 237 -title: "The R5 structure methods for working with extensions on arbtrary fields, e.g. +title: "The R5 structure methods for working with extensions on arbitrary fields, e.g. `getExtensionByUrl(String)`, `removeExtension(String)`, `getExtensionsByUrl(String)` `hasExtension(String)`, and `getExtensionString(String)` have been enhanced so that they now return modifier extensions as well as the non-modifier extensions they previously From 7dd7f2b92329b302717df05da60a7a53cbe46c1d Mon Sep 17 00:00:00 2001 From: Ken Stevens Date: Sun, 16 Aug 2020 20:48:39 -0400 Subject: [PATCH 20/21] resource pid userdata fix (#2044) * fixed resource UserData RESOURCE_PID for expunge hook (was incorrectly pointing to ResourceHistoryTable pid instead of ResourceTable pid. * fix merge --- .../java/ca/uhn/fhir/jpa/api/dao/IDao.java | 3 +- .../ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java | 7 +- .../provider/r4/HookInterceptorR4Test.java | 108 ++++++++++++++++-- 3 files changed, 101 insertions(+), 17 deletions(-) diff --git a/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IDao.java b/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IDao.java index bc9f9e12396..ca1c690f468 100644 --- a/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IDao.java +++ b/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IDao.java @@ -34,8 +34,9 @@ import java.util.Collection; * time to time, even within minor point releases. */ public interface IDao { + String RESOURCE_PID_KEY = "RESOURCE_PID"; - MetadataKeyResourcePid RESOURCE_PID = new MetadataKeyResourcePid("RESOURCE_PID"); + MetadataKeyResourcePid RESOURCE_PID = new MetadataKeyResourcePid(RESOURCE_PID_KEY); MetadataKeyCurrentlyReindexing CURRENTLY_REINDEXING = new MetadataKeyCurrentlyReindexing("CURRENTLY_REINDEXING"); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java index 0628ff48933..7afbb87d637 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java @@ -79,7 +79,6 @@ import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; -import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; @@ -114,10 +113,8 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Repository; import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionSynchronizationAdapter; import org.springframework.transaction.support.TransactionSynchronizationManager; -import org.springframework.transaction.support.TransactionTemplate; import javax.annotation.PostConstruct; import javax.persistence.EntityManager; @@ -636,7 +633,7 @@ public abstract class BaseHapiFhirDao extends BaseStora ResourceMetadataKeyEnum.VERSION.put(res, Long.toString(theEntity.getVersion())); ResourceMetadataKeyEnum.PUBLISHED.put(res, theEntity.getPublished()); ResourceMetadataKeyEnum.UPDATED.put(res, theEntity.getUpdated()); - IDao.RESOURCE_PID.put(res, theEntity.getId()); + IDao.RESOURCE_PID.put(res, theEntity.getResourceId()); Collection tags = theTagList; if (theEntity.isHasTags()) { @@ -708,7 +705,7 @@ public abstract class BaseHapiFhirDao extends BaseStora res.setId(res.getIdElement().withVersion(theVersion.toString())); res.getMeta().setLastUpdated(theEntity.getUpdatedDate()); - IDao.RESOURCE_PID.put(res, theEntity.getId()); + IDao.RESOURCE_PID.put(res, theEntity.getResourceId()); Collection tags = theTagList; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/HookInterceptorR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/HookInterceptorR4Test.java index 040655c5158..80857380f8d 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/HookInterceptorR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/HookInterceptorR4Test.java @@ -1,13 +1,25 @@ package ca.uhn.fhir.jpa.provider.r4; import ca.uhn.fhir.interceptor.api.Pointcut; +import ca.uhn.fhir.jpa.api.config.DaoConfig; +import ca.uhn.fhir.jpa.api.dao.IDao; +import ca.uhn.fhir.jpa.dao.index.IdHelperService; +import ca.uhn.fhir.jpa.model.util.JpaConstants; import ca.uhn.fhir.rest.api.MethodOutcome; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r4.model.BooleanType; +import org.hl7.fhir.r4.model.Parameters; import org.hl7.fhir.r4.model.Patient; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import java.util.Collections; import java.util.concurrent.atomic.AtomicLong; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -15,16 +27,24 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; public class HookInterceptorR4Test extends BaseResourceProviderR4Test { + private static final Logger ourLog = LoggerFactory.getLogger(HookInterceptorR4Test.class); - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(HookInterceptorR4Test.class); + @Autowired + IdHelperService myIdHelperService; -// @Override -// @AfterEach -// public void after( ) throws Exception { -// super.after(); -// -// myInterceptorRegistry.unregisterAllInterceptors(); -// } + @BeforeEach + public void before() throws Exception { + super.before(); + + myDaoConfig.setExpungeEnabled(true); + } + + @AfterEach + public void after() throws Exception { + myDaoConfig.setExpungeEnabled(new DaoConfig().isExpungeEnabled()); + + super.after(); + } @Test public void testOP_PRESTORAGE_RESOURCE_CREATED_ModifyResource() { @@ -69,7 +89,7 @@ public class HookInterceptorR4Test extends BaseResourceProviderR4Test { AtomicLong pid = new AtomicLong(); myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.STORAGE_PRECOMMIT_RESOURCE_CREATED, (thePointcut, t) -> { IAnyResource resource = (IAnyResource) t.get(IBaseResource.class, 0); - Long resourcePid = (Long) resource.getUserData("RESOURCE_PID"); + Long resourcePid = (Long) resource.getUserData(IDao.RESOURCE_PID_KEY); assertNotNull(resourcePid, "Expecting RESOURCE_PID to be set on resource user data."); pid.set(resourcePid); }); @@ -77,6 +97,72 @@ public class HookInterceptorR4Test extends BaseResourceProviderR4Test { assertTrue(pid.get() > 0); } + + @Test + public void testSTORAGE_PRECOMMIT_RESOURCE_CREATED_hasCorrectPid() { + AtomicLong pid = new AtomicLong(); + myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.STORAGE_PRECOMMIT_RESOURCE_CREATED, (thePointcut, t) -> { + IAnyResource resource = (IAnyResource) t.get(IBaseResource.class, 0); + Long resourcePid = (Long) resource.getUserData(IDao.RESOURCE_PID_KEY); + assertNotNull(resourcePid, "Expecting RESOURCE_PID to be set on resource user data."); + pid.set(resourcePid); + }); + IIdType savedPatientId = myClient.create().resource(new Patient()).execute().getId(); + Long savedPatientPid = myIdHelperService.resolveResourcePersistentIdsWithCache(null, Collections.singletonList(savedPatientId)).get(0).getIdAsLong(); + assertEquals(savedPatientPid.longValue(), pid.get()); + } + + @Test + public void testSTORAGE_PRESTORAGE_EXPUNGE_RESOURCE_hasCorrectPid() { + AtomicLong pid = new AtomicLong(); + myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.STORAGE_PRESTORAGE_EXPUNGE_RESOURCE, (thePointcut, t) -> { + IAnyResource resource = (IAnyResource) t.get(IBaseResource.class, 0); + Long resourcePid = (Long) resource.getUserData(IDao.RESOURCE_PID_KEY); + assertNotNull(resourcePid, "Expecting RESOURCE_PID to be set on resource user data."); + pid.set(resourcePid); + }); + IIdType savedPatientId = myClient.create().resource(new Patient()).execute().getId(); + Long savedPatientPid = myIdHelperService.resolveResourcePersistentIdsWithCache(null, Collections.singletonList(savedPatientId)).get(0).getIdAsLong(); + + myClient.delete().resourceById(savedPatientId).execute(); + Parameters parameters = new Parameters(); + + parameters.addParameter().setName(JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_DELETED_RESOURCES).setValue(new BooleanType(true)); + myClient + .operation() + .onInstance(savedPatientId) + .named(JpaConstants.OPERATION_EXPUNGE) + .withParameters(parameters) + .execute(); + + assertEquals(savedPatientPid.longValue(), pid.get()); + } + + + @Test + public void testSTORAGE_PRECOMMIT_RESOURCE_UPDATED_hasCorrectPid() { + AtomicLong pidOld = new AtomicLong(); + AtomicLong pidNew = new AtomicLong(); + Patient patient = new Patient(); + IIdType savedPatientId = myClient.create().resource(patient).execute().getId(); + patient.setId(savedPatientId); + myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.STORAGE_PRECOMMIT_RESOURCE_UPDATED, (thePointcut, t) -> { + IAnyResource resourceOld = (IAnyResource) t.get(IBaseResource.class, 0); + IAnyResource resourceNew = (IAnyResource) t.get(IBaseResource.class, 1); + Long resourceOldPid = (Long) resourceOld.getUserData(IDao.RESOURCE_PID_KEY); + Long resourceNewPid = (Long) resourceNew.getUserData(IDao.RESOURCE_PID_KEY); + assertNotNull(resourceOldPid, "Expecting RESOURCE_PID to be set on resource user data."); + assertNotNull(resourceNewPid, "Expecting RESOURCE_PID to be set on resource user data."); + pidOld.set(resourceOldPid); + pidNew.set(resourceNewPid); + }); + patient.setActive(true); + myClient.update().resource(patient).execute(); + Long savedPatientPid = myIdHelperService.resolveResourcePersistentIdsWithCache(null, Collections.singletonList(savedPatientId)).get(0).getIdAsLong(); + assertEquals(savedPatientPid.longValue(), pidOld.get()); + assertEquals(savedPatientPid.longValue(), pidNew.get()); + } + @Test public void testSTORAGE_PRECOMMIT_RESOURCE_UPDATED_hasPid() { AtomicLong oldPid = new AtomicLong(); @@ -84,12 +170,12 @@ public class HookInterceptorR4Test extends BaseResourceProviderR4Test { myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.STORAGE_PRECOMMIT_RESOURCE_UPDATED, (thePointcut, t) -> { IAnyResource oldResource = (IAnyResource) t.get(IBaseResource.class, 0); - Long oldResourcePid = (Long) oldResource.getUserData("RESOURCE_PID"); + Long oldResourcePid = (Long) oldResource.getUserData(IDao.RESOURCE_PID_KEY); assertNotNull(oldResourcePid, "Expecting RESOURCE_PID to be set on resource user data."); oldPid.set(oldResourcePid); IAnyResource newResource = (IAnyResource) t.get(IBaseResource.class, 1); - Long newResourcePid = (Long) newResource.getUserData("RESOURCE_PID"); + Long newResourcePid = (Long) newResource.getUserData(IDao.RESOURCE_PID_KEY); assertNotNull(newResourcePid, "Expecting RESOURCE_PID to be set on resource user data."); newPid.set(newResourcePid); }); From 3a5e0ed872c5f62a732d2ecde41ff9190a90232b Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Sun, 16 Aug 2020 21:03:27 -0400 Subject: [PATCH 21/21] Fix #2042 - Outdated CI link on website --- README.md | 2 +- .../ca/uhn/hapi/fhir/docs/contributing/hacking_guide.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bf7df70bf1c..351737d7722 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ HAPI FHIR HAPI FHIR - Java API for HL7 FHIR Clients and Servers -[![Build Status](https://dev.azure.com/jamesagnew214/jamesagnew214/_apis/build/status/jamesagnew.hapi-fhir?branchName=master)](https://dev.azure.com/jamesagnew214/jamesagnew214/_build/latest?definitionId=1&branchName=master) +[![Build Status](https://dev.azure.com/hapifhir/HAPI%20FHIR/_apis/build/status/jamesagnew.hapi-fhir?branchName=master)](https://dev.azure.com/hapifhir/HAPI%20FHIR/_build/latest?definitionId=1&branchName=master) [![codecov](https://codecov.io/gh/jamesagnew/hapi-fhir/branch/master/graph/badge.svg)](https://codecov.io/gh/jamesagnew/hapi-fhir) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/ca.uhn.hapi.fhir/hapi-fhir-base/badge.svg)](http://search.maven.org/#search|ga|1|ca.uhn.hapi.fhir) [![License](https://img.shields.io/badge/license-apache%202.0-60C060.svg)](https://hapifhir.io/hapi-fhir/license.html) diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/contributing/hacking_guide.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/contributing/hacking_guide.md index 69d9100446a..2c8d7b12074 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/contributing/hacking_guide.md +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/contributing/hacking_guide.md @@ -15,7 +15,7 @@ The following is a list of key subprojects you might open in your IDE: # Getting the Sources

- Build Status + Build Status

The best way to grab our sources is with Git. Grab the repository URL from our [GitHub page](https://github.com/jamesagnew/hapi-fhir). We try our best to ensure that the sources are always left in a buildable state. Check Azure Pipelines CI (see the image/link on the right) to see if the sources currently build.