From 63eed3936ba25464412509e976e19290760ab991 Mon Sep 17 00:00:00 2001
From: Taha
Date: Mon, 27 Nov 2023 20:12:35 -0800
Subject: [PATCH 01/14] (#5442) fetch should also resolve canonical URL
references (#5443)
* (#5442) fetch should also resolve canonical URL references
* (#5442) - added test
* (#5442) - cleanup?
* (#5442) - changelog and reorganize test
* (#5442) PR feedback
* (#5442) PR feedback
* (#5442) cleared ValidatorResourceFetcher linter warnings
* (#5442) - new error code
* (#5442) caught additional error
* (#5442) spotless apply
* (#5442) spotless apply
---------
Co-authored-by: taha.attari@smilecdr.com
---
...validation-fetcher-fetch-by-canonical.yaml | 4 +
.../fhir/jpa/api/dao/IFhirResourceDao.java | 21 +-
.../validation/ValidatorResourceFetcher.java | 60 +-
.../ValidatorResourceFetcherTest.java | 65 +
.../resources/q_jon_with_url_version.json | 1388 +++++++++++++++++
5 files changed, 1516 insertions(+), 22 deletions(-)
create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5442-validation-fetcher-fetch-by-canonical.yaml
create mode 100644 hapi-fhir-storage/src/test/java/ca/uhn/fhir/jpa/validation/ValidatorResourceFetcherTest.java
create mode 100644 hapi-fhir-storage/src/test/resources/q_jon_with_url_version.json
diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5442-validation-fetcher-fetch-by-canonical.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5442-validation-fetcher-fetch-by-canonical.yaml
new file mode 100644
index 00000000000..226bb7cd153
--- /dev/null
+++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5442-validation-fetcher-fetch-by-canonical.yaml
@@ -0,0 +1,4 @@
+---
+type: add
+issue: 5442
+title: "The ValidatorResourceFetcher will now resolve canonical URL references as well as simple local references."
diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDao.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDao.java
index 171e5cfeb8e..176983551fb 100644
--- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDao.java
+++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDao.java
@@ -327,18 +327,29 @@ public interface IFhirResourceDao extends IDao {
/**
* @deprecated Use {@link #search(SearchParameterMap, RequestDetails)} instead
+ * @throws InvalidRequestException If a SearchParameter is not known to the server
*/
- IBundleProvider search(SearchParameterMap theParams);
+ IBundleProvider search(SearchParameterMap theParams) throws InvalidRequestException;
- IBundleProvider search(SearchParameterMap theParams, RequestDetails theRequestDetails);
+ /**
+ * *
+ * @throws InvalidRequestException If a SearchParameter is not known to the server
+ */
+ IBundleProvider search(SearchParameterMap theParams, RequestDetails theRequestDetails)
+ throws InvalidRequestException;
+ /**
+ * *
+ * @throws InvalidRequestException If a SearchParameter is not known to the server
+ */
IBundleProvider search(
- SearchParameterMap theParams, RequestDetails theRequestDetails, HttpServletResponse theServletResponse);
+ SearchParameterMap theParams, RequestDetails theRequestDetails, HttpServletResponse theServletResponse)
+ throws InvalidRequestException;
/**
* Search for IDs for processing a match URLs, etc.
*/
- default List searchForIds(
+ default List searchForIds(
SearchParameterMap theParams, RequestDetails theRequest) {
return searchForIds(theParams, theRequest, null);
}
@@ -350,7 +361,7 @@ public interface IFhirResourceDao extends IDao {
* create/update, this is the resource being searched for
* @since 5.5.0
*/
- default List searchForIds(
+ default List searchForIds(
SearchParameterMap theParams,
RequestDetails theRequest,
@Nullable IBaseResource theConditionalOperationTargetOrNull) {
diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/validation/ValidatorResourceFetcher.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/validation/ValidatorResourceFetcher.java
index d7fc7b40ab7..f988532989a 100644
--- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/validation/ValidatorResourceFetcher.java
+++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/validation/ValidatorResourceFetcher.java
@@ -24,12 +24,14 @@ import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
+import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.rest.api.server.RequestDetails;
+import ca.uhn.fhir.rest.param.TokenParam;
+import ca.uhn.fhir.rest.param.UriParam;
+import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import org.hl7.fhir.common.hapi.validation.validator.VersionSpecificWorkerContextWrapper;
-import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
-import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r5.elementmodel.Element;
@@ -37,12 +39,11 @@ import org.hl7.fhir.r5.elementmodel.JsonParser;
import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.utils.validation.IResourceValidator;
import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher;
+import org.hl7.fhir.utilities.CanonicalPair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.io.IOException;
-import java.net.MalformedURLException;
-import java.net.URISyntaxException;
+import java.util.List;
import java.util.Locale;
public class ValidatorResourceFetcher implements IValidatorResourceFetcher {
@@ -50,22 +51,19 @@ public class ValidatorResourceFetcher implements IValidatorResourceFetcher {
private static final Logger ourLog = LoggerFactory.getLogger(ValidatorResourceFetcher.class);
private final FhirContext myFhirContext;
- private final IValidationSupport myValidationSupport;
private final DaoRegistry myDaoRegistry;
private final VersionSpecificWorkerContextWrapper myVersionSpecificContextWrapper;
public ValidatorResourceFetcher(
FhirContext theFhirContext, IValidationSupport theValidationSupport, DaoRegistry theDaoRegistry) {
myFhirContext = theFhirContext;
- myValidationSupport = theValidationSupport;
myDaoRegistry = theDaoRegistry;
myVersionSpecificContextWrapper =
- VersionSpecificWorkerContextWrapper.newVersionSpecificWorkerContextWrapper(myValidationSupport);
+ VersionSpecificWorkerContextWrapper.newVersionSpecificWorkerContextWrapper(theValidationSupport);
}
@Override
- public Element fetch(IResourceValidator iResourceValidator, Object appContext, String theUrl)
- throws FHIRFormatError, DefinitionException, FHIRException, IOException {
+ public Element fetch(IResourceValidator iResourceValidator, Object appContext, String theUrl) throws FHIRException {
IdType id = new IdType(theUrl);
String resourceType = id.getResourceType();
IFhirResourceDao> dao = myDaoRegistry.getResourceDao(resourceType);
@@ -74,9 +72,13 @@ public class ValidatorResourceFetcher implements IValidatorResourceFetcher {
target = dao.read(id, (RequestDetails) appContext);
} catch (ResourceNotFoundException e) {
ourLog.info("Failed to resolve local reference: {}", theUrl);
- return null;
+ try {
+ target = fetchByUrl(theUrl, dao, (RequestDetails) appContext);
+ } catch (ResourceNotFoundException e2) {
+ ourLog.info("Failed to find resource by URL: {}", theUrl);
+ return null;
+ }
}
-
try {
return new JsonParser(myVersionSpecificContextWrapper)
.parse(myFhirContext.newJsonParser().encodeResourceToString(target), resourceType);
@@ -85,15 +87,40 @@ public class ValidatorResourceFetcher implements IValidatorResourceFetcher {
}
}
+ private IBaseResource fetchByUrl(String url, IFhirResourceDao> dao, RequestDetails requestDetails)
+ throws ResourceNotFoundException {
+ CanonicalPair pair = new CanonicalPair(url);
+ SearchParameterMap searchParameterMap = new SearchParameterMap();
+ searchParameterMap.add("url", new UriParam(pair.getUrl()));
+ String version = pair.getVersion();
+ if (version != null && !version.isEmpty()) {
+ searchParameterMap.add("version", new TokenParam(version));
+ }
+ List results = null;
+ try {
+ results = dao.search(searchParameterMap, requestDetails).getAllResources();
+ } catch (InvalidRequestException e) {
+ ourLog.info("Resource does not support 'url' or 'version' Search Parameters");
+ }
+ if (results != null && results.size() > 0) {
+ if (results.size() > 1) {
+ ourLog.warn(
+ String.format("Multiple results found for URL '%s', only the first will be considered.", url));
+ }
+ return results.get(0);
+ } else {
+ throw new ResourceNotFoundException(Msg.code(2444) + "Failed to find resource by URL: " + url);
+ }
+ }
+
@Override
public boolean resolveURL(
- IResourceValidator iResourceValidator, Object o, String s, String s1, String s2, boolean isCanonical)
- throws IOException, FHIRException {
+ IResourceValidator iResourceValidator, Object o, String s, String s1, String s2, boolean isCanonical) {
return true;
}
@Override
- public byte[] fetchRaw(IResourceValidator iResourceValidator, String s) throws MalformedURLException, IOException {
+ public byte[] fetchRaw(IResourceValidator iResourceValidator, String s) throws UnsupportedOperationException {
throw new UnsupportedOperationException(Msg.code(577));
}
@@ -104,8 +131,7 @@ public class ValidatorResourceFetcher implements IValidatorResourceFetcher {
}
@Override
- public CanonicalResource fetchCanonicalResource(IResourceValidator iResourceValidator, String s)
- throws URISyntaxException {
+ public CanonicalResource fetchCanonicalResource(IResourceValidator iResourceValidator, String s) {
return null;
}
diff --git a/hapi-fhir-storage/src/test/java/ca/uhn/fhir/jpa/validation/ValidatorResourceFetcherTest.java b/hapi-fhir-storage/src/test/java/ca/uhn/fhir/jpa/validation/ValidatorResourceFetcherTest.java
new file mode 100644
index 00000000000..9009f6137fa
--- /dev/null
+++ b/hapi-fhir-storage/src/test/java/ca/uhn/fhir/jpa/validation/ValidatorResourceFetcherTest.java
@@ -0,0 +1,65 @@
+package ca.uhn.fhir.jpa.validation;
+
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
+import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
+import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
+import ca.uhn.fhir.rest.api.server.RequestDetails;
+import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
+import ca.uhn.fhir.rest.server.SimpleBundleProvider;
+import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
+import ca.uhn.fhir.test.BaseTest;
+import ca.uhn.fhir.util.ClasspathUtil;
+import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator;
+import org.hl7.fhir.common.hapi.validation.validator.VersionSpecificWorkerContextWrapper;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.hl7.fhir.r5.elementmodel.Element;
+import org.hl7.fhir.r5.utils.XVerExtensionManager;
+import org.hl7.fhir.validation.instance.InstanceValidator;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+
+
+public class ValidatorResourceFetcherTest extends BaseTest {
+ private static final FhirContext ourCtx = FhirContext.forR4();
+ private static final DefaultProfileValidationSupport myDefaultValidationSupport = new DefaultProfileValidationSupport(ourCtx);
+ private static ValidatorResourceFetcher fetcher;
+ private static DaoRegistry mockDaoRegistry;
+ private static IFhirResourceDao mockResourceDao;
+
+ @SuppressWarnings("unchecked")
+ @BeforeEach
+ public void before() {
+ mockDaoRegistry = mock(DaoRegistry.class);
+ mockResourceDao = mock(IFhirResourceDao.class);
+ fetcher = new ValidatorResourceFetcher(ourCtx, myDefaultValidationSupport, mockDaoRegistry);
+ }
+
+ @Test
+ public void checkFetchByUrl() {
+ // setup mocks
+ String resource = ClasspathUtil.loadResource("/q_jon_with_url_version.json");
+ doReturn(mockResourceDao).when(mockDaoRegistry).getResourceDao("Questionnaire");
+ doThrow(new ResourceNotFoundException("Not Found")).when(mockResourceDao).read(any(),any());
+ doReturn(new SimpleBundleProvider(List.of(
+ ourCtx.newJsonParser().parseResource(resource)
+ ))).when(mockResourceDao).search(any(),any());
+ VersionSpecificWorkerContextWrapper wrappedWorkerContext = VersionSpecificWorkerContextWrapper.newVersionSpecificWorkerContextWrapper(myDefaultValidationSupport);
+ InstanceValidator v = new InstanceValidator(
+ wrappedWorkerContext,
+ new FhirInstanceValidator.NullEvaluationContext(),
+ new XVerExtensionManager(null));
+ RequestDetails r = new SystemRequestDetails();
+ // test
+ Element returnedResource = fetcher.fetch(v, r,"http://www.test-url-for-questionnaire.com/Questionnaire/test-id|1.0.0");
+ assertNotNull(returnedResource);
+ }
+}
diff --git a/hapi-fhir-storage/src/test/resources/q_jon_with_url_version.json b/hapi-fhir-storage/src/test/resources/q_jon_with_url_version.json
new file mode 100644
index 00000000000..7ca33e5e290
--- /dev/null
+++ b/hapi-fhir-storage/src/test/resources/q_jon_with_url_version.json
@@ -0,0 +1,1388 @@
+{
+ "resourceType": "Questionnaire",
+ "status": "draft",
+ "date": "2015-10-29",
+ "publisher": "Cancer Care Ontario",
+ "id": "test-id",
+ "telecom": [
+ {
+ "system": "email",
+ "value": "jon.zammit@cancercare.on.ca"
+ }
+ ],
+ "title": "CT Lung for Cancer Staging Template - DRAFT (Short Version)",
+ "url": "http://www.test-url-for-questionnaire.com/Questionnaire/test-id",
+ "version": "1.0.0",
+ "item": [
+ {
+ "text": "Patient with high suspicion of cancer as per the PEBC document (EBS #24-2) or radiological/laboratory tests suggesting cancer. Excluding: patients with synchronous lung primary, previous diagnosis of lung cancer, lung cancer surgery or therapy. New single lung primary only.",
+ "type": "display"
+ },
+ {
+ "linkId": "root",
+ "type": "group",
+ "required": true,
+ "item": [
+ {
+ "linkId": "g1",
+ "concept": [
+ {
+ "system": "http://loinc.org",
+ "code": "55752-0",
+ "display": "Clinical Information"
+ }
+ ],
+ "text": "CLINICAL INFORMATION",
+ "type": "group",
+ "item": [
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label",
+ "valueString": "1.1"
+ }
+ ],
+ "linkId": "1.1",
+ "text": "Patient Clinical Information",
+ "type": "text"
+ },
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label",
+ "valueString": "1.2"
+ }
+ ],
+ "linkId": "1.2",
+ "text": "Previous Examination (Date and Modality)",
+ "type": "text"
+ }
+ ]
+ },
+ {
+ "linkId": "g2",
+ "text": "IMAGING PROCEDURE DESCRIPTION",
+ "type": "group",
+ "item": [
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label",
+ "valueString": "2.1"
+ }
+ ],
+ "linkId": "2.1",
+ "text": "Overall Image Quality:",
+ "type": "choice",
+ "option": [
+ {
+ "valueCoding": {
+ "code": "2.1a",
+ "display": "Adequate"
+ }
+ },
+ {
+ "valueCoding": {
+ "code": "2.1b",
+ "display": "Suboptimal"
+ }
+ },
+ {
+ "valueCoding": {
+ "code": "2.1c",
+ "display": "Non-diagnostic"
+ }
+ }
+ ]
+ },
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label",
+ "valueString": "2.2"
+ }
+ ],
+ "linkId": "2.2",
+ "text": "Intravenous Contrast Used?",
+ "type": "boolean"
+ },
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label",
+ "valueString": "2.3"
+ }
+ ],
+ "linkId": "2.3",
+ "text": "Additional Comments",
+ "type": "text"
+ }
+ ]
+ },
+ {
+ "linkId": "g3",
+ "text": "FINDINGS",
+ "type": "group",
+ "item": [
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label",
+ "valueString": "3"
+ }
+ ],
+ "linkId": "g3.0",
+ "text": "T Category",
+ "type": "group",
+ "item": [
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label",
+ "valueString": "3.1"
+ }
+ ],
+ "linkId": "g3.1",
+ "text": "Location of Main Nodule/Mass (Primary tumor, or Reference tumor)",
+ "type": "group",
+ "item": [
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label",
+ "valueString": "3.1.1"
+ }
+ ],
+ "linkId": "q3.1.1",
+ "text": "Location of main nodule/mass:",
+ "type": "choice",
+ "option": [
+ {
+ "valueCoding": {
+ "code": "3.1.1a",
+ "display": "Peripheral"
+ }
+ },
+ {
+ "valueCoding": {
+ "code": "3.1.1b",
+ "display": "Central*"
+ }
+ },
+ {
+ "valueCoding": {
+ "code": "3.1.1c",
+ "display": "Both*"
+ }
+ }
+ ],
+ "item": [
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen",
+ "extension": [
+ {
+ "url": "#question",
+ "valueString": "q3.1.1"
+ },
+ {
+ "url": "#answer",
+ "valueCoding": {
+ "code": "3.1.1b"
+ }
+ }
+ ]
+ },
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen",
+ "extension": [
+ {
+ "url": "#question",
+ "valueString": "q3.1.1"
+ },
+ {
+ "url": "#answer",
+ "valueCoding": {
+ "code": "3.1.1c"
+ }
+ }
+ ]
+ }
+ ],
+ "linkId": "g3.1.1i",
+ "type": "group",
+ "item": [
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label",
+ "valueString": "i)"
+ }
+ ],
+ "linkId": "q3.1.1i",
+ "text": "What is the distance of the nodule/mass to the carina?",
+ "type": "integer",
+ "item": [
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl",
+ "valueCodeableConcept": {
+ "coding": [
+ {
+ "system": "http://hl7.org/fhir/ValueSet/questionnaire-item-control",
+ "code": "unit"
+ }
+ ]
+ }
+ }
+ ],
+ "text": "mm",
+ "type": "display"
+ }
+ ]
+ },
+ {
+ "linkId": "3.1.1i.image",
+ "text": "image",
+ "type": "string"
+ },
+ {
+ "linkId": "3.1.1i.series",
+ "text": "series",
+ "type": "string"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label",
+ "valueString": "3.1.2"
+ }
+ ],
+ "linkId": "3.1.2",
+ "text": "State the lobe(s) and segment(s) where the nodule/mass is located",
+ "type": "text"
+ }
+ ]
+ },
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label",
+ "valueString": "3.2"
+ }
+ ],
+ "linkId": "g3.2",
+ "text": "Size and characteristics of main nodule/mass",
+ "type": "group",
+ "item": [
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label",
+ "valueString": "3.2.1"
+ }
+ ],
+ "linkId": "3.2.1",
+ "text": "Size of the nodule/mass:",
+ "type": "choice",
+ "option": [
+ {
+ "valueCoding": {
+ "code": "3.2.1a",
+ "display": "Solid nodule/mass"
+ }
+ },
+ {
+ "valueCoding": {
+ "code": "3.2.1b",
+ "display": "Part-solid nodule/mass"
+ }
+ },
+ {
+ "valueCoding": {
+ "code": "3.2.1c",
+ "display": "Pure Ground glass"
+ }
+ }
+ ],
+ "item": [
+ {
+ "linkId": "g3.2.1",
+ "type": "group",
+ "item": [
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen",
+ "extension": [
+ {
+ "url": "#question",
+ "valueString": "3.2.1"
+ },
+ {
+ "url": "#answer",
+ "valueCoding": {
+ "code": "3.2.1a"
+ }
+ }
+ ]
+ }
+ ],
+ "linkId": "g3.2.1a",
+ "type": "group",
+ "item": [
+ {
+ "linkId": "3.2.1a",
+ "text": "largest dimension:",
+ "type": "integer",
+ "item": [
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl",
+ "valueCodeableConcept": {
+ "coding": [
+ {
+ "system": "http://hl7.org/fhir/ValueSet/questionnaire-item-control",
+ "code": "unit"
+ }
+ ]
+ }
+ }
+ ],
+ "text": "mm",
+ "type": "display"
+ }
+ ]
+ },
+ {
+ "linkId": "3.2.1a.image",
+ "text": "image",
+ "type": "string"
+ },
+ {
+ "linkId": "3.2.1a.series",
+ "text": "series",
+ "type": "string"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label",
+ "valueString": "3.2.2"
+ }
+ ],
+ "linkId": "3.2.2",
+ "text": "Plane in which the mass was measured:",
+ "type": "choice",
+ "option": [
+ {
+ "code": "3.2.2a",
+ "display": "Axial"
+ },
+ {
+ "code": "3.2.2b",
+ "display": "Coronal"
+ },
+ {
+ "code": "3.2.2c",
+ "display": "Sagittal"
+ }
+ ]
+ },
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label",
+ "valueString": "3.2.3"
+ }
+ ],
+ "linkId": "3.2.3",
+ "text": "Other characteristics of the main nodule/mass:",
+ "type": "text"
+ }
+ ]
+ },
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label",
+ "valueString": "3.3"
+ }
+ ],
+ "linkId": "g3.3",
+ "text": "Structures directly involved",
+ "type": "group",
+ "item": [
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label",
+ "valueString": "3.3.1"
+ }
+ ],
+ "linkId": "3.3.1",
+ "text": "State if there is bronchial involvement:",
+ "type": "choice",
+ "option": [
+ {
+ "code": "3.3.1a",
+ "display": "*Yes"
+ },
+ {
+ "code": "3.3.1b",
+ "display": "No"
+ },
+ {
+ "code": "3.3.1c",
+ "display": "Uncertain"
+ }
+ ],
+ "item": [
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory",
+ "valueCodeableConcept": {
+ "coding": [
+ {
+ "system": "http://hl7.org/fhir/ValueSet/questionnaire-display-category",
+ "code": "instructions"
+ }
+ ]
+ }
+ }
+ ],
+ "text": "*If yes, answer i and ii.",
+ "type": "display"
+ },
+ {
+ "linkId": "g3.3.1",
+ "type": "group",
+ "item": [
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen",
+ "extension": [
+ {
+ "url": "#question",
+ "valueString": "3.3.1"
+ },
+ {
+ "url": "#answer",
+ "valueCoding": {
+ "code": "3.3.1a"
+ }
+ }
+ ]
+ }
+ ],
+ "linkId": "g3.3.1.yes",
+ "type": "group",
+ "item": [
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label",
+ "valueString": "i)"
+ }
+ ],
+ "linkId": "3.3.1i",
+ "text": "Type of involvement:",
+ "type": "text"
+ },
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label",
+ "valueString": "ii)"
+ }
+ ],
+ "linkId": "3.3.1ii",
+ "text": "State if there is endobronchial involvement:",
+ "type": "choice",
+ "option": [
+ {
+ "code": "3.3.1.iia",
+ "display": "*Yes"
+ },
+ {
+ "code": "3.3.1.iib",
+ "display": "No"
+ },
+ {
+ "code": "3.3.1.iic",
+ "display": "Uncertain"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen",
+ "extension": [
+ {
+ "url": "#question",
+ "valueString": "3.3.1ii"
+ },
+ {
+ "url": "#answer",
+ "valueCoding": {
+ "code": "3.3.1.iia"
+ }
+ }
+ ]
+ }
+ ],
+ "linkId": "3.3.1iic",
+ "text": "Describe:",
+ "type": "text"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label",
+ "valueString": "3.3.2"
+ }
+ ],
+ "linkId": "3.3.2",
+ "text": "Is there direct involvement of any other anatomical structures?",
+ "type": "boolean",
+ "item": [
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory",
+ "valueCodeableConcept": {
+ "coding": [
+ {
+ "system": "http://hl7.org/fhir/ValueSet/questionnaire-display-category",
+ "code": "instructions"
+ }
+ ]
+ }
+ }
+ ],
+ "text": "*If yes, indicate and describe all ipsilateral anatomical structures involved by the mass. If no, go to 3.3.3.",
+ "type": "display"
+ },
+ {
+ "linkId": "g3.3.2.yes",
+ "type": "group",
+ "item": [
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen",
+ "extension": [
+ {
+ "url": "#question",
+ "valueString": "3.3.2"
+ },
+ {
+ "url": "#answer",
+ "valueBoolean": true
+ }
+ ]
+ }
+ ],
+ "linkId": "3.3.2.Yes",
+ "text": "Indicate and describe all ipsilateral anatomical structures involved by the mass:",
+ "type": "choice",
+ "repeats": true,
+ "option": [
+ {
+ "code": "3.3.2.Yes.a",
+ "display": "Pleura"
+ },
+ {
+ "code": "3.3.2.Yes.b",
+ "display": "Brachial Plexus"
+ },
+ {
+ "code": "3.3.2.Yes.c",
+ "display": "Diaphragm"
+ },
+ {
+ "code": "3.3.2.Yes.d",
+ "display": "Mediastinal fat"
+ },
+ {
+ "code": "3.3.2.Yes.e",
+ "display": "Aorta"
+ },
+ {
+ "code": "3.3.2.Yes.f",
+ "display": "Pulmonary Vessel"
+ },
+ {
+ "code": "3.3.2.Yes.g",
+ "display": "Pericardium or Heart"
+ },
+ {
+ "code": "3.3.2.Yes.h",
+ "display": "Mediastinal Vessels (including SVC)"
+ },
+ {
+ "code": "3.3.2.Yes.i",
+ "display": "Trachea/carina"
+ },
+ {
+ "code": "3.3.2.Yes.j",
+ "display": "Esophagus"
+ },
+ {
+ "code": "3.3.2.Yes.k",
+ "display": "Trachea esophageal groove"
+ },
+ {
+ "code": "3.3.2.Yes.l",
+ "display": "Vertebral Body"
+ },
+ {
+ "code": "3.3.2.Yes.m",
+ "display": "Chest wall and Ribs"
+ },
+ {
+ "code": "3.3.2.Yes.n",
+ "display": "Other structures"
+ }
+ ],
+ "item": [
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen",
+ "extension": [
+ {
+ "url": "#question",
+ "valueString": "3.3.2.Yes"
+ },
+ {
+ "url": "#answer",
+ "valueCoding": {
+ "code": "3.3.2.Yes.a"
+ }
+ }
+ ]
+ }
+ ],
+ "linkId": "3.3.2.Yes.a.text",
+ "text": "Pleura, description:",
+ "type": "string"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label",
+ "valueString": "3.3.3"
+ }
+ ],
+ "linkId": "3.3.3",
+ "text": "Are there additional suspicious pulmonary nodules?",
+ "type": "boolean",
+ "item": [
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory",
+ "valueCodeableConcept": {
+ "coding": [
+ {
+ "system": "http://hl7.org/fhir/ValueSet/questionnaire-display-category",
+ "code": "instructions"
+ }
+ ]
+ }
+ }
+ ],
+ "text": "*If yes, indicate and describe pulmonary nodules which apply. If no, go to 3.3.4.",
+ "type": "display"
+ },
+ {
+ "linkId": "g3.3.3.yes",
+ "type": "group",
+ "item": [
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen",
+ "extension": [
+ {
+ "url": "#question",
+ "valueString": "3.3.3"
+ },
+ {
+ "url": "#answer",
+ "valueBoolean": true
+ }
+ ]
+ }
+ ],
+ "linkId": "3.3.3.Yes",
+ "text": "Indicate and describe pulmonary nodules which apply:",
+ "type": "choice",
+ "option": [
+ {
+ "code": "3.3.3.Yes.a",
+ "display": "In the same lobe"
+ },
+ {
+ "code": "3.3.3.Yes.b",
+ "display": "In a different lobe, same lung"
+ },
+ {
+ "code": "3.3.3.Yes.c",
+ "display": "In the opposite lung (M1a)"
+ }
+ ],
+ "item": [
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen",
+ "extension": [
+ {
+ "url": "#question",
+ "valueString": "3.3.3.Yes"
+ },
+ {
+ "url": "#answer",
+ "valueCoding": {
+ "code": "3.3.3.Yes.a"
+ }
+ }
+ ]
+ }
+ ],
+ "linkId": "3.3.3.Yes.a.text",
+ "text": "In the same lobe, description:",
+ "type": "string"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label",
+ "valueString": "3.3.4"
+ }
+ ],
+ "linkId": "3.3.4",
+ "text": "Other notable intrathoracic findings (eg lymphangitis carcinomatosis):",
+ "type": "text"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label",
+ "valueString": "4"
+ }
+ ],
+ "linkId": "4.0",
+ "text": "N Category",
+ "type": "group",
+ "item": [
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label",
+ "valueString": "4.1"
+ }
+ ],
+ "linkId": "4.1",
+ "text": "Are there enlarged lymph nodes?",
+ "type": "boolean",
+ "item": [
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory",
+ "valueCodeableConcept": {
+ "coding": [
+ {
+ "system": "http://hl7.org/fhir/ValueSet/questionnaire-display-category",
+ "code": "instructions"
+ }
+ ]
+ }
+ }
+ ],
+ "text": "*If yes, indicate and describe the nodes which apply. If no, go to 4.2.",
+ "type": "display"
+ },
+ {
+ "linkId": "g4.1.yes",
+ "type": "group",
+ "item": [
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen",
+ "extension": [
+ {
+ "url": "#question",
+ "valueString": "4.1"
+ },
+ {
+ "url": "#answer",
+ "valueBoolean": true
+ }
+ ]
+ }
+ ],
+ "linkId": "4.1.yes",
+ "text": "Nodes and Descriptions:",
+ "type": "choice",
+ "repeats": true,
+ "option": [
+ {
+ "code": "4.1.yes.a",
+ "display": "1 Low Cervical, supraclavicular, and sternal notch nodes"
+ },
+ {
+ "code": "4.1.yes.b",
+ "display": "2R Upper Paratracheal (right)"
+ },
+ {
+ "code": "4.1.yes.c",
+ "display": "2L Upper paratracheal (left)"
+ },
+ {
+ "code": "4.1.yes.d",
+ "display": "3a Pre-vascular"
+ },
+ {
+ "code": "4.1.yes.e",
+ "display": "3p Retrotracheal"
+ },
+ {
+ "code": "4.1.yes.f",
+ "display": "4R Lower paratracheal (right)"
+ },
+ {
+ "code": "4.1.yes.g",
+ "display": "4L Upper paratracheal (left)"
+ },
+ {
+ "code": "4.1.yes.h",
+ "display": "5 Subaortic"
+ },
+ {
+ "code": "4.1.yes.i",
+ "display": "6 Para-aortic (ascending aorta or phrenic)"
+ },
+ {
+ "code": "4.1.yes.j",
+ "display": "7 Subcarinal"
+ },
+ {
+ "code": "4.1.yes.k",
+ "display": "8 Paraesophageal (below carina)"
+ },
+ {
+ "code": "4.1.yes.l",
+ "display": "9 Pulmonary ligament"
+ },
+ {
+ "code": "4.1.yes.m",
+ "display": "10 Hilar"
+ },
+ {
+ "code": "4.1.yes.n",
+ "display": "11 Interlobar"
+ },
+ {
+ "code": "4.1.yes.o",
+ "display": "12 Lobar"
+ },
+ {
+ "code": "4.1.yes.p",
+ "display": "13 Segmental"
+ },
+ {
+ "code": "4.1.yes.q",
+ "display": "14 Subsegmental"
+ },
+ {
+ "code": "4.1.yes.r",
+ "display": "Other Nodes (axilla, sub-diaphragmatic)"
+ }
+ ],
+ "item": [
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen",
+ "extension": [
+ {
+ "url": "#question",
+ "valueString": "4.1.yes"
+ },
+ {
+ "url": "#answer",
+ "valueCoding": {
+ "code": "4.1.yes.a"
+ }
+ }
+ ]
+ }
+ ],
+ "linkId": "4.1.yes.description",
+ "text": "Description of node:",
+ "type": "string"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label",
+ "valueString": "4.2"
+ }
+ ],
+ "linkId": "4.2",
+ "text": "Other notable findings:",
+ "type": "text"
+ }
+ ]
+ },
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label",
+ "valueString": "5"
+ }
+ ],
+ "linkId": "5.0",
+ "text": "M Category (Suspicious Extrathoracic Findings (M1b))",
+ "type": "group",
+ "item": [
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label",
+ "valueString": "5.1"
+ }
+ ],
+ "linkId": "5.1",
+ "text": "Are there suspicious extrathoracic findings?",
+ "type": "boolean",
+ "item": [
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory",
+ "valueCodeableConcept": {
+ "coding": [
+ {
+ "system": "http://hl7.org/fhir/ValueSet/questionnaire-display-category",
+ "code": "instructions"
+ }
+ ]
+ }
+ }
+ ],
+ "text": "*If yes, indicate and describe the structures below which apply. If no, go to 6.",
+ "type": "display"
+ },
+ {
+ "linkId": "g5.1.yes",
+ "type": "group",
+ "item": [
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen",
+ "extension": [
+ {
+ "url": "#question",
+ "valueString": "5.1"
+ },
+ {
+ "url": "#answer",
+ "valueBoolean": true
+ }
+ ]
+ }
+ ],
+ "linkId": "5.1.yes",
+ "text": "Applicable Structures and Descriptions:",
+ "type": "choice",
+ "repeats": true,
+ "option": [
+ {
+ "code": "5.1.yes.a",
+ "display": "Adrenals"
+ },
+ {
+ "code": "5.1.yes.b",
+ "display": "Liver"
+ },
+ {
+ "code": "5.1.yes.c",
+ "display": "Bone"
+ },
+ {
+ "code": "5.1.yes.d",
+ "display": "Other"
+ }
+ ],
+ "item": [
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen",
+ "extension": [
+ {
+ "url": "#question",
+ "valueString": "5.1.yes"
+ },
+ {
+ "url": "#answer",
+ "valueCoding": {
+ "code": "5.1.yes.d"
+ }
+ }
+ ]
+ }
+ ],
+ "linkId": "5.1.yes.d.description",
+ "text": "Description of structures:",
+ "type": "string"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label",
+ "valueString": "6"
+ }
+ ],
+ "linkId": "6.0",
+ "text": "Additional Findings",
+ "type": "group",
+ "item": [
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label",
+ "valueString": "6.1"
+ }
+ ],
+ "linkId": "6.1",
+ "text": "Are there additional findings?",
+ "type": "boolean",
+ "item": [
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory",
+ "valueCodeableConcept": {
+ "coding": [
+ {
+ "system": "http://hl7.org/fhir/ValueSet/questionnaire-display-category",
+ "code": "instructions"
+ }
+ ]
+ }
+ }
+ ],
+ "text": "*If yes, indicate and describe the findings below which apply. If no, go to Impressions.",
+ "type": "display"
+ }
+ ]
+ },
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label",
+ "valueString": "6.2"
+ },
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen",
+ "extension": [
+ {
+ "url": "#question",
+ "valueString": "6.1"
+ },
+ {
+ "url": "#answer",
+ "valueBoolean": true
+ }
+ ]
+ }
+ ],
+ "linkId": "6.2",
+ "text": "Findings and Descriptions:",
+ "type": "open-choice",
+ "repeats": true,
+ "option": [
+ {
+ "code": "6.2a",
+ "display": "Emphysema"
+ },
+ {
+ "code": "6.2b",
+ "display": "Fibrosis"
+ },
+ {
+ "code": "6.2c",
+ "display": "Coronary artery classification"
+ },
+ {
+ "code": "6.2d",
+ "display": "Asbestos related pleural disease"
+ },
+ {
+ "code": "6.2e",
+ "display": "Interstitial lung disease"
+ },
+ {
+ "code": "6.2f",
+ "display": "Atherosclerosis"
+ },
+ {
+ "code": "6.2g",
+ "display": "Pulmonary Embolism"
+ }
+ ],
+ "item": [
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen",
+ "extension": [
+ {
+ "url": "#question",
+ "valueString": "6.2"
+ },
+ {
+ "url": "#answer",
+ "valueCoding": {
+ "code": "6.2a"
+ }
+ }
+ ]
+ }
+ ],
+ "linkId": "6.2a",
+ "text": "Pulmonary, description:",
+ "type": "string"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "linkId": "g7",
+ "text": "IMPRESSIONS",
+ "type": "group",
+ "item": [
+ {
+ "linkId": "g7.1",
+ "type": "group",
+ "item": [
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label",
+ "valueString": "7.1"
+ }
+ ],
+ "linkId": "7.1",
+ "text": "Impression/Summary:",
+ "type": "text"
+ }
+ ]
+ },
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label",
+ "valueString": "7.2"
+ }
+ ],
+ "linkId": "g7.2",
+ "text": "Radiologic Staging (TNM Version â 7th edition)",
+ "type": "group",
+ "item": [
+ {
+ "linkId": "g7.20",
+ "text": "If this is a biopsy proven carcinoma, the preliminary radiologic stage is:",
+ "type": "group",
+ "item": [
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label",
+ "valueString": "i)"
+ },
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-questionControl",
+ "valueCodeableConcept": {
+ "coding": [
+ {
+ "code": "radio-button",
+ "display": "Radio Button"
+ }
+ ]
+ }
+ },
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-choiceOrientation",
+ "valueCode": "horizontal"
+ }
+ ],
+ "linkId": "7.2i",
+ "text": "Primary Tumour (T):",
+ "type": "choice",
+ "option": [
+ {
+ "code": "T1a",
+ "display": "T1a"
+ },
+ {
+ "code": "T1b",
+ "display": "T1b"
+ },
+ {
+ "code": "T2",
+ "display": "T2"
+ },
+ {
+ "code": "T2a",
+ "display": "T2a"
+ },
+ {
+ "code": "T2b",
+ "display": "T2b"
+ },
+ {
+ "code": "T3",
+ "display": "T3"
+ },
+ {
+ "code": "T4",
+ "display": "T4"
+ }
+ ]
+ },
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label",
+ "valueString": "ii)"
+ }
+ ],
+ "linkId": "7.2ii",
+ "text": "Regional Lymph Nodes (N):",
+ "type": "choice",
+ "option": [
+ {
+ "code": "NX",
+ "display": "NX"
+ },
+ {
+ "code": "N0",
+ "display": "N0"
+ },
+ {
+ "code": "N1",
+ "display": "N1"
+ },
+ {
+ "code": "N2",
+ "display": "N2"
+ },
+ {
+ "code": "N3",
+ "display": "N3"
+ }
+ ]
+ },
+ {
+ "extension": [
+ {
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label",
+ "valueString": "iii)"
+ }
+ ],
+ "linkId": "7.2iii",
+ "text": "Distant Metastasis (M):",
+ "type": "choice",
+ "option": [
+ {
+ "code": "M0",
+ "display": "M0"
+ },
+ {
+ "code": "M1",
+ "display": "M1"
+ },
+ {
+ "code": "M1a",
+ "display": "M1a"
+ },
+ {
+ "code": "M1b",
+ "display": "M1b"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
From ee414b73d9755ebfa9ba40714b8c837930bd971d Mon Sep 17 00:00:00 2001
From: Martha Mitran
Date: Tue, 28 Nov 2023 13:12:05 -0800
Subject: [PATCH 02/14] Pass properties through to Remote Terminology Service
on CodeSystem lookup (#5477)
* Pass properties through to Remote Terminology Service on CodeSystem operation
* Propagate list of property names throughout. Introduce a parameter object for the lookupCode method. Mark other lookupCode methods as deprecated. Add unit tests.
* Update remote terminology service tests
* Address code review comments
* Fix unit tests
* Address latest code review comments to update default methods
* Address latest code review comments to update default methods - update fallback condition
* Address latest code review comments to update default methods - update fallback condition
---
.../context/support/IValidationSupport.java | 57 +++-
.../context/support/LookupCodeRequest.java | 56 ++++
.../uhn/hapi/fhir/docs/ValidatorExamples.java | 5 +-
...e-terminology-service-for-code-system.yaml | 5 +
.../jpa/dao/JpaResourceDaoCodeSystem.java | 41 ++-
.../BaseJpaResourceProviderCodeSystem.java | 11 +-
.../provider/ValueSetOperationProvider.java | 42 +--
.../ValueSetOperationProviderDstu2.java | 13 +-
.../ca/uhn/fhir/jpa/term/TermReadSvcImpl.java | 25 +-
...ProviderDstu3CodeSystemPropertiesTest.java | 101 ++++++
.../ResourceProviderDstu3ValueSetTest.java | 14 +-
...rceProviderR4CodeSystemPropertiesTest.java | 91 ++++++
.../r4/ResourceProviderR4CodeSystemTest.java | 23 ++
...rceProviderR4ValueSetNoVerCSNoVerTest.java | 8 +-
.../jpa/term/TerminologySvcDeltaR4Test.java | 15 +-
...erminologySvcImplCurrentVersionR4Test.java | 13 +-
...rceProviderR5CodeSystemPropertiesTest.java | 89 ++++++
.../r5/ResourceProviderR5ValueSetTest.java | 8 +-
.../CodeSystemLookupWithPropertiesUtil.java | 28 ++
...rminologyDisplayPopulationInterceptor.java | 5 +-
.../api/dao/IFhirResourceDaoCodeSystem.java | 10 +
.../support/BaseValidationSupportWrapper.java | 8 +-
.../support/CachingValidationSupport.java | 15 +-
.../CommonCodeSystemsTerminologyService.java | 36 ++-
...oryTerminologyServerValidationSupport.java | 21 +-
...teTerminologyServiceValidationSupport.java | 33 +-
...ownCodeSystemWarningValidationSupport.java | 8 +-
.../support/ValidationSupportChain.java | 27 +-
...ologyDisplayPopulationInterceptorTest.java | 10 -
...mmonCodeSystemsTerminologyServiceTest.java | 21 +-
...rminologyServiceValidationSupportTest.java | 297 +++++++-----------
...logyServiceValidationSupportDstu3Test.java | 189 +++++------
.../FhirInstanceValidatorR4Test.java | 8 +-
.../FhirInstanceValidatorR4BTest.java | 35 ++-
...inologyServiceValidationSupportR5Test.java | 14 +-
35 files changed, 890 insertions(+), 492 deletions(-)
create mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/LookupCodeRequest.java
create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5476-pass-properties-through-remote-terminology-service-for-code-system.yaml
create mode 100644 hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemPropertiesTest.java
create mode 100644 hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemPropertiesTest.java
create mode 100644 hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5CodeSystemPropertiesTest.java
create mode 100644 hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/provider/CodeSystemLookupWithPropertiesUtil.java
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java
index 27e0f5ddea5..3f9c4030c44 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java
@@ -329,14 +329,16 @@ public interface IValidationSupport {
}
/**
- * Look up a code using the system and code value
+ * Look up a code using the system and code value.
+ * @deprecated This method has been deprecated in HAPI FHIR 7.0.0. Use {@link IValidationSupport#lookupCode(ValidationSupportContext, LookupCodeRequest)} instead.
*
* @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to
* other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
* @param theSystem The CodeSystem URL
* @param theCode The code
- * @param theDisplayLanguage to filter out the designation by the display language. To return all designation, set this value to null
.
+ * @param theDisplayLanguage Used to filter out the designation by the display language. To return all designation, set this value to null
.
*/
+ @Deprecated
@Nullable
default LookupCodeResult lookupCode(
ValidationSupportContext theValidationSupportContext,
@@ -348,12 +350,14 @@ public interface IValidationSupport {
/**
* Look up a code using the system and code value
+ * @deprecated This method has been deprecated in HAPI FHIR 7.0.0. Use {@link IValidationSupport#lookupCode(ValidationSupportContext, LookupCodeRequest)} instead.
*
* @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to
* other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
* @param theSystem The CodeSystem URL
* @param theCode The code
*/
+ @Deprecated
@Nullable
default LookupCodeResult lookupCode(
ValidationSupportContext theValidationSupportContext, String theSystem, String theCode) {
@@ -361,7 +365,26 @@ public interface IValidationSupport {
}
/**
- * Returns true
if the given valueset can be validated by the given
+ * Look up a code using the system, code and other parameters captured in {@link LookupCodeRequest}.
+ * @since 7.0.0
+ *
+ * @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to
+ * other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
+ * @param theLookupCodeRequest The parameters used to perform the lookup, including system and code.
+ */
+ @Nullable
+ default LookupCodeResult lookupCode(
+ ValidationSupportContext theValidationSupportContext, @Nonnull LookupCodeRequest theLookupCodeRequest) {
+ // TODO: can change to return null once the deprecated methods are removed
+ return lookupCode(
+ theValidationSupportContext,
+ theLookupCodeRequest.getSystem(),
+ theLookupCodeRequest.getCode(),
+ theLookupCodeRequest.getDisplayLanguage());
+ }
+
+ /**
+ * Returns true
if the given ValueSet can be validated by the given
* validation support module
*
* @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to
@@ -554,6 +577,11 @@ public interface IValidationSupport {
}
class CodeValidationResult {
+ public static final String SOURCE_DETAILS = "sourceDetails";
+ public static final String RESULT = "result";
+ public static final String MESSAGE = "message";
+ public static final String DISPLAY = "display";
+
private String myCode;
private String myMessage;
private IssueSeverity mySeverity;
@@ -682,6 +710,23 @@ public interface IValidationSupport {
setSeverity(IssueSeverity.valueOf(theIssueSeverity.toUpperCase()));
return this;
}
+
+ public IBaseParameters toParameters(FhirContext theContext) {
+ IBaseParameters retVal = ParametersUtil.newInstance(theContext);
+
+ ParametersUtil.addParameterToParametersBoolean(theContext, retVal, RESULT, isOk());
+ if (isNotBlank(getMessage())) {
+ ParametersUtil.addParameterToParametersString(theContext, retVal, MESSAGE, getMessage());
+ }
+ if (isNotBlank(getDisplay())) {
+ ParametersUtil.addParameterToParametersString(theContext, retVal, DISPLAY, getDisplay());
+ }
+ if (isNotBlank(getSourceDetails())) {
+ ParametersUtil.addParameterToParametersString(theContext, retVal, SOURCE_DETAILS, getSourceDetails());
+ }
+
+ return retVal;
+ }
}
class ValueSetExpansionOutcome {
@@ -814,7 +859,7 @@ public interface IValidationSupport {
}
public IBaseParameters toParameters(
- FhirContext theContext, List extends IPrimitiveType> theProperties) {
+ FhirContext theContext, List extends IPrimitiveType> thePropertyNames) {
IBaseParameters retVal = ParametersUtil.newInstance(theContext);
if (isNotBlank(getCodeSystemDisplayName())) {
@@ -829,8 +874,8 @@ public interface IValidationSupport {
if (myProperties != null) {
Set properties = Collections.emptySet();
- if (theProperties != null) {
- properties = theProperties.stream()
+ if (thePropertyNames != null) {
+ properties = thePropertyNames.stream()
.map(IPrimitiveType::getValueAsString)
.collect(Collectors.toSet());
}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/LookupCodeRequest.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/LookupCodeRequest.java
new file mode 100644
index 00000000000..dc7074bba25
--- /dev/null
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/LookupCodeRequest.java
@@ -0,0 +1,56 @@
+package ca.uhn.fhir.context.support;
+
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * Represents parameters which can be passed to the $lookup operation for codes.
+ * @since 7.0.0
+ */
+public class LookupCodeRequest {
+ private final String mySystem;
+ private final String myCode;
+ private String myDisplayLanguage;
+ private Collection myPropertyNames;
+
+ /**
+ * @param theSystem The CodeSystem URL
+ * @param theCode The code
+ */
+ public LookupCodeRequest(String theSystem, String theCode) {
+ mySystem = theSystem;
+ myCode = theCode;
+ }
+
+ /**
+ * @param theSystem The CodeSystem URL
+ * @param theCode The code
+ * @param theDisplayLanguage Used to filter out the designation by the display language. To return all designation, set this value to null
.
+ * @param thePropertyNames The collection of properties to be returned in the output. If no properties are specified, the implementor chooses what to return.
+ */
+ public LookupCodeRequest(
+ String theSystem, String theCode, String theDisplayLanguage, Collection thePropertyNames) {
+ this(theSystem, theCode);
+ myDisplayLanguage = theDisplayLanguage;
+ myPropertyNames = thePropertyNames;
+ }
+
+ public String getSystem() {
+ return mySystem;
+ }
+
+ public String getCode() {
+ return myCode;
+ }
+
+ public String getDisplayLanguage() {
+ return myDisplayLanguage;
+ }
+
+ public Collection getPropertyNames() {
+ if (myPropertyNames == null) {
+ return Collections.emptyList();
+ }
+ return myPropertyNames;
+ }
+}
diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ValidatorExamples.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ValidatorExamples.java
index ed304aa3b48..d75bd9d5064 100644
--- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ValidatorExamples.java
+++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ValidatorExamples.java
@@ -23,6 +23,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.ConceptValidationOptions;
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
import ca.uhn.fhir.context.support.IValidationSupport;
+import ca.uhn.fhir.context.support.LookupCodeRequest;
import ca.uhn.fhir.context.support.ValidationSupportContext;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.parser.IParser;
@@ -308,9 +309,7 @@ public class ValidatorExamples {
@Override
public LookupCodeResult lookupCode(
ValidationSupportContext theValidationSupportContext,
- String theSystem,
- String theCode,
- String theDisplayLanguage) {
+ @Nonnull LookupCodeRequest validationSupportParameterObject) {
// TODO: implement (or return null if your implementation does not support this function)
return null;
}
diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5476-pass-properties-through-remote-terminology-service-for-code-system.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5476-pass-properties-through-remote-terminology-service-for-code-system.yaml
new file mode 100644
index 00000000000..f4b02e23951
--- /dev/null
+++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5476-pass-properties-through-remote-terminology-service-for-code-system.yaml
@@ -0,0 +1,5 @@
+---
+type: add
+issue: 5476
+title: "A new method on the IValidationSupport interface called lookupCode(LookupCodeRequest) has been added.
+This method will replace the existing lookupCode methods, which are now deprecated."
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoCodeSystem.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoCodeSystem.java
index e4b128f608e..12a4f01a947 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoCodeSystem.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoCodeSystem.java
@@ -24,6 +24,7 @@ import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.support.ConceptValidationOptions;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.IValidationSupport.CodeValidationResult;
+import ca.uhn.fhir.context.support.LookupCodeRequest;
import ca.uhn.fhir.context.support.ValidationSupportContext;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem;
@@ -41,6 +42,7 @@ import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.util.FhirTerser;
import ca.uhn.hapi.converters.canonical.VersionCanonicalizer;
+import org.apache.commons.collections4.CollectionUtils;
import org.hl7.fhir.common.hapi.validation.support.CommonCodeSystemsTerminologyService;
import org.hl7.fhir.instance.model.api.IBaseCoding;
import org.hl7.fhir.instance.model.api.IBaseDatatype;
@@ -52,8 +54,10 @@ import org.hl7.fhir.r4.model.Coding;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Date;
import java.util.List;
+import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.PostConstruct;
@@ -125,8 +129,33 @@ public class JpaResourceDaoCodeSystem extends BaseHapiF
IBaseCoding theCoding,
IPrimitiveType theDisplayLanguage,
RequestDetails theRequestDetails) {
+ return lookupCode(
+ theCode,
+ theSystem,
+ theCoding,
+ theDisplayLanguage,
+ CollectionUtils.emptyCollection(),
+ theRequestDetails);
+ }
+
+ @Nonnull
+ @Override
+ public IValidationSupport.LookupCodeResult lookupCode(
+ IPrimitiveType theCode,
+ IPrimitiveType theSystem,
+ IBaseCoding theCoding,
+ IPrimitiveType theDisplayLanguage,
+ Collection> thePropertyNames,
+ RequestDetails theRequestDetails) {
return doLookupCode(
- myFhirContext, myTerser, myValidationSupport, theCode, theSystem, theCoding, theDisplayLanguage);
+ myFhirContext,
+ myTerser,
+ myValidationSupport,
+ theCode,
+ theSystem,
+ theCoding,
+ theDisplayLanguage,
+ thePropertyNames);
}
@Override
@@ -285,7 +314,8 @@ public class JpaResourceDaoCodeSystem extends BaseHapiF
IPrimitiveType theCode,
IPrimitiveType theSystem,
IBaseCoding theCoding,
- IPrimitiveType theDisplayLanguage) {
+ IPrimitiveType theDisplayLanguage,
+ Collection> thePropertyNames) {
boolean haveCoding = theCoding != null
&& isNotBlank(extractCodingSystem(theCoding))
&& isNotBlank(extractCodingCode(theCoding));
@@ -323,11 +353,16 @@ public class JpaResourceDaoCodeSystem extends BaseHapiF
ourLog.info("Looking up {} / {}", system, code);
+ Collection propertyNames = CollectionUtils.emptyIfNull(thePropertyNames).stream()
+ .map(IPrimitiveType::getValueAsString)
+ .collect(Collectors.toSet());
+
if (theValidationSupport.isCodeSystemSupported(new ValidationSupportContext(theValidationSupport), system)) {
ourLog.info("Code system {} is supported", system);
IValidationSupport.LookupCodeResult retVal = theValidationSupport.lookupCode(
- new ValidationSupportContext(theValidationSupport), system, code, displayLanguage);
+ new ValidationSupportContext(theValidationSupport),
+ new LookupCodeRequest(system, code, displayLanguage, propertyNames));
if (retVal != null) {
return retVal;
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderCodeSystem.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderCodeSystem.java
index 1929635de94..85b88f5f0bc 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderCodeSystem.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderCodeSystem.java
@@ -45,7 +45,6 @@ import java.util.Optional;
import java.util.function.Supplier;
import javax.servlet.http.HttpServletRequest;
-import static ca.uhn.fhir.jpa.provider.ValueSetOperationProvider.toValidateCodeResult;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public abstract class BaseJpaResourceProviderCodeSystem extends BaseJpaResourceProvider {
@@ -65,6 +64,7 @@ public abstract class BaseJpaResourceProviderCodeSystem
@OperationParam(name = "version", typeName = "string", min = 0),
@OperationParam(name = "display", typeName = "string", min = 1),
@OperationParam(name = "abstract", typeName = "boolean", min = 1),
+ @OperationParam(name = "property", min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "code")
})
public IBaseParameters lookup(
HttpServletRequest theServletRequest,
@@ -75,7 +75,7 @@ public abstract class BaseJpaResourceProviderCodeSystem
@OperationParam(name = "displayLanguage", min = 0, max = 1, typeName = "code")
IPrimitiveType theDisplayLanguage,
@OperationParam(name = "property", min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "code")
- List> theProperties,
+ List> thePropertyNames,
RequestDetails theRequestDetails) {
startRequest(theServletRequest);
@@ -83,9 +83,10 @@ public abstract class BaseJpaResourceProviderCodeSystem
IFhirResourceDaoCodeSystem dao = (IFhirResourceDaoCodeSystem) getDao();
IValidationSupport.LookupCodeResult result;
applyVersionToSystem(theSystem, theVersion);
- result = dao.lookupCode(theCode, theSystem, theCoding, theDisplayLanguage, theRequestDetails);
+ result = dao.lookupCode(
+ theCode, theSystem, theCoding, theDisplayLanguage, thePropertyNames, theRequestDetails);
result.throwNotFoundIfAppropriate();
- return result.toParameters(theRequestDetails.getFhirContext(), theProperties);
+ return result.toParameters(theRequestDetails.getFhirContext(), thePropertyNames);
} finally {
endRequest(theServletRequest);
}
@@ -191,7 +192,7 @@ public abstract class BaseJpaResourceProviderCodeSystem
theCodeableConcept,
theRequestDetails);
}
- return toValidateCodeResult(getContext(), result);
+ return result.toParameters(getContext());
} finally {
endRequest(theServletRequest);
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ValueSetOperationProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ValueSetOperationProvider.java
index 99189c92154..2bb327cf9c7 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ValueSetOperationProvider.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ValueSetOperationProvider.java
@@ -19,7 +19,6 @@
*/
package ca.uhn.fhir.jpa.provider;
-import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.ConceptValidationOptions;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.IValidationSupport.CodeValidationResult;
@@ -61,10 +60,6 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class ValueSetOperationProvider extends BaseJpaProvider {
private static final Logger ourLog = LoggerFactory.getLogger(ValueSetOperationProvider.class);
- public static final String SOURCE_DETAILS = "sourceDetails";
- public static final String RESULT = "result";
- public static final String MESSAGE = "message";
- public static final String DISPLAY = "display";
@Autowired
protected IValidationSupport myValidationSupport;
@@ -149,10 +144,10 @@ public class ValueSetOperationProvider extends BaseJpaProvider {
idempotent = true,
typeName = "ValueSet",
returnParameters = {
- @OperationParam(name = RESULT, typeName = "boolean", min = 1),
- @OperationParam(name = MESSAGE, typeName = "string"),
- @OperationParam(name = DISPLAY, typeName = "string"),
- @OperationParam(name = SOURCE_DETAILS, typeName = "string")
+ @OperationParam(name = CodeValidationResult.RESULT, typeName = "boolean", min = 1),
+ @OperationParam(name = CodeValidationResult.MESSAGE, typeName = "string"),
+ @OperationParam(name = CodeValidationResult.DISPLAY, typeName = "string"),
+ @OperationParam(name = CodeValidationResult.SOURCE_DETAILS, typeName = "string")
})
public IBaseParameters validateCode(
HttpServletRequest theServletRequest,
@@ -164,7 +159,8 @@ public class ValueSetOperationProvider extends BaseJpaProvider {
@OperationParam(name = "system", min = 0, max = 1, typeName = "uri") IPrimitiveType theSystem,
@OperationParam(name = "systemVersion", min = 0, max = 1, typeName = "string")
IPrimitiveType theSystemVersion,
- @OperationParam(name = DISPLAY, min = 0, max = 1, typeName = "string") IPrimitiveType theDisplay,
+ @OperationParam(name = CodeValidationResult.DISPLAY, min = 0, max = 1, typeName = "string")
+ IPrimitiveType theDisplay,
@OperationParam(name = "coding", min = 0, max = 1, typeName = "Coding") IBaseCoding theCoding,
@OperationParam(name = "codeableConcept", min = 0, max = 1, typeName = "CodeableConcept")
ICompositeType theCodeableConcept,
@@ -228,7 +224,7 @@ public class ValueSetOperationProvider extends BaseJpaProvider {
theCodeableConcept,
theRequestDetails);
}
- return toValidateCodeResult(getContext(), result);
+ return result.toParameters(getContext());
} finally {
endRequest(theServletRequest);
}
@@ -256,7 +252,9 @@ public class ValueSetOperationProvider extends BaseJpaProvider {
name = ProviderConstants.OPERATION_INVALIDATE_EXPANSION,
idempotent = false,
typeName = "ValueSet",
- returnParameters = {@OperationParam(name = MESSAGE, typeName = "string", min = 1, max = 1)})
+ returnParameters = {
+ @OperationParam(name = CodeValidationResult.MESSAGE, typeName = "string", min = 1, max = 1)
+ })
public IBaseParameters invalidateValueSetExpansion(
@IdParam IIdType theValueSetId, RequestDetails theRequestDetails, HttpServletRequest theServletRequest) {
startRequest(theServletRequest);
@@ -265,7 +263,7 @@ public class ValueSetOperationProvider extends BaseJpaProvider {
String outcome = myTermReadSvc.invalidatePreCalculatedExpansion(theValueSetId, theRequestDetails);
IBaseParameters retVal = ParametersUtil.newInstance(getContext());
- ParametersUtil.addParameterToParametersString(getContext(), retVal, MESSAGE, outcome);
+ ParametersUtil.addParameterToParametersString(getContext(), retVal, CodeValidationResult.MESSAGE, outcome);
return retVal;
} finally {
@@ -326,22 +324,4 @@ public class ValueSetOperationProvider extends BaseJpaProvider {
return options;
}
-
- public static IBaseParameters toValidateCodeResult(FhirContext theContext, CodeValidationResult theResult) {
- IBaseParameters retVal = ParametersUtil.newInstance(theContext);
-
- ParametersUtil.addParameterToParametersBoolean(theContext, retVal, RESULT, theResult.isOk());
- if (isNotBlank(theResult.getMessage())) {
- ParametersUtil.addParameterToParametersString(theContext, retVal, MESSAGE, theResult.getMessage());
- }
- if (isNotBlank(theResult.getDisplay())) {
- ParametersUtil.addParameterToParametersString(theContext, retVal, DISPLAY, theResult.getDisplay());
- }
- if (isNotBlank(theResult.getSourceDetails())) {
- ParametersUtil.addParameterToParametersString(
- theContext, retVal, SOURCE_DETAILS, theResult.getSourceDetails());
- }
-
- return retVal;
- }
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ValueSetOperationProviderDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ValueSetOperationProviderDstu2.java
index 372b030629b..0480f0290d2 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ValueSetOperationProviderDstu2.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ValueSetOperationProviderDstu2.java
@@ -127,7 +127,7 @@ public class ValueSetOperationProviderDstu2 extends ValueSetOperationProvider {
@OperationParam(name = "displayLanguage", min = 0, max = 1, typeName = "code")
IPrimitiveType theDisplayLanguage,
@OperationParam(name = "property", min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "code")
- List> theProperties,
+ List> thePropertyNames,
RequestDetails theRequestDetails) {
startRequest(theServletRequest);
@@ -137,9 +137,16 @@ public class ValueSetOperationProviderDstu2 extends ValueSetOperationProvider {
FhirTerser terser = getContext().newTerser();
result = JpaResourceDaoCodeSystem.doLookupCode(
- getContext(), terser, myValidationSupport, theCode, theSystem, theCoding, theDisplayLanguage);
+ getContext(),
+ terser,
+ myValidationSupport,
+ theCode,
+ theSystem,
+ theCoding,
+ theDisplayLanguage,
+ thePropertyNames);
result.throwNotFoundIfAppropriate();
- return result.toParameters(theRequestDetails.getFhirContext(), theProperties);
+ return result.toParameters(theRequestDetails.getFhirContext(), thePropertyNames);
} finally {
endRequest(theServletRequest);
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java
index 73f95569ba6..9372b9b03b9 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java
@@ -23,6 +23,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.support.ConceptValidationOptions;
import ca.uhn.fhir.context.support.IValidationSupport;
+import ca.uhn.fhir.context.support.LookupCodeRequest;
import ca.uhn.fhir.context.support.ValidationSupportContext;
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
import ca.uhn.fhir.i18n.Msg;
@@ -90,6 +91,7 @@ import ca.uhn.hapi.converters.canonical.VersionCanonicalizer;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Stopwatch;
import com.google.common.collect.ArrayListMultimap;
+import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.ListUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
@@ -147,7 +149,6 @@ import org.springframework.transaction.interceptor.NoRollbackRuleAttribute;
import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.transaction.support.TransactionTemplate;
-import org.springframework.util.CollectionUtils;
import org.springframework.util.comparator.Comparators;
import java.util.ArrayList;
@@ -2575,10 +2576,13 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs {
return new IFhirResourceDaoCodeSystem.SubsumesResult(subsumes);
}
- protected IValidationSupport.LookupCodeResult lookupCode(
- String theSystem, String theCode, String theDisplayLanguage) {
+ @Override
+ public IValidationSupport.LookupCodeResult lookupCode(
+ ValidationSupportContext theValidationSupportContext, @Nonnull LookupCodeRequest theLookupCodeRequest) {
TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager);
return txTemplate.execute(t -> {
+ final String theSystem = theLookupCodeRequest.getSystem();
+ final String theCode = theLookupCodeRequest.getCode();
Optional codeOpt = findCode(theSystem, theCode);
if (codeOpt.isPresent()) {
TermConcept code = codeOpt.get();
@@ -2593,7 +2597,7 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs {
for (TermConceptDesignation next : code.getDesignations()) {
// filter out the designation based on displayLanguage if any
- if (isDisplayLanguageMatch(theDisplayLanguage, next.getLanguage())) {
+ if (isDisplayLanguageMatch(theLookupCodeRequest.getDisplayLanguage(), next.getLanguage())) {
IValidationSupport.ConceptDesignation designation = new IValidationSupport.ConceptDesignation();
designation.setLanguage(next.getLanguage());
designation.setUseSystem(next.getUseSystem());
@@ -2604,7 +2608,11 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs {
}
}
+ final Collection propertyNames = theLookupCodeRequest.getPropertyNames();
for (TermConceptProperty next : code.getProperties()) {
+ if (ObjectUtils.isNotEmpty(propertyNames) && !propertyNames.contains(next.getKey())) {
+ continue;
+ }
if (next.getType() == TermConceptPropertyTypeEnum.CODING) {
IValidationSupport.CodingConceptProperty property =
new IValidationSupport.CodingConceptProperty(
@@ -3117,15 +3125,6 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs {
return isValueSetPreExpandedForCodeValidation(valueSetR4);
}
- @Override
- public LookupCodeResult lookupCode(
- ValidationSupportContext theValidationSupportContext,
- String theSystem,
- String theCode,
- String theDisplayLanguage) {
- return lookupCode(theSystem, theCode, theDisplayLanguage);
- }
-
private static class TermCodeSystemVersionDetails {
private final long myPid;
diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemPropertiesTest.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemPropertiesTest.java
new file mode 100644
index 00000000000..66eaa679850
--- /dev/null
+++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemPropertiesTest.java
@@ -0,0 +1,101 @@
+package ca.uhn.fhir.jpa.provider.dstu3;
+
+import ca.uhn.fhir.jpa.model.util.JpaConstants;
+import ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil;
+import ca.uhn.fhir.rest.gclient.IOperationUntypedWithInputAndPartialOutput;
+import org.hl7.fhir.dstu3.model.CodeSystem;
+import org.hl7.fhir.dstu3.model.CodeType;
+import org.hl7.fhir.dstu3.model.Parameters;
+import org.hl7.fhir.dstu3.model.StringType;
+import org.hl7.fhir.dstu3.model.UriType;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.stream.Stream;
+
+import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourCode;
+import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourCodeSystemId;
+import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourCodeSystemUrl;
+import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyA;
+import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyB;
+import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyValueA;
+import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyValueB;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class ResourceProviderDstu3CodeSystemPropertiesTest extends BaseResourceProviderDstu3Test {
+
+ public static Stream parametersLookup() {
+ return CodeSystemLookupWithPropertiesUtil.parametersLookupWithProperties();
+ }
+
+ @ParameterizedTest
+ @MethodSource(value = "parametersLookup")
+ public void testLookup_withProperties_returnsCorrectParameters(List theLookupProperties, List theExpectedReturnedProperties) {
+ // setup
+ CodeSystem codeSystem = new CodeSystem();
+ codeSystem.setId(ourCodeSystemId);
+ codeSystem.setUrl(ourCodeSystemUrl);
+ CodeSystem.ConceptDefinitionComponent concept = codeSystem.addConcept().setCode(ourCode);
+ CodeSystem.ConceptPropertyComponent propertyComponent = new CodeSystem.ConceptPropertyComponent()
+ .setCode(ourPropertyA).setValue(new StringType(ourPropertyValueA));
+ concept.addProperty(propertyComponent);
+ propertyComponent = new CodeSystem.ConceptPropertyComponent()
+ .setCode(ourPropertyB).setValue(new StringType(ourPropertyValueB));
+ concept.addProperty(propertyComponent);
+ myCodeSystemDao.create(codeSystem, mySrd);
+
+ // test
+ IOperationUntypedWithInputAndPartialOutput respParam = myClient
+ .operation()
+ .onType(CodeSystem.class)
+ .named(JpaConstants.OPERATION_LOOKUP)
+ .withParameter(Parameters.class, "code", new CodeType(ourCode))
+ .andParameter("system", new UriType(ourCodeSystemUrl));
+
+ theLookupProperties.forEach(p -> respParam.andParameter("property", new CodeType(p)));
+ Parameters parameters = respParam.execute();
+
+ Iterator paramIterator = parameters.getParameter().iterator();
+ Parameters.ParametersParameterComponent parameter = null;
+ while (paramIterator.hasNext()) {
+ Parameters.ParametersParameterComponent currentParameter = paramIterator.next();
+ if (currentParameter.getName().equals("property")) {
+ parameter = currentParameter;
+ break;
+ }
+ }
+
+ if (theExpectedReturnedProperties.isEmpty()) {
+ assertNull(parameter);
+ return;
+ }
+
+ Iterator propertyIterator = concept.getProperty().stream()
+ .filter(property -> theExpectedReturnedProperties.contains(property.getCode())).iterator();
+
+ while (propertyIterator.hasNext()) {
+ CodeSystem.ConceptPropertyComponent property = propertyIterator.next();
+ assertNotNull(parameter);
+
+ Iterator parameterPartIterator = parameter.getPart().iterator();
+
+ parameter = parameterPartIterator.next();
+ assertEquals("code", parameter.getName());
+ assertEquals(property.getCode(), ((CodeType)parameter.getValue()).getValue());
+
+ parameter = parameterPartIterator.next();
+ assertEquals("value", parameter.getName());
+ assertTrue(property.getValue().equalsShallow(parameter.getValue()));
+
+ if (paramIterator.hasNext()) {
+ parameter = paramIterator.next();
+ }
+ }
+ }
+}
diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java
index 09ce4621a13..5bee62a6211 100644
--- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java
+++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java
@@ -1,5 +1,6 @@
package ca.uhn.fhir.jpa.provider.dstu3;
+import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
@@ -9,7 +10,6 @@ import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
import ca.uhn.fhir.jpa.model.dao.JpaPid;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
-import ca.uhn.fhir.jpa.provider.ValueSetOperationProvider;
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
import ca.uhn.fhir.jpa.util.CircularQueueCaptureQueriesListener;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
@@ -790,13 +790,13 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp);
- assertEquals(ValueSetOperationProvider.RESULT, respParam.getParameter().get(0).getName());
+ assertEquals(IValidationSupport.CodeValidationResult.RESULT, respParam.getParameter().get(0).getName());
assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue());
- assertEquals(ValueSetOperationProvider.DISPLAY, respParam.getParameter().get(1).getName());
+ assertEquals(IValidationSupport.CodeValidationResult.DISPLAY, respParam.getParameter().get(1).getName());
assertEquals("Male", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
- assertEquals(ValueSetOperationProvider.SOURCE_DETAILS, respParam.getParameter().get(2).getName());
+ assertEquals(IValidationSupport.CodeValidationResult.SOURCE_DETAILS, respParam.getParameter().get(2).getName());
assertEquals("Code was validated against in-memory expansion of ValueSet: http://hl7.org/fhir/ValueSet/administrative-gender", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
}
@@ -820,13 +820,13 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp);
- assertEquals(ValueSetOperationProvider.RESULT, respParam.getParameter().get(0).getName());
+ assertEquals(IValidationSupport.CodeValidationResult.RESULT, respParam.getParameter().get(0).getName());
assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue());
- assertEquals(ValueSetOperationProvider.DISPLAY, respParam.getParameter().get(1).getName());
+ assertEquals(IValidationSupport.CodeValidationResult.DISPLAY, respParam.getParameter().get(1).getName());
assertEquals("Male", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
- assertEquals(ValueSetOperationProvider.SOURCE_DETAILS, respParam.getParameter().get(2).getName());
+ assertEquals(IValidationSupport.CodeValidationResult.SOURCE_DETAILS, respParam.getParameter().get(2).getName());
assertEquals("Code was validated against in-memory expansion of ValueSet: http://hl7.org/fhir/ValueSet/administrative-gender", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
}
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemPropertiesTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemPropertiesTest.java
new file mode 100644
index 00000000000..7338a0b4174
--- /dev/null
+++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemPropertiesTest.java
@@ -0,0 +1,91 @@
+package ca.uhn.fhir.jpa.provider.r4;
+
+import ca.uhn.fhir.jpa.model.util.JpaConstants;
+import ca.uhn.fhir.jpa.provider.BaseResourceProviderR4Test;
+import ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil;
+import ca.uhn.fhir.rest.gclient.IOperationUntypedWithInputAndPartialOutput;
+import org.hl7.fhir.r4.model.CodeSystem;
+import org.hl7.fhir.r4.model.CodeType;
+import org.hl7.fhir.r4.model.Parameters;
+import org.hl7.fhir.r4.model.StringType;
+import org.hl7.fhir.r4.model.UriType;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.stream.Stream;
+
+import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourCode;
+import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourCodeSystemId;
+import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourCodeSystemUrl;
+import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyA;
+import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyB;
+import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyValueA;
+import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyValueB;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class ResourceProviderR4CodeSystemPropertiesTest extends BaseResourceProviderR4Test {
+ public static Stream parametersLookup() {
+ return CodeSystemLookupWithPropertiesUtil.parametersLookupWithProperties();
+ }
+
+ @ParameterizedTest
+ @MethodSource(value = "parametersLookup")
+ public void testLookup_withProperties_returnsCorrectParameters(List theLookupProperties, List theExpectedReturnedProperties) {
+ // setup
+ CodeSystem codeSystem = new CodeSystem();
+ codeSystem.setId(ourCodeSystemId);
+ codeSystem.setUrl(ourCodeSystemUrl);
+ CodeSystem.ConceptDefinitionComponent concept = codeSystem.addConcept().setCode(ourCode);
+ CodeSystem.ConceptPropertyComponent propertyComponent = new CodeSystem.ConceptPropertyComponent()
+ .setCode(ourPropertyA).setValue(new StringType(ourPropertyValueA));
+ concept.addProperty(propertyComponent);
+ propertyComponent = new CodeSystem.ConceptPropertyComponent()
+ .setCode(ourPropertyB).setValue(new StringType(ourPropertyValueB));
+ concept.addProperty(propertyComponent);
+ myCodeSystemDao.create(codeSystem, mySrd);
+
+ // test
+ IOperationUntypedWithInputAndPartialOutput respParam = myClient
+ .operation()
+ .onType(CodeSystem.class)
+ .named(JpaConstants.OPERATION_LOOKUP)
+ .withParameter(Parameters.class, "code", new CodeType(ourCode))
+ .andParameter("system", new UriType(ourCodeSystemUrl));
+
+ theLookupProperties.forEach(p -> respParam.andParameter("property", new CodeType(p)));
+ Parameters parameters = respParam.execute();
+
+
+ if (theExpectedReturnedProperties.isEmpty()) {
+ assertFalse(parameters.hasParameter("property"));
+ return;
+ }
+
+ assertTrue(parameters.hasParameter("property"));
+ Iterator parameterPropertyIterator = parameters.getParameters("property").iterator();
+
+ Iterator propertyIterator = concept.getProperty().stream()
+ .filter(property -> theExpectedReturnedProperties.contains(property.getCode())).iterator();
+
+ while (propertyIterator.hasNext()) {
+ CodeSystem.ConceptPropertyComponent property = propertyIterator.next();
+
+ assertTrue(parameterPropertyIterator.hasNext());
+ Parameters.ParametersParameterComponent parameter = parameterPropertyIterator.next();
+ Iterator parameterPartIterator = parameter.getPart().iterator();
+
+ parameter = parameterPartIterator.next();
+ assertEquals("code", parameter.getName());
+ assertEquals(property.getCode(), ((CodeType)parameter.getValue()).getCode());
+
+ parameter = parameterPartIterator.next();
+ assertEquals("value", parameter.getName());
+ assertTrue(property.getValue().equalsShallow(parameter.getValue()));
+ }
+ }
+}
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemTest.java
index 24958f4153b..06693388bee 100644
--- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemTest.java
+++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemTest.java
@@ -138,6 +138,29 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test
assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue());
}
+ @Test
+ public void testLookupOperationByCodeAndSystemWithPropertiesBuiltInCode() {
+ Parameters respParam = myClient
+ .operation()
+ .onType(CodeSystem.class)
+ .named("lookup")
+ .withParameter(Parameters.class, "code", new CodeType("ACSN"))
+ .andParameter("system", new UriType("http://terminology.hl7.org/CodeSystem/v2-0203"))
+ .execute();
+
+ String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
+ ourLog.info(resp);
+
+ assertEquals("name", respParam.getParameter().get(0).getName());
+ assertEquals("v2.0203", ((StringType) respParam.getParameter().get(0).getValue()).getValue());
+ assertEquals("version", respParam.getParameter().get(1).getName());
+ assertEquals("2.9", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
+ assertEquals("display", respParam.getParameter().get(2).getName());
+ assertEquals("Accession ID", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
+ assertEquals("abstract", respParam.getParameter().get(3).getName());
+ assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue());
+ }
+
@Test
public void testLookupOperationByCodeAndSystemBuiltInNonexistantCode() {
try {
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetNoVerCSNoVerTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetNoVerCSNoVerTest.java
index 3ac4d4ed784..7b69cc6a741 100644
--- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetNoVerCSNoVerTest.java
+++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetNoVerCSNoVerTest.java
@@ -1,5 +1,6 @@
package ca.uhn.fhir.jpa.provider.r4;
+import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
@@ -14,7 +15,6 @@ import ca.uhn.fhir.jpa.model.dao.JpaPid;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.provider.BaseResourceProviderR4Test;
-import ca.uhn.fhir.jpa.provider.ValueSetOperationProvider;
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
@@ -1295,13 +1295,13 @@ public class ResourceProviderR4ValueSetNoVerCSNoVerTest extends BaseResourceProv
String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp);
- assertEquals(ValueSetOperationProvider.RESULT, respParam.getParameter().get(0).getName());
+ assertEquals(IValidationSupport.CodeValidationResult.RESULT, respParam.getParameter().get(0).getName());
assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue());
- assertEquals(ValueSetOperationProvider.DISPLAY, respParam.getParameter().get(1).getName());
+ assertEquals(IValidationSupport.CodeValidationResult.DISPLAY, respParam.getParameter().get(1).getName());
assertEquals("Male", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
- assertEquals(ValueSetOperationProvider.SOURCE_DETAILS, respParam.getParameter().get(2).getName());
+ assertEquals(IValidationSupport.CodeValidationResult.SOURCE_DETAILS, respParam.getParameter().get(2).getName());
assertEquals("Code was validated against in-memory expansion of ValueSet: http://hl7.org/fhir/ValueSet/administrative-gender", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
}
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcDeltaR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcDeltaR4Test.java
index dd0fe20bf61..7ab96037c26 100644
--- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcDeltaR4Test.java
+++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcDeltaR4Test.java
@@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.term;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.ValidationSupportContext;
+import ca.uhn.fhir.context.support.LookupCodeRequest;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
import ca.uhn.fhir.jpa.entity.TermConcept;
@@ -369,7 +370,7 @@ public class TerminologySvcDeltaR4Test extends BaseJpaR4Test {
assertEquals("http://foo", outcome.getUrl());
assertEquals(CodeSystem.CodeSystemContentMode.NOTPRESENT, outcome.getContent());
- IValidationSupport.LookupCodeResult lookup = myTermSvc.lookupCode(new ValidationSupportContext(myValidationSupport), "http://foo", "CBC", null);
+ IValidationSupport.LookupCodeResult lookup = myTermSvc.lookupCode(new ValidationSupportContext(myValidationSupport), new LookupCodeRequest("http://foo", "CBC"));
assertEquals("Complete Blood Count", lookup.getCodeDisplay());
}
@@ -433,7 +434,8 @@ public class TerminologySvcDeltaR4Test extends BaseJpaR4Test {
UploadStatistics outcome = myTermCodeSystemStorageSvc.applyDeltaCodeSystemsAdd("http://foo", delta);
assertEquals(2, outcome.getUpdatedConceptCount());
- assertEquals("CODEA0", myTermSvc.lookupCode(new ValidationSupportContext(myValidationSupport), "http://foo", "codea", null).getCodeDisplay());
+ assertEquals("CODEA0", myTermSvc.lookupCode(new ValidationSupportContext(myValidationSupport),
+ new LookupCodeRequest("http://foo", "codea")).getCodeDisplay());
// Add codes again with different display
delta = new CustomTerminologySet();
@@ -441,12 +443,14 @@ public class TerminologySvcDeltaR4Test extends BaseJpaR4Test {
delta.addRootConcept("codeb", "CODEB1");
outcome = myTermCodeSystemStorageSvc.applyDeltaCodeSystemsAdd("http://foo", delta);
assertEquals(2, outcome.getUpdatedConceptCount());
- assertEquals("CODEA1", myTermSvc.lookupCode(new ValidationSupportContext(myValidationSupport), "http://foo", "codea", null).getCodeDisplay());
+ assertEquals("CODEA1", myTermSvc.lookupCode(new ValidationSupportContext(myValidationSupport),
+ new LookupCodeRequest("http://foo", "codea")).getCodeDisplay());
// Add codes again with no changes
outcome = myTermCodeSystemStorageSvc.applyDeltaCodeSystemsAdd("http://foo", delta);
assertEquals(2, outcome.getUpdatedConceptCount());
- assertEquals("CODEA1", myTermSvc.lookupCode(new ValidationSupportContext(myValidationSupport), "http://foo", "codea", null).getCodeDisplay());
+ assertEquals("CODEA1", myTermSvc.lookupCode(new ValidationSupportContext(myValidationSupport),
+ new LookupCodeRequest("http://foo", "codea")).getCodeDisplay());
}
@Test
@@ -481,7 +485,8 @@ public class TerminologySvcDeltaR4Test extends BaseJpaR4Test {
.setValue(new Coding("http://snomed.info", "1234567", "Choked on large meal (finding)"));
myCodeSystemDao.create(cs, mySrd);
- IValidationSupport.LookupCodeResult result = myTermSvc.lookupCode(new ValidationSupportContext(myValidationSupport), "http://foo/cs", "lunch", null);
+ IValidationSupport.LookupCodeResult result = myTermSvc.lookupCode(new ValidationSupportContext(myValidationSupport),
+ new LookupCodeRequest("http://foo/cs", "lunch"));
assertEquals(true, result.isFound());
assertEquals("lunch", result.getSearchedForCode());
assertEquals("http://foo/cs", result.getSearchedForSystem());
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplCurrentVersionR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplCurrentVersionR4Test.java
index 387609117bf..17cdf197835 100644
--- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplCurrentVersionR4Test.java
+++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplCurrentVersionR4Test.java
@@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.term;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.ValidationSupportContext;
+import ca.uhn.fhir.context.support.LookupCodeRequest;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.config.JpaConfig;
import ca.uhn.fhir.jpa.entity.TermCodeSystem;
@@ -214,13 +215,13 @@ public class TerminologySvcImplCurrentVersionR4Test extends BaseJpaR4Test {
private void validateValueLookup(String theCurrentVersion, Collection allVersions) {
IValidationSupport.LookupCodeResult resultNoVer = myValidationSupport.lookupCode(
- new ValidationSupportContext(myValidationSupport), BASE_LOINC_URL, VS_NO_VERSIONED_ON_UPLOAD_FIRST_CODE, null);
+ new ValidationSupportContext(myValidationSupport), new LookupCodeRequest(BASE_LOINC_URL, VS_NO_VERSIONED_ON_UPLOAD_FIRST_CODE));
assertNotNull(resultNoVer);
String expectedNoVer = prefixWithVersion(theCurrentVersion, VS_NO_VERSIONED_ON_UPLOAD_FIRST_DISPLAY);
assertEquals(expectedNoVer, resultNoVer.getCodeDisplay());
IValidationSupport.LookupCodeResult resultWithVer = myValidationSupport.lookupCode(
- new ValidationSupportContext(myValidationSupport), BASE_LOINC_URL, VS_VERSIONED_ON_UPLOAD_FIRST_CODE, null);
+ new ValidationSupportContext(myValidationSupport), new LookupCodeRequest(BASE_LOINC_URL, VS_VERSIONED_ON_UPLOAD_FIRST_CODE));
assertNotNull(resultWithVer);
String expectedWithVer = prefixWithVersion(theCurrentVersion, VS_VERSIONED_ON_UPLOAD_FIRST_DISPLAY);
assertEquals(expectedWithVer, resultWithVer.getCodeDisplay());
@@ -231,15 +232,15 @@ public class TerminologySvcImplCurrentVersionR4Test extends BaseJpaR4Test {
private void lookupForVersion(String theVersion) {
IValidationSupport.LookupCodeResult resultNoVer = myValidationSupport.lookupCode(
- new ValidationSupportContext(myValidationSupport), BASE_LOINC_URL + "|" + theVersion,
- VS_NO_VERSIONED_ON_UPLOAD_FIRST_CODE, null);
+ new ValidationSupportContext(myValidationSupport),
+ new LookupCodeRequest(BASE_LOINC_URL + "|" + theVersion, VS_NO_VERSIONED_ON_UPLOAD_FIRST_CODE));
assertNotNull(resultNoVer);
String expectedNoVer = prefixWithVersion(theVersion, VS_NO_VERSIONED_ON_UPLOAD_FIRST_DISPLAY);
assertEquals(expectedNoVer, resultNoVer.getCodeDisplay());
IValidationSupport.LookupCodeResult resultWithVer = myValidationSupport.lookupCode(
- new ValidationSupportContext(myValidationSupport), BASE_LOINC_URL + "|" + theVersion,
- VS_VERSIONED_ON_UPLOAD_FIRST_CODE, null);
+ new ValidationSupportContext(myValidationSupport),
+ new LookupCodeRequest(BASE_LOINC_URL + "|" + theVersion, VS_VERSIONED_ON_UPLOAD_FIRST_CODE));
assertNotNull(resultWithVer);
String expectedWithVer = prefixWithVersion(theVersion, VS_VERSIONED_ON_UPLOAD_FIRST_DISPLAY);
assertEquals(expectedWithVer, resultWithVer.getCodeDisplay());
diff --git a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5CodeSystemPropertiesTest.java b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5CodeSystemPropertiesTest.java
new file mode 100644
index 00000000000..e8b5be13fb4
--- /dev/null
+++ b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5CodeSystemPropertiesTest.java
@@ -0,0 +1,89 @@
+package ca.uhn.fhir.jpa.provider.r5;
+
+import ca.uhn.fhir.jpa.model.util.JpaConstants;
+import ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil;
+import ca.uhn.fhir.rest.gclient.IOperationUntypedWithInputAndPartialOutput;
+import org.hl7.fhir.r5.model.CodeSystem;
+import org.hl7.fhir.r5.model.CodeType;
+import org.hl7.fhir.r5.model.Parameters;
+import org.hl7.fhir.r5.model.StringType;
+import org.hl7.fhir.r5.model.UriType;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.stream.Stream;
+
+import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourCode;
+import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourCodeSystemId;
+import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourCodeSystemUrl;
+import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyA;
+import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyB;
+import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyValueA;
+import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyValueB;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class ResourceProviderR5CodeSystemPropertiesTest extends BaseResourceProviderR5Test {
+ public static Stream parametersLookup() {
+ return CodeSystemLookupWithPropertiesUtil.parametersLookupWithProperties();
+ }
+ @ParameterizedTest
+ @MethodSource(value = "parametersLookup")
+ public void testLookup_withProperties_returnsCorrectParameters(List theLookupProperties, List theExpectedReturnedProperties) {
+ // setup
+ CodeSystem codeSystem = new CodeSystem();
+ codeSystem.setId(ourCodeSystemId);
+ codeSystem.setUrl(ourCodeSystemUrl);
+ CodeSystem.ConceptDefinitionComponent concept = codeSystem.addConcept().setCode(ourCode);
+ CodeSystem.ConceptPropertyComponent propertyComponent = new CodeSystem.ConceptPropertyComponent()
+ .setCode(ourPropertyA).setValue(new StringType(ourPropertyValueA));
+ concept.addProperty(propertyComponent);
+ propertyComponent = new CodeSystem.ConceptPropertyComponent()
+ .setCode(ourPropertyB).setValue(new StringType(ourPropertyValueB));
+ concept.addProperty(propertyComponent);
+ myCodeSystemDao.create(codeSystem, mySrd);
+
+ // test
+ IOperationUntypedWithInputAndPartialOutput respParam = myClient
+ .operation()
+ .onType(CodeSystem.class)
+ .named(JpaConstants.OPERATION_LOOKUP)
+ .withParameter(Parameters.class, "code", new CodeType(ourCode))
+ .andParameter("system", new UriType(ourCodeSystemUrl));
+
+ theLookupProperties.forEach(p -> respParam.andParameter("property", new CodeType(p)));
+ Parameters parameters = respParam.execute();
+
+
+ if (theExpectedReturnedProperties.isEmpty()) {
+ assertFalse(parameters.hasParameter("property"));
+ return;
+ }
+
+ assertTrue(parameters.hasParameter("property"));
+ Iterator parameterPropertyIterator = parameters.getParameters("property").iterator();
+
+ Iterator propertyIterator = concept.getProperty().stream()
+ .filter(property -> theExpectedReturnedProperties.contains(property.getCode())).iterator();
+
+ while (propertyIterator.hasNext()) {
+ CodeSystem.ConceptPropertyComponent property = propertyIterator.next();
+
+ assertTrue(parameterPropertyIterator.hasNext());
+ Parameters.ParametersParameterComponent parameter = parameterPropertyIterator.next();
+ Iterator parameterPartIterator = parameter.getPart().iterator();
+
+ parameter = parameterPartIterator.next();
+ assertEquals("code", parameter.getName());
+ assertEquals(property.getCode(), ((CodeType)parameter.getValue()).getCode());
+
+ parameter = parameterPartIterator.next();
+ assertEquals("value", parameter.getName());
+ assertTrue(property.getValue().equalsShallow(parameter.getValue()));
+ }
+ }
+}
diff --git a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetTest.java b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetTest.java
index 6d9b8a299c7..a40eddf4e05 100644
--- a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetTest.java
+++ b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetTest.java
@@ -1,5 +1,6 @@
package ca.uhn.fhir.jpa.provider.r5;
+import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
@@ -12,7 +13,6 @@ import ca.uhn.fhir.jpa.entity.TermValueSetConcept;
import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum;
import ca.uhn.fhir.jpa.model.dao.JpaPid;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
-import ca.uhn.fhir.jpa.provider.ValueSetOperationProvider;
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
@@ -1229,13 +1229,13 @@ public class ResourceProviderR5ValueSetTest extends BaseResourceProviderR5Test {
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp);
- assertEquals(ValueSetOperationProvider.RESULT, respParam.getParameter().get(0).getName());
+ assertEquals(IValidationSupport.CodeValidationResult.RESULT, respParam.getParameter().get(0).getName());
assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue());
- assertEquals(ValueSetOperationProvider.DISPLAY, respParam.getParameter().get(1).getName());
+ assertEquals(IValidationSupport.CodeValidationResult.DISPLAY, respParam.getParameter().get(1).getName());
assertEquals("Male", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
- assertEquals(ValueSetOperationProvider.SOURCE_DETAILS, respParam.getParameter().get(2).getName());
+ assertEquals(IValidationSupport.CodeValidationResult.SOURCE_DETAILS, respParam.getParameter().get(2).getName());
assertEquals("Code was validated against in-memory expansion of ValueSet: http://hl7.org/fhir/ValueSet/administrative-gender", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
}
diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/provider/CodeSystemLookupWithPropertiesUtil.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/provider/CodeSystemLookupWithPropertiesUtil.java
new file mode 100644
index 00000000000..feedbd05f0e
--- /dev/null
+++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/provider/CodeSystemLookupWithPropertiesUtil.java
@@ -0,0 +1,28 @@
+package ca.uhn.fhir.jpa.provider;
+
+import org.junit.jupiter.params.provider.Arguments;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.params.provider.Arguments.arguments;
+
+public class CodeSystemLookupWithPropertiesUtil {
+ public static final String ourCodeSystemId = "CodeSystem-Example",
+ ourCodeSystemUrl = "http://example/" + ourCodeSystemId;
+ public static final String ourCode = "Code-WithProperties";
+ public static final String ourPropertyA = "Property-A", ourPropertyB = "Property-B";
+ public static final String ourPropertyValueA = "Value-A", ourPropertyValueB = "Value-B";
+
+ public static Stream parametersLookupWithProperties() {
+ return Stream.of(
+ arguments(List.of(ourPropertyB), List.of(ourPropertyB)),
+ arguments(List.of(ourPropertyA, ourPropertyB), List.of(ourPropertyA, ourPropertyB)),
+ arguments(List.of(ourPropertyB, ourPropertyA), List.of(ourPropertyB, ourPropertyA)),
+ arguments(List.of(ourPropertyA, ourPropertyA), List.of(ourPropertyA, ourPropertyA)),
+ arguments(List.of(ourPropertyB, "ABC"), List.of(ourPropertyB)),
+ arguments(List.of("ABC", ourPropertyA), List.of(ourPropertyA)),
+ arguments(List.of("ABC"), Collections.emptyList()));
+ }
+}
diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseTerminologyDisplayPopulationInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseTerminologyDisplayPopulationInterceptor.java
index c6a7c901ee6..e86facffe09 100644
--- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseTerminologyDisplayPopulationInterceptor.java
+++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseTerminologyDisplayPopulationInterceptor.java
@@ -24,6 +24,7 @@ import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
import ca.uhn.fhir.context.RuntimePrimitiveDatatypeDefinition;
import ca.uhn.fhir.context.support.IValidationSupport;
+import ca.uhn.fhir.context.support.LookupCodeRequest;
import ca.uhn.fhir.context.support.ValidationSupportContext;
import ca.uhn.fhir.interceptor.api.Hook;
import ca.uhn.fhir.interceptor.api.Pointcut;
@@ -122,8 +123,8 @@ public class ResponseTerminologyDisplayPopulationInterceptor extends BaseRespons
ValidationSupportContext validationSupportContext = new ValidationSupportContext(myValidationSupport);
if (myValidationSupport.isCodeSystemSupported(validationSupportContext, system)) {
- IValidationSupport.LookupCodeResult lookupCodeResult =
- myValidationSupport.lookupCode(validationSupportContext, system, code);
+ IValidationSupport.LookupCodeResult lookupCodeResult = myValidationSupport.lookupCode(
+ validationSupportContext, new LookupCodeRequest(system, code));
if (lookupCodeResult != null && lookupCodeResult.isFound()) {
String newDisplay = lookupCodeResult.getCodeDisplay();
IPrimitiveType> newString = myStringDefinition.newInstance(newDisplay);
diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoCodeSystem.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoCodeSystem.java
index bf323ac8226..3ac4d312cd1 100644
--- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoCodeSystem.java
+++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoCodeSystem.java
@@ -32,6 +32,7 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4.model.codesystems.ConceptSubsumptionOutcome;
import org.springframework.transaction.annotation.Transactional;
+import java.util.Collection;
import java.util.List;
import javax.annotation.Nonnull;
@@ -55,6 +56,15 @@ public interface IFhirResourceDaoCodeSystem extends IFh
IPrimitiveType theDisplayLanguage,
RequestDetails theRequestDetails);
+ @Nonnull
+ IValidationSupport.LookupCodeResult lookupCode(
+ IPrimitiveType theCode,
+ IPrimitiveType theSystem,
+ IBaseCoding theCoding,
+ IPrimitiveType theDisplayLanguage,
+ Collection> thePropertyNames,
+ RequestDetails theRequestDetails);
+
SubsumesResult subsumes(
IPrimitiveType theCodeA,
IPrimitiveType theCodeB,
diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/BaseValidationSupportWrapper.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/BaseValidationSupportWrapper.java
index e14a686e208..ecd220a3b98 100644
--- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/BaseValidationSupportWrapper.java
+++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/BaseValidationSupportWrapper.java
@@ -3,6 +3,7 @@ package org.hl7.fhir.common.hapi.validation.support;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.ConceptValidationOptions;
import ca.uhn.fhir.context.support.IValidationSupport;
+import ca.uhn.fhir.context.support.LookupCodeRequest;
import ca.uhn.fhir.context.support.TranslateConceptResults;
import ca.uhn.fhir.context.support.ValidationSupportContext;
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
@@ -98,11 +99,8 @@ public abstract class BaseValidationSupportWrapper extends BaseValidationSupport
@Override
public LookupCodeResult lookupCode(
- ValidationSupportContext theValidationSupportContext,
- String theSystem,
- String theCode,
- String theDisplayLanguage) {
- return myWrap.lookupCode(theValidationSupportContext, theSystem, theCode, theDisplayLanguage);
+ ValidationSupportContext theValidationSupportContext, @Nonnull LookupCodeRequest theLookupCodeRequest) {
+ return myWrap.lookupCode(theValidationSupportContext, theLookupCodeRequest);
}
@Override
diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CachingValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CachingValidationSupport.java
index 873a854f1f3..82d86f234fb 100644
--- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CachingValidationSupport.java
+++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CachingValidationSupport.java
@@ -3,6 +3,7 @@ package org.hl7.fhir.common.hapi.validation.support;
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.support.ConceptValidationOptions;
import ca.uhn.fhir.context.support.IValidationSupport;
+import ca.uhn.fhir.context.support.LookupCodeRequest;
import ca.uhn.fhir.context.support.TranslateConceptResults;
import ca.uhn.fhir.context.support.ValidationSupportContext;
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
@@ -195,15 +196,13 @@ public class CachingValidationSupport extends BaseValidationSupportWrapper imple
@Override
public LookupCodeResult lookupCode(
- ValidationSupportContext theValidationSupportContext,
- String theSystem,
- String theCode,
- String theDisplayLanguage) {
- String key = "lookupCode " + theSystem + " " + theCode + " " + defaultIfBlank(theDisplayLanguage, "NO_LANG");
+ ValidationSupportContext theValidationSupportContext, @Nonnull LookupCodeRequest theLookupCodeRequest) {
+ String key = "lookupCode " + theLookupCodeRequest.getSystem() + " "
+ + theLookupCodeRequest.getCode()
+ + " " + defaultIfBlank(theLookupCodeRequest.getDisplayLanguage(), "NO_LANG")
+ + " " + theLookupCodeRequest.getPropertyNames().toString();
return loadFromCache(
- myLookupCodeCache,
- key,
- t -> super.lookupCode(theValidationSupportContext, theSystem, theCode, theDisplayLanguage));
+ myLookupCodeCache, key, t -> super.lookupCode(theValidationSupportContext, theLookupCodeRequest));
}
@Override
diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java
index af9cd737566..f334cbcf29b 100644
--- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java
+++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java
@@ -5,6 +5,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.support.ConceptValidationOptions;
import ca.uhn.fhir.context.support.IValidationSupport;
+import ca.uhn.fhir.context.support.LookupCodeRequest;
import ca.uhn.fhir.context.support.ValidationSupportContext;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.util.ClasspathUtil;
@@ -221,7 +222,8 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport {
@Nullable
public CodeValidationResult validateLookupCode(
ValidationSupportContext theValidationSupportContext, String theCode, String theSystem) {
- LookupCodeResult lookupResult = lookupCode(theValidationSupportContext, theSystem, theCode);
+ LookupCodeResult lookupResult =
+ lookupCode(theValidationSupportContext, new LookupCodeRequest(theSystem, theCode));
CodeValidationResult validationResult = null;
if (lookupResult != null) {
if (lookupResult.isFound()) {
@@ -240,18 +242,18 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport {
@Override
public LookupCodeResult lookupCode(
- ValidationSupportContext theValidationSupportContext,
- String theSystem,
- String theCode,
- String theDisplayLanguage) {
+ ValidationSupportContext theValidationSupportContext, @Nonnull LookupCodeRequest theLookupCodeRequest) {
+ final String code = theLookupCodeRequest.getCode();
+ final String system = theLookupCodeRequest.getSystem();
+
Map map;
- switch (theSystem) {
+ switch (system) {
case LANGUAGES_CODESYSTEM_URL:
- return lookupLanguageCode(theCode);
+ return lookupLanguageCode(code);
case UCUM_CODESYSTEM_URL:
- return lookupUcumCode(theCode);
+ return lookupUcumCode(code);
case MIMETYPES_CODESYSTEM_URL:
- return lookupMimetypeCode(theCode);
+ return lookupMimetypeCode(code);
case COUNTRIES_CODESYSTEM_URL:
map = ISO_3166_CODES;
break;
@@ -265,11 +267,11 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport {
return null;
}
- String display = map.get(theCode);
+ String display = map.get(code);
if (isNotBlank(display)) {
LookupCodeResult retVal = new LookupCodeResult();
- retVal.setSearchedForCode(theCode);
- retVal.setSearchedForSystem(theSystem);
+ retVal.setSearchedForCode(code);
+ retVal.setSearchedForSystem(system);
retVal.setFound(true);
retVal.setCodeDisplay(display);
return retVal;
@@ -277,10 +279,10 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport {
// If we get here it means we know the codesystem but the code was bad
LookupCodeResult retVal = new LookupCodeResult();
- retVal.setSearchedForCode(theCode);
- retVal.setSearchedForSystem(theSystem);
+ retVal.setSearchedForCode(code);
+ retVal.setSearchedForSystem(system);
retVal.setFound(false);
- retVal.setErrorMessage("Code '" + theCode + "' is not valid for system: " + theSystem);
+ retVal.setErrorMessage("Code '" + code + "' is not valid for system: " + system);
return retVal;
}
@@ -384,7 +386,7 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport {
String language = theNext.get("Subtag").asText();
ArrayNode descriptions = (ArrayNode) theNext.get("Description");
String description = null;
- if (descriptions.size() > 0) {
+ if (!descriptions.isEmpty()) {
description = descriptions.get(0).asText();
}
theLanguagesMap.put(language, description);
@@ -403,7 +405,7 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport {
@Nonnull
private LookupCodeResult lookupUcumCode(String theCode) {
InputStream input = ClasspathUtil.loadResourceAsStream("/ucum-essence.xml");
- String outcome = null;
+ String outcome;
LookupCodeResult retVal = new LookupCodeResult();
retVal.setSearchedForCode(theCode);
retVal.setSearchedForSystem(UCUM_CODESYSTEM_URL);
diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java
index a2a709c0be6..2fa1c80befc 100644
--- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java
+++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java
@@ -4,6 +4,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.support.ConceptValidationOptions;
import ca.uhn.fhir.context.support.IValidationSupport;
+import ca.uhn.fhir.context.support.LookupCodeRequest;
import ca.uhn.fhir.context.support.ValidationSupportContext;
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
import ca.uhn.fhir.i18n.Msg;
@@ -611,16 +612,20 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
@Override
public LookupCodeResult lookupCode(
- ValidationSupportContext theValidationSupportContext,
- String theSystem,
- String theCode,
- String theDisplayLanguage) {
+ ValidationSupportContext theValidationSupportContext, @Nonnull LookupCodeRequest theLookupCodeRequest) {
+ final String code = theLookupCodeRequest.getCode();
+ final String system = theLookupCodeRequest.getSystem();
CodeValidationResult codeValidationResult = validateCode(
- theValidationSupportContext, new ConceptValidationOptions(), theSystem, theCode, null, null);
+ theValidationSupportContext,
+ new ConceptValidationOptions(),
+ system,
+ code,
+ theLookupCodeRequest.getDisplayLanguage(),
+ null);
if (codeValidationResult == null) {
return null;
}
- return codeValidationResult.asLookupCodeResult(theSystem, theCode);
+ return codeValidationResult.asLookupCodeResult(system, code);
}
@Nullable
@@ -927,9 +932,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
.getRootValidationSupport()
.lookupCode(
theValidationSupportContext,
- includeOrExcludeConceptSystemUrl,
- theWantCode,
- null);
+ new LookupCodeRequest(includeOrExcludeConceptSystemUrl, theWantCode));
if (lookup != null) {
ableToHandleCode = true;
if (lookup.isFound()) {
diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupport.java
index a889d36f05e..3bd5d2db48b 100644
--- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupport.java
+++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupport.java
@@ -5,6 +5,7 @@ import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.support.ConceptValidationOptions;
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
import ca.uhn.fhir.context.support.IValidationSupport;
+import ca.uhn.fhir.context.support.LookupCodeRequest;
import ca.uhn.fhir.context.support.TranslateConceptResults;
import ca.uhn.fhir.context.support.ValidationSupportContext;
import ca.uhn.fhir.i18n.Msg;
@@ -183,11 +184,11 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
@Override
public LookupCodeResult lookupCode(
- ValidationSupportContext theValidationSupportContext,
- String theSystem,
- String theCode,
- String theDisplayLanguage) {
- Validate.notBlank(theCode, "theCode must be provided");
+ ValidationSupportContext theValidationSupportContext, @Nonnull LookupCodeRequest theLookupCodeRequest) {
+ final String code = theLookupCodeRequest.getCode();
+ final String system = theLookupCodeRequest.getSystem();
+ final String displayLanguage = theLookupCodeRequest.getDisplayLanguage();
+ Validate.notBlank(code, "theCode must be provided");
IGenericClient client = provideClient();
FhirContext fhirContext = client.getFhirContext();
@@ -197,17 +198,20 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
case DSTU3:
case R4:
IBaseParameters params = ParametersUtil.newInstance(fhirContext);
- ParametersUtil.addParameterToParametersString(fhirContext, params, "code", theCode);
- if (!StringUtils.isEmpty(theSystem)) {
- ParametersUtil.addParameterToParametersString(fhirContext, params, "system", theSystem);
+ ParametersUtil.addParameterToParametersString(fhirContext, params, "code", code);
+ if (!StringUtils.isEmpty(system)) {
+ ParametersUtil.addParameterToParametersString(fhirContext, params, "system", system);
}
- if (!StringUtils.isEmpty(theDisplayLanguage)) {
- ParametersUtil.addParameterToParametersString(fhirContext, params, "language", theDisplayLanguage);
+ if (!StringUtils.isEmpty(displayLanguage)) {
+ ParametersUtil.addParameterToParametersString(fhirContext, params, "language", displayLanguage);
}
- Class> codeSystemClass =
+ for (String propertyName : theLookupCodeRequest.getPropertyNames()) {
+ ParametersUtil.addParameterToParametersString(fhirContext, params, "property", propertyName);
+ }
+ Class extends IBaseResource> codeSystemClass =
myCtx.getResourceDefinition("CodeSystem").getImplementingClass();
IBaseParameters outcome = client.operation()
- .onType((Class extends IBaseResource>) codeSystemClass)
+ .onType(codeSystemClass)
.named("$lookup")
.withParameters(params)
.useHttpGet()
@@ -216,10 +220,9 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
switch (fhirVersion) {
case DSTU3:
return generateLookupCodeResultDSTU3(
- theCode, theSystem, (org.hl7.fhir.dstu3.model.Parameters) outcome);
+ code, system, (org.hl7.fhir.dstu3.model.Parameters) outcome);
case R4:
- return generateLookupCodeResultR4(
- theCode, theSystem, (org.hl7.fhir.r4.model.Parameters) outcome);
+ return generateLookupCodeResultR4(code, system, (org.hl7.fhir.r4.model.Parameters) outcome);
}
}
break;
diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/UnknownCodeSystemWarningValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/UnknownCodeSystemWarningValidationSupport.java
index 0b4be8d9006..f8493bbaf03 100644
--- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/UnknownCodeSystemWarningValidationSupport.java
+++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/UnknownCodeSystemWarningValidationSupport.java
@@ -2,6 +2,7 @@ package org.hl7.fhir.common.hapi.validation.support;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.ConceptValidationOptions;
+import ca.uhn.fhir.context.support.LookupCodeRequest;
import ca.uhn.fhir.context.support.ValidationSupportContext;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IBaseResource;
@@ -51,12 +52,9 @@ public class UnknownCodeSystemWarningValidationSupport extends BaseValidationSup
@Nullable
@Override
public LookupCodeResult lookupCode(
- ValidationSupportContext theValidationSupportContext,
- String theSystem,
- String theCode,
- String theDisplayLanguage) {
+ ValidationSupportContext theValidationSupportContext, @Nonnull LookupCodeRequest theLookupCodeRequest) {
// filters out error/fatal
- if (canValidateCodeSystem(theValidationSupportContext, theSystem)) {
+ if (canValidateCodeSystem(theValidationSupportContext, theLookupCodeRequest.getSystem())) {
return new LookupCodeResult().setFound(true);
}
diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChain.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChain.java
index 2b31d6852d3..65b6245fb29 100644
--- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChain.java
+++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChain.java
@@ -5,6 +5,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.support.ConceptValidationOptions;
import ca.uhn.fhir.context.support.IValidationSupport;
+import ca.uhn.fhir.context.support.LookupCodeRequest;
import ca.uhn.fhir.context.support.TranslateConceptResults;
import ca.uhn.fhir.context.support.ValidationSupportContext;
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
@@ -416,20 +417,26 @@ public class ValidationSupportChain implements IValidationSupport {
@Override
public LookupCodeResult lookupCode(
- ValidationSupportContext theValidationSupportContext,
- String theSystem,
- String theCode,
- String theDisplayLanguage) {
+ ValidationSupportContext theValidationSupportContext, @Nonnull LookupCodeRequest theLookupCodeRequest) {
for (IValidationSupport next : myChain) {
- if (next.isCodeSystemSupported(theValidationSupportContext, theSystem)) {
- LookupCodeResult lookupCodeResult =
- next.lookupCode(theValidationSupportContext, theSystem, theCode, theDisplayLanguage);
+ final String system = theLookupCodeRequest.getSystem();
+ final String code = theLookupCodeRequest.getCode();
+ final String displayLanguage = theLookupCodeRequest.getDisplayLanguage();
+ if (next.isCodeSystemSupported(theValidationSupportContext, system)) {
+ LookupCodeResult lookupCodeResult = next.lookupCode(theValidationSupportContext, theLookupCodeRequest);
+ if (lookupCodeResult == null) {
+ /*
+ This branch has been added as a fall-back mechanism for supporting lookupCode
+ methods marked as deprecated in interface IValidationSupport.
+ */
+ lookupCodeResult = next.lookupCode(theValidationSupportContext, system, code, displayLanguage);
+ }
if (ourLog.isDebugEnabled()) {
ourLog.debug(
"Code {}|{}{} {} by {}",
- theSystem,
- theCode,
- isBlank(theDisplayLanguage) ? "" : " (" + theDisplayLanguage + ")",
+ system,
+ code,
+ isBlank(displayLanguage) ? "" : " (" + theLookupCodeRequest.getDisplayLanguage() + ")",
lookupCodeResult != null && lookupCodeResult.isFound() ? "found" : "not found",
next.getName());
}
diff --git a/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/interceptor/ResponseTerminologyDisplayPopulationInterceptorTest.java b/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/interceptor/ResponseTerminologyDisplayPopulationInterceptorTest.java
index 2b476d65a31..99f5a4c00e6 100644
--- a/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/interceptor/ResponseTerminologyDisplayPopulationInterceptorTest.java
+++ b/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/interceptor/ResponseTerminologyDisplayPopulationInterceptorTest.java
@@ -138,16 +138,6 @@ public class ResponseTerminologyDisplayPopulationInterceptorTest {
public boolean isCodeSystemSupported(ValidationSupportContext theValidationSupportContext, String theSystem) {
return true;
}
-
- @Override
- public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theDisplayLanguage) {
- return null;
- }
-
- @Override
- public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode) {
- return lookupCode(theValidationSupportContext, theSystem, theCode, null);
- }
}
}
diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyServiceTest.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyServiceTest.java
index dc0d72961a0..cb8c812949c 100644
--- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyServiceTest.java
+++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyServiceTest.java
@@ -4,6 +4,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.ConceptValidationOptions;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.ValidationSupportContext;
+import ca.uhn.fhir.context.support.LookupCodeRequest;
import ca.uhn.fhir.i18n.Msg;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.CodeSystem;
@@ -31,35 +32,35 @@ public class CommonCodeSystemsTerminologyServiceTest {
@Test
public void testUcum_LookupCode_Good() {
- IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(newSupport(), "http://unitsofmeasure.org", "Cel");
+ IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(newSupport(), new LookupCodeRequest("http://unitsofmeasure.org", "Cel"));
assert outcome != null;
assertTrue(outcome.isFound());
}
@Test
public void testUcum_LookupCode_Good2() {
- IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(newSupport(), "http://unitsofmeasure.org", "kg/m2");
+ IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(newSupport(), new LookupCodeRequest("http://unitsofmeasure.org", "kg/m2"));
assert outcome != null;
assertTrue(outcome.isFound());
}
@Test
public void testUcum_LookupCode_Bad() {
- IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(newSupport(), "http://unitsofmeasure.org", "AAAAA");
+ IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(newSupport(), new LookupCodeRequest("http://unitsofmeasure.org", "AAAAA"));
assert outcome != null;
assertFalse(outcome.isFound());
}
@Test
public void testUcum_LookupCode_UnknownSystem() {
- IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(newSupport(), "http://foo", "AAAAA", null);
+ IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(newSupport(), new LookupCodeRequest("http://foo", "AAAAA"));
assertNull(outcome);
}
@Test
public void lookupCode_languageOnlyLookup_isCaseInsensitive() {
- IValidationSupport.LookupCodeResult outcomeUpper = mySvc.lookupCode(newSupport(), "urn:ietf:bcp:47", "SGN", "Sign Languages");
- IValidationSupport.LookupCodeResult outcomeLower = mySvc.lookupCode(newSupport(), "urn:ietf:bcp:47", "sgn", "Sign Languages");
+ IValidationSupport.LookupCodeResult outcomeUpper = mySvc.lookupCode(newSupport(), new LookupCodeRequest("urn:ietf:bcp:47", "SGN", "Sign Languages", null));
+ IValidationSupport.LookupCodeResult outcomeLower = mySvc.lookupCode(newSupport(), new LookupCodeRequest("urn:ietf:bcp:47", "sgn", "Sign Languages", null));
assertNotNull(outcomeUpper);
assertNotNull(outcomeLower);
assertTrue(outcomeLower.isFound());
@@ -68,8 +69,8 @@ public class CommonCodeSystemsTerminologyServiceTest {
@Test
public void lookupCode_languageAndRegionLookup_isCaseInsensitive() {
- IValidationSupport.LookupCodeResult outcomeUpper = mySvc.lookupCode(newSupport(), "urn:ietf:bcp:47", "EN-US", "English");
- IValidationSupport.LookupCodeResult outcomeLower = mySvc.lookupCode(newSupport(), "urn:ietf:bcp:47", "en-us", "English");
+ IValidationSupport.LookupCodeResult outcomeUpper = mySvc.lookupCode(newSupport(), new LookupCodeRequest("urn:ietf:bcp:47", "EN-US", "English", null));
+ IValidationSupport.LookupCodeResult outcomeLower = mySvc.lookupCode(newSupport(), new LookupCodeRequest("urn:ietf:bcp:47", "en-us", "English", null));
assertNotNull(outcomeUpper);
assertNotNull(outcomeLower);
assertTrue(outcomeLower.isFound());
@@ -131,14 +132,14 @@ public class CommonCodeSystemsTerminologyServiceTest {
@Test
public void testLanguages_CommonLanguagesVs_OnlyLanguage_NoRegion() {
- IValidationSupport.LookupCodeResult nl = mySvc.lookupCode(newSupport(), "urn:ietf:bcp:47", "nl");
+ IValidationSupport.LookupCodeResult nl = mySvc.lookupCode(newSupport(), new LookupCodeRequest("urn:ietf:bcp:47", "nl"));
assertTrue(nl.isFound());
assertEquals("Dutch", nl.getCodeDisplay());
}
@Test
public void testLanguages_CommonLanguagesVs_LanguageAndRegion() {
- IValidationSupport.LookupCodeResult nl = mySvc.lookupCode(newSupport(), "urn:ietf:bcp:47", "nl-NL");
+ IValidationSupport.LookupCodeResult nl = mySvc.lookupCode(newSupport(), new LookupCodeRequest("urn:ietf:bcp:47", "nl-NL"));
assertTrue(nl.isFound());
assertEquals("Dutch Netherlands", nl.getCodeDisplay());
}
diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupportTest.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupportTest.java
index 99e8b754c3a..ebbf218b0e8 100644
--- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupportTest.java
+++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupportTest.java
@@ -5,6 +5,7 @@ import ca.uhn.fhir.context.support.ConceptValidationOptions;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.TranslateConceptResult;
import ca.uhn.fhir.context.support.TranslateConceptResults;
+import ca.uhn.fhir.context.support.LookupCodeRequest;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.parser.IJsonLikeParser;
import ca.uhn.fhir.rest.annotation.IdParam;
@@ -24,6 +25,7 @@ import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
import ca.uhn.fhir.util.ParametersUtil;
import com.google.common.collect.Lists;
import org.hl7.fhir.instance.model.api.IBaseCoding;
+import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.BooleanType;
import org.hl7.fhir.r4.model.CodeSystem;
@@ -47,11 +49,14 @@ import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Iterator;
import java.util.List;
+import java.util.Set;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.lessThan;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -60,8 +65,7 @@ public class RemoteTerminologyServiceValidationSupportTest {
private static final String DISPLAY = "DISPLAY";
private static final String LANGUAGE = "en";
private static final String CODE_SYSTEM = "CODE_SYS";
- private static final String CODE_SYSTEM_VERSION = "2.1";
- private static final String CODE_SYSTEM_VERSION_AS_TEXT = "v2.1.12";
+ private static final String CODE_SYSTEM_NAME = "Code System";
private static final String CODE = "CODE";
private static final String VALUE_SET_URL = "http://value.set/url";
private static final String TARGET_SYSTEM = "http://target.system/url";
@@ -77,10 +81,10 @@ public class RemoteTerminologyServiceValidationSupportTest {
private static final String ERROR_MESSAGE = "This is an error message";
private static final String SUCCESS_MESSAGE = "This is a success message";
- private static FhirContext ourCtx = FhirContext.forR4Cached();
+ private static final FhirContext ourCtx = FhirContext.forR4Cached();
@RegisterExtension
- public RestfulServerExtension myRestfulServerExtension = new RestfulServerExtension(ourCtx);
+ public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
private MyValueSetProvider myValueSetProvider;
private RemoteTerminologyServiceValidationSupport mySvc;
@@ -90,15 +94,15 @@ public class RemoteTerminologyServiceValidationSupportTest {
@BeforeEach
public void before() {
myValueSetProvider = new MyValueSetProvider();
- myRestfulServerExtension.getRestfulServer().registerProvider(myValueSetProvider);
+ ourRestfulServerExtension.getRestfulServer().registerProvider(myValueSetProvider);
myCodeSystemProvider = new MyCodeSystemProvider();
- myRestfulServerExtension.getRestfulServer().registerProvider(myCodeSystemProvider);
+ ourRestfulServerExtension.getRestfulServer().registerProvider(myCodeSystemProvider);
myConceptMapProvider = new MyConceptMapProvider();
- myRestfulServerExtension.getRestfulServer().registerProvider(myConceptMapProvider);
+ ourRestfulServerExtension.getRestfulServer().registerProvider(myConceptMapProvider);
- String baseUrl = "http://localhost:" + myRestfulServerExtension.getPort();
+ String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx);
mySvc.setBaseUrl(baseUrl);
@@ -111,69 +115,98 @@ public class RemoteTerminologyServiceValidationSupportTest {
}
@Test
- public void testValidateCode_SystemCodeDisplayUrl_BlankCode() {
+ public void testValidateCode_withBlankCode_returnsNull() {
IValidationSupport.CodeValidationResult outcome = mySvc.validateCode(null, null, CODE_SYSTEM, "", DISPLAY, VALUE_SET_URL);
- assertEquals(null, outcome);
+ assertNull(outcome);
}
- @Test
- public void testLookupOperation_CodeSystem_Success() {
- createNextCodeSystemLookupReturnParameters(true, CODE_SYSTEM_VERSION, CODE_SYSTEM_VERSION_AS_TEXT,
- DISPLAY, null);
- IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(null, CODE_SYSTEM, CODE);
+ @Test
+ public void testLookupCode_forCodeSystemWithAllParams_returnsCorrectParameters() {
+ myCodeSystemProvider.myNextLookupCodeResult = new IValidationSupport.LookupCodeResult();
+ myCodeSystemProvider.myNextLookupCodeResult.setFound(true);
+ myCodeSystemProvider.myNextLookupCodeResult.setCodeSystemVersion(CODE_SYSTEM);
+ myCodeSystemProvider.myNextLookupCodeResult.setSearchedForCode(CODE);
+ myCodeSystemProvider.myNextLookupCodeResult.setCodeSystemDisplayName(CODE_SYSTEM_NAME);
+ myCodeSystemProvider.myNextLookupCodeResult.setCodeDisplay(DISPLAY);
+
+ // property
+ String propertyName = "birthDate";
+ String propertyValue = "1930-01-01";
+ IValidationSupport.BaseConceptProperty property = new IValidationSupport.StringConceptProperty(propertyName, propertyValue);
+ myCodeSystemProvider.myNextLookupCodeResult.getProperties().add(property);
+
+ // designation
+ IValidationSupport.ConceptDesignation designation = new IValidationSupport.ConceptDesignation();
+ designation.setLanguage("en");
+ designation.setUseCode("code");
+ designation.setUseSystem("system");
+ designation.setUseDisplay("display");
+ designation.setValue("some value");
+ myCodeSystemProvider.myNextLookupCodeResult.getDesignations().add(designation);
+
+ IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(null, new LookupCodeRequest(CODE_SYSTEM, CODE, null, Set.of("birthDate")));
assertNotNull(outcome, "Call to lookupCode() should return a non-NULL result!");
assertEquals(DISPLAY, outcome.getCodeDisplay());
- assertEquals(CODE_SYSTEM_VERSION, outcome.getCodeSystemVersion());
+ assertEquals(CODE_SYSTEM, outcome.getCodeSystemVersion());
+ assertEquals(CODE_SYSTEM_NAME, myCodeSystemProvider.myNextReturnParams.getParameterValue("name").toString());
assertEquals(CODE, myCodeSystemProvider.myLastCode.getCode());
assertEquals(CODE_SYSTEM, myCodeSystemProvider.myLastUrl.getValueAsString());
- assertEquals(CODE_SYSTEM_VERSION_AS_TEXT, myCodeSystemProvider.myNextReturnParams.getParameterValue("name").toString());
- assertTrue(Boolean.parseBoolean(myCodeSystemProvider.myNextReturnParams.getParameterValue("result").primitiveValue()));
+
+ Parameters.ParametersParameterComponent propertyComponent = myCodeSystemProvider.myNextReturnParams.getParameter("property");
+ assertNotNull(propertyComponent);
+
+ Iterator propertyComponentIterator = propertyComponent.getPart().iterator();
+ propertyComponent = propertyComponentIterator.next();
+ assertEquals("code", propertyComponent.getName());
+ assertEquals(propertyName, ((StringType)propertyComponent.getValue()).getValue());
+
+ propertyComponent = propertyComponentIterator.next();
+ assertEquals("value", propertyComponent.getName());
+ assertEquals(propertyValue, ((StringType)propertyComponent.getValue()).getValue());
+
+ Parameters.ParametersParameterComponent designationComponent = myCodeSystemProvider.myNextReturnParams.getParameter("designation");
+ Iterator partParameter = designationComponent.getPart().iterator();
+ designationComponent = partParameter.next();
+ assertEquals("language", designationComponent.getName());
+ assertEquals(LANGUAGE, designationComponent.getValue().toString());
+
+ designationComponent = partParameter.next();
+ assertEquals("use", designationComponent.getName());
+ Coding coding = (Coding)designationComponent.getValue();
+ assertNotNull(coding, "Coding value returned via designation use should NOT be NULL!");
+ assertEquals("code", coding.getCode());
+ assertEquals("system", coding.getSystem());
+ assertEquals("display", coding.getDisplay());
+
+ designationComponent = partParameter.next();
+ assertEquals("value", designationComponent.getName());
+ assertEquals("some value", designationComponent.getValue().toString());
}
@Test
- public void testLookupOperationWithAllParams_CodeSystem_Success() {
- createNextCodeSystemLookupReturnParameters(true, CODE_SYSTEM_VERSION, CODE_SYSTEM_VERSION_AS_TEXT,
- DISPLAY, null);
- addAdditionalReturnParameters();
-
- IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(null, CODE_SYSTEM, CODE);
- assertNotNull(outcome, "Call to lookupCode() should return a non-NULL result!");
- assertEquals(DISPLAY, outcome.getCodeDisplay());
- assertEquals(CODE_SYSTEM_VERSION, outcome.getCodeSystemVersion());
- assertEquals(CODE_SYSTEM_VERSION_AS_TEXT, myCodeSystemProvider.myNextReturnParams.getParameterValue("name").toString());
-
- assertEquals(CODE, myCodeSystemProvider.myLastCode.getCode());
- assertEquals(CODE_SYSTEM, myCodeSystemProvider.myLastUrl.getValueAsString());
- assertTrue(Boolean.parseBoolean(myCodeSystemProvider.myNextReturnParams.getParameterValue("result").primitiveValue()));
-
- validateExtraCodeSystemParams();
+ public void testLookupCode_forCodeSystemWithBlankCode_throwsException() {
+ Assertions.assertThrows(IllegalArgumentException.class,
+ () -> mySvc.lookupCode(null, new LookupCodeRequest(CODE_SYSTEM, "")));
}
@Test
- public void testLookupCode_BlankCode_ThrowsException() {
- Assertions.assertThrows(IllegalArgumentException.class, () -> {
- IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(null, CODE_SYSTEM,
- "", null);
- });
- }
-
- @Test
- public void testValidateCode_ValueSet_Success() {
+ public void testValidateCode_forValueSet_returnsCorrectly() {
createNextValueSetReturnParameters(true, DISPLAY, null);
IValidationSupport.CodeValidationResult outcome = mySvc.validateCode(null, null, CODE_SYSTEM, CODE, DISPLAY, VALUE_SET_URL);
+ assertNotNull(outcome);
assertEquals(CODE, outcome.getCode());
assertEquals(DISPLAY, outcome.getDisplay());
- assertEquals(null, outcome.getSeverity());
- assertEquals(null, outcome.getMessage());
+ assertNull(outcome.getSeverity());
+ assertNull(outcome.getMessage());
assertEquals(CODE, myValueSetProvider.myLastCode.getCode());
assertEquals(DISPLAY, myValueSetProvider.myLastDisplay.getValue());
assertEquals(CODE_SYSTEM, myValueSetProvider.myLastSystem.getValue());
assertEquals(VALUE_SET_URL, myValueSetProvider.myLastUrl.getValue());
- assertEquals(null, myValueSetProvider.myLastValueSet);
+ assertNull(myValueSetProvider.myLastValueSet);
}
@Test
@@ -182,75 +215,20 @@ public class RemoteTerminologyServiceValidationSupportTest {
myValueSetProvider.myNextReturnValueSets = new ArrayList<>();
// when
- IBaseResource valueSet = mySvc.fetchValueSet(VALUE_SET_URL);
+ mySvc.fetchValueSet(VALUE_SET_URL);
// then
assertEquals(SummaryEnum.FALSE, myValueSetProvider.myLastSummaryParam);
}
@Test
- public void testValidateCodeWithAllParams_CodeSystem_Success() {
- createNextCodeSystemReturnParameters(true, DISPLAY, null);
- addAdditionalReturnParameters();
-
- IValidationSupport.CodeValidationResult outcome = mySvc.validateCode(null, null, CODE_SYSTEM, CODE, DISPLAY, null);
- assertEquals(CODE, outcome.getCode());
- assertEquals(DISPLAY, outcome.getDisplay());
- assertEquals(null, outcome.getSeverity());
- assertEquals(null, outcome.getMessage());
-
- validateExtraCodeSystemParams();
- }
-
- private void validateExtraCodeSystemParams() {
- assertEquals(CODE, myCodeSystemProvider.myLastCode.getCode());
- assertEquals(CODE_SYSTEM, myCodeSystemProvider.myLastUrl.getValueAsString());
- for (Parameters.ParametersParameterComponent param : myCodeSystemProvider.myNextReturnParams.getParameter()) {
- String paramName = param.getName();
- if (paramName.equals("result")) {
- assertEquals(true, ((BooleanType)param.getValue()).booleanValue());
- } else if (paramName.equals("display")) {
- assertEquals(DISPLAY, param.getValue().toString());
- } else if (paramName.equals("property")) {
- for (Parameters.ParametersParameterComponent propertyComponent : param.getPart()) {
- switch(propertyComponent.getName()) {
- case "name":
- assertEquals("birthDate", propertyComponent.getValue().toString());
- break;
- case "value":
- assertEquals("1930-01-01", propertyComponent.getValue().toString());
- break;
- }
- }
- } else if (paramName.equals("designation")) {
- for (Parameters.ParametersParameterComponent designationComponent : param.getPart()) {
- switch(designationComponent.getName()) {
- case "language":
- assertEquals(LANGUAGE, designationComponent.getValue().toString());
- break;
- case "use":
- Coding coding = (Coding)designationComponent.getValue();
- assertNotNull(coding, "Coding value returned via designation use should NOT be NULL!");
- assertEquals("code", coding.getCode());
- assertEquals("system", coding.getSystem());
- assertEquals("display", coding.getDisplay());
- break;
- case "value":
- assertEquals("some value", designationComponent.getValue().toString());
- break;
- }
- }
- }
- }
- }
-
- @Test
- public void testValidateCode_SystemCodeDisplayUrl_Error() {
+ public void testValidateCode_forSystemCodeWithError_returnsCorrectly() {
createNextValueSetReturnParameters(false, null, ERROR_MESSAGE);
IValidationSupport.CodeValidationResult outcome = mySvc.validateCode(null, null, CODE_SYSTEM, CODE, DISPLAY, VALUE_SET_URL);
- assertEquals(null, outcome.getCode());
- assertEquals(null, outcome.getDisplay());
+ assertNotNull(outcome);
+ assertNull(outcome.getCode());
+ assertNull(outcome.getDisplay());
assertEquals(IValidationSupport.IssueSeverity.ERROR, outcome.getSeverity());
assertEquals(ERROR_MESSAGE, outcome.getMessage());
@@ -258,18 +236,24 @@ public class RemoteTerminologyServiceValidationSupportTest {
assertEquals(DISPLAY, myValueSetProvider.myLastDisplay.getValue());
assertEquals(CODE_SYSTEM, myValueSetProvider.myLastSystem.getValue());
assertEquals(VALUE_SET_URL, myValueSetProvider.myLastUrl.getValue());
- assertEquals(null, myValueSetProvider.myLastValueSet);
+ assertNull(myValueSetProvider.myLastValueSet);
}
@Test
- public void testValidateCodeInCodeSystem_Good() {
- createNextCodeSystemReturnParameters(true, DISPLAY, null);
+ public void testValidateCode_forCodeSystem_returnsCorrectly() {
+ myCodeSystemProvider.myNextValidationResult = new IValidationSupport.CodeValidationResult();
+ myCodeSystemProvider.myNextValidationResult.setCodeSystemVersion(CODE_SYSTEM);
+ myCodeSystemProvider.myNextValidationResult.setCode(CODE);
+ myCodeSystemProvider.myNextValidationResult.setCodeSystemName(CODE_SYSTEM_NAME);
+ myCodeSystemProvider.myNextValidationResult.setDisplay(DISPLAY);
+ myCodeSystemProvider.myNextValidationResult.setMessage(SUCCESS_MESSAGE);
IValidationSupport.CodeValidationResult outcome = mySvc.validateCode(null, null, CODE_SYSTEM, CODE, DISPLAY, null);
+ assertNotNull(outcome);
assertEquals(CODE, outcome.getCode());
assertEquals(DISPLAY, outcome.getDisplay());
- assertEquals(null, outcome.getSeverity());
- assertEquals(null, outcome.getMessage());
+ assertNull(outcome.getSeverity());
+ assertNull(outcome.getMessage());
assertEquals(CODE, myCodeSystemProvider.myLastCode.getCode());
assertEquals(CODE_SYSTEM, myCodeSystemProvider.myLastUrl.getValueAsString());
@@ -284,16 +268,17 @@ public class RemoteTerminologyServiceValidationSupportTest {
valueSet.setUrl(VALUE_SET_URL);
IValidationSupport.CodeValidationResult outcome = mySvc.validateCodeInValueSet(null, new ConceptValidationOptions(), CODE_SYSTEM, CODE, DISPLAY, valueSet);
+ assertNotNull(outcome);
assertEquals(CODE, outcome.getCode());
assertEquals(DISPLAY, outcome.getDisplay());
- assertEquals(null, outcome.getSeverity());
- assertEquals(null, outcome.getMessage());
+ assertNull(outcome.getSeverity());
+ assertNull(outcome.getMessage());
assertEquals(CODE, myValueSetProvider.myLastCode.getCode());
assertEquals(DISPLAY, myValueSetProvider.myLastDisplay.getValue());
assertEquals(CODE_SYSTEM, myValueSetProvider.myLastSystem.getValue());
assertEquals(VALUE_SET_URL, myValueSetProvider.myLastUrl.getValueAsString());
- assertEquals(null, myValueSetProvider.myLastValueSet);
+ assertNull(myValueSetProvider.myLastValueSet);
}
/**
@@ -307,7 +292,7 @@ public class RemoteTerminologyServiceValidationSupportTest {
valueSet.setUrl(VALUE_SET_URL);
IValidationSupport.CodeValidationResult outcome = mySvc.validateCodeInValueSet(null, new ConceptValidationOptions().setInferSystem(true), null, CODE, DISPLAY, valueSet);
- assertEquals(null, outcome);
+ assertNull(outcome);
}
@Test
@@ -350,7 +335,8 @@ public class RemoteTerminologyServiceValidationSupportTest {
TranslateConceptResults results = mySvc.translateConcept(request);
- assertEquals(results.getResult(), true);
+ assertNotNull(results);
+ assertTrue(results.getResult());
assertEquals(results.getResults().size(), 2);
for(TranslateConceptResult result : results.getResults()) {
assertEquals(singleResult, result);
@@ -374,8 +360,8 @@ public class RemoteTerminologyServiceValidationSupportTest {
IValidationSupport.TranslateCodeRequest request = new IValidationSupport.TranslateCodeRequest(codings, null);
TranslateConceptResults results = mySvc.translateConcept(request);
-
- assertEquals(results.getResult(), false);
+ assertNotNull(results);
+ assertFalse(results.getResult());
assertEquals(results.getResults().size(), 0);
assertNull(myConceptMapProvider.myLastCodeableConcept);
@@ -393,7 +379,7 @@ public class RemoteTerminologyServiceValidationSupportTest {
myCodeSystemProvider.myNextReturnCodeSystems = new ArrayList<>();
// when
- IBaseResource codeSystem = mySvc.fetchCodeSystem("http://loinc.org");
+ mySvc.fetchCodeSystem("http://loinc.org");
// then
assertEquals(SummaryEnum.FALSE, myCodeSystemProvider.myLastSummaryParam);
@@ -535,7 +521,7 @@ public class RemoteTerminologyServiceValidationSupportTest {
/**
* Captures the system parameter of the request
*/
- private class TestClientInterceptor implements IClientInterceptor {
+ private static class TestClientInterceptor implements IClientInterceptor {
private String capturedSystemParameter;
@@ -552,12 +538,12 @@ public class RemoteTerminologyServiceValidationSupportTest {
capturedSystemParameter = systemValues.get(0);
}
} catch (IOException theE) {
- theE.printStackTrace();
+ // ignore
}
}
@Override
- public void interceptResponse(IHttpResponse theResponse) throws IOException { }
+ public void interceptResponse(IHttpResponse theResponse) { }
public String getCapturedSystemParameter() { return capturedSystemParameter; }
}
@@ -570,7 +556,7 @@ public class RemoteTerminologyServiceValidationSupportTest {
myValueSetProvider.myNextReturnValueSets = new ArrayList<>();
boolean outcome = mySvc.isValueSetSupported(null, "http://loinc.org/VS");
- assertEquals(false, outcome);
+ assertFalse(outcome);
assertEquals("http://loinc.org/VS", myValueSetProvider.myLastUrlParam.getValue());
}
@@ -580,7 +566,7 @@ public class RemoteTerminologyServiceValidationSupportTest {
myValueSetProvider.myNextReturnValueSets.add((ValueSet) new ValueSet().setId("ValueSet/123"));
boolean outcome = mySvc.isValueSetSupported(null, "http://loinc.org/VS");
- assertEquals(true, outcome);
+ assertTrue(outcome);
assertEquals("http://loinc.org/VS", myValueSetProvider.myLastUrlParam.getValue());
}
@@ -589,7 +575,7 @@ public class RemoteTerminologyServiceValidationSupportTest {
myCodeSystemProvider.myNextReturnCodeSystems = new ArrayList<>();
boolean outcome = mySvc.isCodeSystemSupported(null, "http://loinc.org");
- assertEquals(false, outcome);
+ assertFalse(outcome);
assertEquals("http://loinc.org", myCodeSystemProvider.myLastUrlParam.getValue());
}
@@ -599,31 +585,10 @@ public class RemoteTerminologyServiceValidationSupportTest {
myCodeSystemProvider.myNextReturnCodeSystems.add((CodeSystem) new CodeSystem().setId("CodeSystem/123"));
boolean outcome = mySvc.isCodeSystemSupported(null, "http://loinc.org");
- assertEquals(true, outcome);
+ assertTrue(outcome);
assertEquals("http://loinc.org", myCodeSystemProvider.myLastUrlParam.getValue());
}
- private void createNextCodeSystemReturnParameters(boolean theResult, String theDisplay, String theMessage) {
- myCodeSystemProvider.myNextReturnParams = new Parameters();
- myCodeSystemProvider.myNextReturnParams.addParameter("result", theResult);
- myCodeSystemProvider.myNextReturnParams.addParameter("display", theDisplay);
- if (theMessage != null) {
- myCodeSystemProvider.myNextReturnParams.addParameter("message", theMessage);
- }
- }
-
- private void createNextCodeSystemLookupReturnParameters(boolean theResult, String theVersion, String theVersionAsText,
- String theDisplay, String theMessage) {
- myCodeSystemProvider.myNextReturnParams = new Parameters();
- myCodeSystemProvider.myNextReturnParams.addParameter("result", theResult);
- myCodeSystemProvider.myNextReturnParams.addParameter("version", theVersion);
- myCodeSystemProvider.myNextReturnParams.addParameter("name", theVersionAsText);
- myCodeSystemProvider.myNextReturnParams.addParameter("display", theDisplay);
- if (theMessage != null) {
- myCodeSystemProvider.myNextReturnParams.addParameter("message", theMessage);
- }
- }
-
private void createNextValueSetReturnParameters(boolean theResult, String theDisplay, String theMessage) {
myValueSetProvider.myNextReturnParams = new Parameters();
myValueSetProvider.myNextReturnParams.addParameter("result", theResult);
@@ -633,53 +598,33 @@ public class RemoteTerminologyServiceValidationSupportTest {
}
}
- private void addAdditionalReturnParameters() {
- // property
- Parameters.ParametersParameterComponent param = myCodeSystemProvider.myNextReturnParams.addParameter().setName("property");
- param.addPart().setName("name").setValue(new StringType("birthDate"));
- param.addPart().setName("value").setValue(new StringType("1930-01-01"));
- // designation
- param = myCodeSystemProvider.myNextReturnParams.addParameter().setName("designation");
- param.addPart().setName("language").setValue(new CodeType("en"));
- Parameters.ParametersParameterComponent codingParam = param.addPart().setName("use");
- Coding coding = new Coding();
- coding.setCode("code");
- coding.setSystem("system");
- coding.setDisplay("display");
- codingParam.setValue(coding);
- param.addPart().setName("value").setValue(new StringType("some value"));
- }
-
private static class MyCodeSystemProvider implements IResourceProvider {
private SummaryEnum myLastSummaryParam;
private UriParam myLastUrlParam;
private List myNextReturnCodeSystems;
- private int myInvocationCount;
private UriType myLastUrl;
private CodeType myLastCode;
- private Coding myLastCoding;
- private StringType myLastVersion;
private Parameters myNextReturnParams;
private IValidationSupport.LookupCodeResult myNextLookupCodeResult;
+ private IValidationSupport.CodeValidationResult myNextValidationResult;
@Operation(name = "validate-code", idempotent = true, returnParameters = {
@OperationParam(name = "result", type = BooleanType.class, min = 1),
@OperationParam(name = "message", type = StringType.class),
@OperationParam(name = "display", type = StringType.class)
})
- public Parameters validateCode(
+ public IBaseParameters validateCode(
HttpServletRequest theServletRequest,
@IdParam(optional = true) IdType theId,
@OperationParam(name = "url", min = 0, max = 1) UriType theCodeSystemUrl,
@OperationParam(name = "code", min = 0, max = 1) CodeType theCode,
@OperationParam(name = "display", min = 0, max = 1) StringType theDisplay
) {
- myInvocationCount++;
myLastUrl = theCodeSystemUrl;
myLastCode = theCode;
+ myNextReturnParams = (Parameters)myNextValidationResult.toParameters(ourCtx);
return myNextReturnParams;
-
}
@Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters= {
@@ -687,22 +632,21 @@ public class RemoteTerminologyServiceValidationSupportTest {
@OperationParam(name="version", type=StringType.class, min=0),
@OperationParam(name="display", type=StringType.class, min=1),
@OperationParam(name="abstract", type=BooleanType.class, min=1),
+ @OperationParam(name="property", min = 0, max = OperationParam.MAX_UNLIMITED)
})
- public Parameters lookup(
+ public IBaseParameters lookup(
HttpServletRequest theServletRequest,
@OperationParam(name="code", min=0, max=1) CodeType theCode,
@OperationParam(name="system", min=0, max=1) UriType theSystem,
@OperationParam(name="coding", min=0, max=1) Coding theCoding,
@OperationParam(name="version", min=0, max=1) StringType theVersion,
@OperationParam(name="displayLanguage", min=0, max=1) CodeType theDisplayLanguage,
- @OperationParam(name="property", min = 0, max = OperationParam.MAX_UNLIMITED) List theProperties,
+ @OperationParam(name="property", min = 0, max = OperationParam.MAX_UNLIMITED) List thePropertyNames,
RequestDetails theRequestDetails
) {
- myInvocationCount++;
myLastCode = theCode;
myLastUrl = theSystem;
- myLastCoding = theCoding;
- myLastVersion = theVersion;
+ myNextReturnParams = (Parameters)myNextLookupCodeResult.toParameters(theRequestDetails.getFhirContext(), thePropertyNames);
return myNextReturnParams;
}
@@ -779,8 +723,6 @@ public class RemoteTerminologyServiceValidationSupportTest {
private UriType myLastTargetValueSet;
private UriType myLastTargetCodeSystem;
private BooleanType myLastReverse;
-
- private int myInvocationCount;
private Parameters myNextReturnParams;
@Operation(name = JpaConstants.OPERATION_TRANSLATE, idempotent = true, returnParameters = {
@@ -799,7 +741,6 @@ public class RemoteTerminologyServiceValidationSupportTest {
@OperationParam(name = "reverse", min = 0, max = 1) BooleanType theReverse,
RequestDetails theRequestDetails
) {
- myInvocationCount++;
myLastConceptMapUrl = theConceptMapUrl;
myLastConceptMapVersion = theConceptMapVersion;
myLastCodeableConcept = theSourceCodeableConcept;
diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/RemoteTerminologyServiceValidationSupportDstu3Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/RemoteTerminologyServiceValidationSupportDstu3Test.java
index 9d435406dd8..017eea69f19 100644
--- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/RemoteTerminologyServiceValidationSupportDstu3Test.java
+++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/RemoteTerminologyServiceValidationSupportDstu3Test.java
@@ -2,6 +2,7 @@ package org.hl7.fhir.dstu3.hapi.validation;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.IValidationSupport;
+import ca.uhn.fhir.context.support.LookupCodeRequest;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
@@ -9,13 +10,11 @@ import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
-import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
import org.hl7.fhir.dstu3.model.BooleanType;
import org.hl7.fhir.dstu3.model.CodeSystem;
import org.hl7.fhir.dstu3.model.CodeType;
import org.hl7.fhir.dstu3.model.Coding;
-import org.hl7.fhir.dstu3.model.DateType;
import org.hl7.fhir.dstu3.model.Parameters;
import org.hl7.fhir.dstu3.model.StringType;
import org.hl7.fhir.dstu3.model.UriType;
@@ -25,7 +24,9 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import javax.servlet.http.HttpServletRequest;
+import java.util.Iterator;
import java.util.List;
+import java.util.Set;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -34,14 +35,13 @@ public class RemoteTerminologyServiceValidationSupportDstu3Test {
private static final String DISPLAY = "DISPLAY";
private static final String LANGUAGE = "en";
private static final String CODE_SYSTEM = "CODE_SYS";
- private static final String CODE_SYSTEM_VERSION = "2.1";
- private static final String CODE_SYSTEM_VERSION_AS_TEXT = "v2.1.12";
+ private static final String CODE_SYSTEM_NAME = "Code System";
private static final String CODE = "CODE";
- private static FhirContext ourCtx = FhirContext.forDstu3();
+ private static final FhirContext ourCtx = FhirContext.forDstu3();
@RegisterExtension
- public RestfulServerExtension myRestfulServerExtension = new RestfulServerExtension(ourCtx);
+ public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
private RemoteTerminologyServiceValidationSupport mySvc;
private MyCodeSystemProvider myCodeSystemProvider;
@@ -49,143 +49,98 @@ public class RemoteTerminologyServiceValidationSupportDstu3Test {
@BeforeEach
public void before() {
myCodeSystemProvider = new MyCodeSystemProvider();
- myRestfulServerExtension.getRestfulServer().registerProvider(myCodeSystemProvider);
- String baseUrl = "http://localhost:" + myRestfulServerExtension.getPort();
+ ourRestfulServerExtension.getRestfulServer().registerProvider(myCodeSystemProvider);
+ String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx);
mySvc.setBaseUrl(baseUrl);
mySvc.addClientInterceptor(new LoggingInterceptor(true));
}
- @Test
- public void testLookupOperation_CodeSystem_Success() {
- createNextCodeSystemLookupReturnParameters(true, CODE_SYSTEM_VERSION, CODE_SYSTEM_VERSION_AS_TEXT, DISPLAY);
-
- IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(null, CODE_SYSTEM, CODE);
- assertNotNull(outcome, "Call to lookupCode() should return a non-NULL result!");
- assertEquals(DISPLAY, outcome.getCodeDisplay());
- assertEquals(CODE_SYSTEM_VERSION, outcome.getCodeSystemVersion());
-
- assertEquals(CODE, myCodeSystemProvider.myLastCode.asStringValue());
- assertEquals(CODE_SYSTEM, myCodeSystemProvider.myLastUrl.getValueAsString());
- for (Parameters.ParametersParameterComponent param : myCodeSystemProvider.myNextReturnParams.getParameter()) {
- String paramName = param.getName();
- if (paramName.equals("result")) {
- assertEquals(true, ((BooleanType)param.getValue()).booleanValue());
- } else if (paramName.equals("version")) {
- assertEquals(CODE_SYSTEM_VERSION, param.getValue().toString());
- } else if (paramName.equals("display")) {
- assertEquals(DISPLAY, param.getValue().toString());
- } else if (paramName.equals("name")) {
- assertEquals(CODE_SYSTEM_VERSION_AS_TEXT, param.getValue().toString());
- }
- }
- }
-
@Test
public void testLookupOperationWithAllParams_CodeSystem_Success() {
- createNextCodeSystemLookupReturnParameters(true, CODE_SYSTEM_VERSION, CODE_SYSTEM_VERSION_AS_TEXT, DISPLAY, LANGUAGE);
- addAdditionalCodeSystemLookupReturnParameters();
+ myCodeSystemProvider.myNextLookupCodeResult = new IValidationSupport.LookupCodeResult();
+ myCodeSystemProvider.myNextLookupCodeResult.setFound(true);
+ myCodeSystemProvider.myNextLookupCodeResult.setCodeSystemVersion(CODE_SYSTEM);
+ myCodeSystemProvider.myNextLookupCodeResult.setSearchedForCode(CODE);
+ myCodeSystemProvider.myNextLookupCodeResult.setCodeSystemDisplayName(CODE_SYSTEM_NAME);
+ myCodeSystemProvider.myNextLookupCodeResult.setCodeDisplay(DISPLAY);
- IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(null, CODE_SYSTEM, CODE, LANGUAGE);
+ // property
+ String propertyName = "birthDate";
+ String propertyValue = "1930-01-01";
+ IValidationSupport.BaseConceptProperty property = new IValidationSupport.StringConceptProperty(propertyName, propertyValue);
+ myCodeSystemProvider.myNextLookupCodeResult.getProperties().add(property);
+
+ // designation
+ IValidationSupport.ConceptDesignation designation = new IValidationSupport.ConceptDesignation();
+ designation.setLanguage("en");
+ designation.setUseCode("code");
+ designation.setUseSystem("system");
+ designation.setUseDisplay("display");
+ designation.setValue("some value");
+ myCodeSystemProvider.myNextLookupCodeResult.getDesignations().add(designation);
+
+ IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(null, new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, Set.of("birthDate")));
assertNotNull(outcome, "Call to lookupCode() should return a non-NULL result!");
assertEquals(DISPLAY, outcome.getCodeDisplay());
- assertEquals(CODE_SYSTEM_VERSION, outcome.getCodeSystemVersion());
+ assertEquals(CODE_SYSTEM, outcome.getCodeSystemVersion());
assertEquals(CODE, myCodeSystemProvider.myLastCode.asStringValue());
assertEquals(CODE_SYSTEM, myCodeSystemProvider.myLastUrl.getValueAsString());
- for (Parameters.ParametersParameterComponent param : myCodeSystemProvider.myNextReturnParams.getParameter()) {
- String paramName = param.getName();
- if (paramName.equals("result")) {
- assertEquals(true, ((BooleanType)param.getValue()).booleanValue());
- } else if (paramName.equals("version")) {
- assertEquals(CODE_SYSTEM_VERSION, param.getValue().toString());
- } else if (paramName.equals("display")) {
- assertEquals(DISPLAY, param.getValue().toString());
- } else if (paramName.equals("name")) {
- assertEquals(CODE_SYSTEM_VERSION_AS_TEXT, param.getValue().toString());
- } else if (paramName.equals("language")) {
- assertEquals(LANGUAGE, param.getValue().toString());
- } else if (paramName.equals("property")) {
- for (org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent propertyComponent : param.getPart()) {
- switch(propertyComponent.getName()) {
- case "name":
- assertEquals("birthDate", propertyComponent.getValue().toString());
- break;
- case "value":
- assertEquals("1930-01-01", ((DateType)propertyComponent.getValue()).asStringValue());
- break;
- }
- }
- } else if (paramName.equals("designation")) {
- for (org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent designationComponent : param.getPart()) {
- switch(designationComponent.getName()) {
- case "language":
- assertEquals(LANGUAGE, designationComponent.getValue().toString());
- break;
- case "use":
- Coding coding = (Coding)designationComponent.getValue();
- assertNotNull(coding, "Coding value returned via designation use should NOT be NULL!");
- assertEquals("code", coding.getCode());
- assertEquals("system", coding.getSystem());
- assertEquals("display", coding.getDisplay());
- break;
- case "value":
- assertEquals("some value", designationComponent.getValue().toString());
- break;
- }
- }
+
+ Parameters.ParametersParameterComponent propertyComponent = null;
+ Parameters.ParametersParameterComponent designationComponent = null;
+ for (Parameters.ParametersParameterComponent parameterComponent : myCodeSystemProvider.myNextReturnParams.getParameter()) {
+ if ("property".equals(parameterComponent.getName())) {
+ propertyComponent = parameterComponent;
+ }
+ if ("designation".equals(parameterComponent.getName())) {
+ designationComponent = parameterComponent;
}
}
- }
- private void createNextCodeSystemLookupReturnParameters(boolean theResult, String theVersion, String theVersionAsText,
- String theDisplay) {
- createNextCodeSystemLookupReturnParameters(theResult, theVersion, theVersionAsText, theDisplay, null);
- }
+ assertNotNull(propertyComponent);
- private void createNextCodeSystemLookupReturnParameters(boolean theResult, String theVersion, String theVersionAsText,
- String theDisplay, String theLanguage) {
- myCodeSystemProvider.myNextReturnParams = new Parameters();
- myCodeSystemProvider.myNextReturnParams.addParameter().setName("result").setValue(new BooleanType(theResult));
- myCodeSystemProvider.myNextReturnParams.addParameter().setName("version").setValue(new StringType(theVersion));
- myCodeSystemProvider.myNextReturnParams.addParameter().setName("name").setValue(new StringType(theVersionAsText));
- myCodeSystemProvider.myNextReturnParams.addParameter().setName("display").setValue(new StringType(theDisplay));
- if (!StringUtils.isBlank(theLanguage)) {
- myCodeSystemProvider.myNextReturnParams.addParameter().setName("language").setValue(new StringType(theLanguage));
- }
- }
+ Iterator propertyComponentIterator = propertyComponent.getPart().iterator();
+ propertyComponent = propertyComponentIterator.next();
+ assertEquals("code", propertyComponent.getName());
+ assertEquals(propertyName, ((StringType)propertyComponent.getValue()).getValue());
- private void addAdditionalCodeSystemLookupReturnParameters() {
- // property
- Parameters.ParametersParameterComponent param = myCodeSystemProvider.myNextReturnParams.addParameter().setName("property");
- param.addPart().setName("name").setValue(new StringType("birthDate"));
- param.addPart().setName("value").setValue(new DateType("1930-01-01"));
- // designation
- param = myCodeSystemProvider.myNextReturnParams.addParameter().setName("designation");
- param.addPart().setName("language").setValue(new CodeType("en"));
- Parameters.ParametersParameterComponent codingParam = param.addPart().setName("use");
- Coding coding = new Coding();
- coding.setCode("code");
- coding.setSystem("system");
- coding.setDisplay("display");
- codingParam.setValue(coding);
- param.addPart().setName("value").setValue(new StringType("some value"));
+ propertyComponent = propertyComponentIterator.next();
+ assertEquals("value", propertyComponent.getName());
+ assertEquals(propertyValue, ((StringType)propertyComponent.getValue()).getValue());
+
+ assertNotNull(designationComponent);
+ Iterator partParameter = designationComponent.getPart().iterator();
+ designationComponent = partParameter.next();
+ assertEquals("language", designationComponent.getName());
+ assertEquals(LANGUAGE, designationComponent.getValue().toString());
+
+ designationComponent = partParameter.next();
+ assertEquals("use", designationComponent.getName());
+ Coding coding = (Coding)designationComponent.getValue();
+ assertNotNull(coding, "Coding value returned via designation use should NOT be NULL!");
+ assertEquals("code", coding.getCode());
+ assertEquals("system", coding.getSystem());
+ assertEquals("display", coding.getDisplay());
+
+ designationComponent = partParameter.next();
+ assertEquals("value", designationComponent.getName());
+ assertEquals("some value", designationComponent.getValue().toString());
}
private static class MyCodeSystemProvider implements IResourceProvider {
- private int myInvocationCount;
private UriType myLastUrl;
private CodeType myLastCode;
- private Coding myLastCoding;
- private StringType myLastVersion;
- private CodeType myLastDisplayLanguage;
private Parameters myNextReturnParams;
+ private IValidationSupport.LookupCodeResult myNextLookupCodeResult;
@Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters= {
@OperationParam(name="name", type=StringType.class, min=1),
@OperationParam(name="version", type=StringType.class, min=0),
@OperationParam(name="display", type=StringType.class, min=1),
@OperationParam(name="abstract", type=BooleanType.class, min=1),
+ @OperationParam(name="property", min = 0, max = OperationParam.MAX_UNLIMITED)
})
public Parameters lookup(
HttpServletRequest theServletRequest,
@@ -194,15 +149,13 @@ public class RemoteTerminologyServiceValidationSupportDstu3Test {
@OperationParam(name="coding", min=0, max=1) Coding theCoding,
@OperationParam(name="version", min=0, max=1) StringType theVersion,
@OperationParam(name="displayLanguage", min=0, max=1) CodeType theDisplayLanguage,
- @OperationParam(name="property", min = 0, max = OperationParam.MAX_UNLIMITED) List theProperties,
+ @OperationParam(name="property", min = 0, max = OperationParam.MAX_UNLIMITED) List thePropertyNames,
RequestDetails theRequestDetails
) {
- myInvocationCount++;
myLastCode = theCode;
myLastUrl = theSystem;
- myLastCoding = theCoding;
- myLastVersion = theVersion;
- myLastDisplayLanguage = theDisplayLanguage;
+
+ myNextReturnParams = (Parameters)myNextLookupCodeResult.toParameters(theRequestDetails.getFhirContext(), thePropertyNames);
return myNextReturnParams;
}
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 4484737c1c8..7f29442a01a 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
@@ -4,6 +4,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.ConceptValidationOptions;
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
import ca.uhn.fhir.context.support.IValidationSupport;
+import ca.uhn.fhir.context.support.LookupCodeRequest;
import ca.uhn.fhir.context.support.ValidationSupportContext;
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
import ca.uhn.fhir.rest.api.Constants;
@@ -264,9 +265,10 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
return retVal;
}
});
- when(myMockSupport.lookupCode(any(), any(), any(), any())).thenAnswer(t -> {
- String system = t.getArgument(1, String.class);
- String code = t.getArgument(2, String.class);
+ when(myMockSupport.lookupCode(any(), any())).thenAnswer(t -> {
+ LookupCodeRequest request = t.getArgument(1, LookupCodeRequest.class);
+ String system = request.getSystem();
+ String code = request.getCode();
if (myValidConcepts.contains(system + "___" + code)) {
return new IValidationSupport.LookupCodeResult().setFound(true);
} else {
diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4b/validation/FhirInstanceValidatorR4BTest.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4b/validation/FhirInstanceValidatorR4BTest.java
index 97f735b9ebb..f700c6e6744 100644
--- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4b/validation/FhirInstanceValidatorR4BTest.java
+++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4b/validation/FhirInstanceValidatorR4BTest.java
@@ -4,6 +4,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.ConceptValidationOptions;
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
import ca.uhn.fhir.context.support.IValidationSupport;
+import ca.uhn.fhir.context.support.LookupCodeRequest;
import ca.uhn.fhir.context.support.ValidationSupportContext;
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
import ca.uhn.fhir.rest.api.Constants;
@@ -31,9 +32,33 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4b.conformance.ProfileUtilities;
import org.hl7.fhir.r4b.context.IWorkerContext;
import org.hl7.fhir.r4b.hapi.ctx.HapiWorkerContext;
-import org.hl7.fhir.r4b.model.*;
+import org.hl7.fhir.r4b.model.AllergyIntolerance;
+import org.hl7.fhir.r4b.model.Base;
+import org.hl7.fhir.r4b.model.Base64BinaryType;
+import org.hl7.fhir.r4b.model.BooleanType;
+import org.hl7.fhir.r4b.model.Bundle;
import org.hl7.fhir.r4b.model.Bundle.BundleEntryComponent;
+import org.hl7.fhir.r4b.model.CodeSystem;
+import org.hl7.fhir.r4b.model.CodeType;
+import org.hl7.fhir.r4b.model.Consent;
+import org.hl7.fhir.r4b.model.ContactPoint;
+import org.hl7.fhir.r4b.model.DateTimeType;
+import org.hl7.fhir.r4b.model.Enumerations;
+import org.hl7.fhir.r4b.model.Extension;
+import org.hl7.fhir.r4b.model.Media;
+import org.hl7.fhir.r4b.model.Narrative;
+import org.hl7.fhir.r4b.model.Observation;
+import org.hl7.fhir.r4b.model.OperationOutcome;
+import org.hl7.fhir.r4b.model.Patient;
+import org.hl7.fhir.r4b.model.Period;
+import org.hl7.fhir.r4b.model.Practitioner;
+import org.hl7.fhir.r4b.model.Procedure;
+import org.hl7.fhir.r4b.model.QuestionnaireResponse;
+import org.hl7.fhir.r4b.model.Reference;
+import org.hl7.fhir.r4b.model.StringType;
+import org.hl7.fhir.r4b.model.StructureDefinition;
import org.hl7.fhir.r4b.model.StructureDefinition.StructureDefinitionKind;
+import org.hl7.fhir.r4b.model.ValueSet;
import org.hl7.fhir.r4b.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.r4b.terminologies.ValueSetExpander;
import org.hl7.fhir.r4b.utils.FHIRPathEngine;
@@ -72,7 +97,6 @@ import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.lessThan;
import static org.hamcrest.Matchers.not;
-import static org.hamcrest.Matchers.startsWith;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -230,9 +254,10 @@ public class FhirInstanceValidatorR4BTest extends BaseTest {
return retVal;
}
});
- when(myMockSupport.lookupCode(any(), any(), any(), any())).thenAnswer(t -> {
- String system = t.getArgument(1, String.class);
- String code = t.getArgument(2, String.class);
+ when(myMockSupport.lookupCode(any(), any())).thenAnswer(t -> {
+ LookupCodeRequest request = t.getArgument(1, LookupCodeRequest.class);
+ String system = request.getSystem();
+ String code = request.getCode();
if (myValidConcepts.contains(system + "___" + code)) {
return new IValidationSupport.LookupCodeResult().setFound(true);
} else {
diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/RemoteTerminologyServiceValidationSupportR5Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/RemoteTerminologyServiceValidationSupportR5Test.java
index 2d5c71bce56..0e9485bae4d 100644
--- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/RemoteTerminologyServiceValidationSupportR5Test.java
+++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/RemoteTerminologyServiceValidationSupportR5Test.java
@@ -1,8 +1,8 @@
package org.hl7.fhir.r5.validation;
import ca.uhn.fhir.context.FhirContext;
-import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.ValidationSupportContext;
+import ca.uhn.fhir.context.support.LookupCodeRequest;
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
import org.junit.jupiter.api.Assertions;
@@ -12,9 +12,9 @@ import org.junit.jupiter.api.extension.RegisterExtension;
public class RemoteTerminologyServiceValidationSupportR5Test {
private static final String ANY_NONBLANK_VALUE = "anything";
- private static FhirContext ourCtx = FhirContext.forR5Cached();
+ private static final FhirContext ourCtx = FhirContext.forR5Cached();
@RegisterExtension
- public RestfulServerExtension myRestfulServerExtension = new RestfulServerExtension(ourCtx);
+ public static RestfulServerExtension myRestfulServerExtension = new RestfulServerExtension(ourCtx);
private RemoteTerminologyServiceValidationSupport mySvc;
@@ -27,9 +27,9 @@ public class RemoteTerminologyServiceValidationSupportR5Test {
@Test
public void testLookupCode_R5_ThrowsException() {
- Assertions.assertThrows(UnsupportedOperationException.class, () -> {
- IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(
- new ValidationSupportContext(ourCtx.getValidationSupport()), ANY_NONBLANK_VALUE, ANY_NONBLANK_VALUE);
- });
+ Assertions.assertThrows(UnsupportedOperationException.class,
+ () -> mySvc.lookupCode(
+ new ValidationSupportContext(ourCtx.getValidationSupport()),
+ new LookupCodeRequest(ANY_NONBLANK_VALUE, ANY_NONBLANK_VALUE)));
}
}
From 6e683405a1840cbfc4d0006f4ab81719e3f83c88 Mon Sep 17 00:00:00 2001
From: TipzCM
Date: Tue, 28 Nov 2023 16:28:17 -0500
Subject: [PATCH 03/14] 5237 fixing empty last page if even results (#5506)
paging will not return empty pages if no results left
---
.../ca/uhn/fhir/cli/BulkImportCommandIT.java | 11 +-
.../5192-include-in-search-paging-fix.yaml | 7 ++
.../search/PersistedJpaBundleProvider.java | 20 +++-
...istedJpaSearchFirstPageBundleProvider.java | 16 ++-
.../jpa/search/SynchronousSearchSvcImpl.java | 25 +++-
.../search/SynchronousSearchSvcImplTest.java | 16 ++-
.../jpa/dao/r4/ConsentEventsDaoR4Test.java | 38 ++++--
.../r4/FhirResourceDaoR4QueryCountTest.java | 23 +---
.../ForceOffsetSearchModeInterceptorTest.java | 43 +------
...sourceProviderCustomSearchParamR4Test.java | 7 +-
.../r4/ResourceProviderR4EverythingTest.java | 1 -
.../provider/r4/ResourceProviderR4Test.java | 100 +++++++++++++++-
.../ca/uhn/fhir/jpa/test/BaseJpaR4Test.java | 5 +-
.../jpa/test/config/TestHapiJpaConfig.java | 21 ++++
.../fhir/jpa/test/config/TestR4Config.java | 3 +-
.../fhir/rest/api/server/IBundleProvider.java | 1 +
.../rest/server/SimpleBundleProvider.java | 14 +++
.../server/method/ResponseBundleBuilder.java | 8 +-
.../fhir/rest/server/method/ResponsePage.java | 111 +++++++++++++++---
.../api/server/method/ResponsePageTest.java | 59 +++++++---
.../jobs/imprt/BulkDataImportProvider.java | 6 +-
21 files changed, 403 insertions(+), 132 deletions(-)
create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5192-include-in-search-paging-fix.yaml
create mode 100644 hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestHapiJpaConfig.java
diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/BulkImportCommandIT.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/BulkImportCommandIT.java
index 02d3e24325b..c7f4369faef 100644
--- a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/BulkImportCommandIT.java
+++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/BulkImportCommandIT.java
@@ -8,6 +8,8 @@ import ca.uhn.fhir.batch2.model.JobInstanceStartRequest;
import ca.uhn.fhir.batch2.model.StatusEnum;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.batch.models.Batch2JobStartResponse;
+import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc;
+import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.interceptor.LoggingInterceptor;
import ca.uhn.fhir.system.HapiSystemProperties;
@@ -66,6 +68,8 @@ public class BulkImportCommandIT {
private IJobCoordinator myJobCoordinator;
private final BulkDataImportProvider myProvider = new BulkDataImportProvider();
private final FhirContext myCtx = FhirContext.forR4Cached();
+ @Mock
+ private IRequestPartitionHelperSvc myRequestPartitionHelperSvc;
@RegisterExtension
public RestfulServerExtension myRestfulServerExtension = new RestfulServerExtension(myCtx, myProvider)
.registerInterceptor(new LoggingInterceptor());
@@ -77,6 +81,7 @@ public class BulkImportCommandIT {
public void beforeEach() throws IOException {
myProvider.setFhirContext(myCtx);
myProvider.setJobCoordinator(myJobCoordinator);
+ myProvider.setRequestPartitionHelperService(myRequestPartitionHelperSvc);
myTempDir = Files.createTempDirectory("hapifhir");
ourLog.info("Created temp directory: {}", myTempDir);
}
@@ -123,7 +128,7 @@ public class BulkImportCommandIT {
await().until(() -> myRestfulServerExtension.getRequestContentTypes().size(), equalTo(2));
ourLog.info("Initiation requests complete");
- verify(myJobCoordinator, timeout(10000).times(1)).startInstance(myStartCaptor.capture());
+ verify(myJobCoordinator, timeout(10000).times(1)).startInstance(any(RequestDetails.class), myStartCaptor.capture());
JobInstanceStartRequest startRequest = myStartCaptor.getValue();
BulkImportJobParameters jobParameters = startRequest.getParameters(BulkImportJobParameters.class);
@@ -165,7 +170,7 @@ public class BulkImportCommandIT {
await().until(() -> myRestfulServerExtension.getRequestContentTypes().size(), equalTo(2));
ourLog.info("Initiation requests complete");
- verify(myJobCoordinator, timeout(10000).times(1)).startInstance(myStartCaptor.capture());
+ verify(myJobCoordinator, timeout(10000).times(1)).startInstance(any(RequestDetails.class), myStartCaptor.capture());
JobInstanceStartRequest startRequest = myStartCaptor.getValue();
BulkImportJobParameters jobParameters = startRequest.getParameters(BulkImportJobParameters.class);
@@ -206,7 +211,7 @@ public class BulkImportCommandIT {
await().until(() -> myRestfulServerExtension.getRequestContentTypes().size(), equalTo(2));
ourLog.info("Initiation requests complete");
- verify(myJobCoordinator, timeout(10000).times(1)).startInstance(myStartCaptor.capture());
+ verify(myJobCoordinator, timeout(10000).times(1)).startInstance(any(RequestDetails.class), myStartCaptor.capture());
try{
JobInstanceStartRequest startRequest = myStartCaptor.getValue();
diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5192-include-in-search-paging-fix.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5192-include-in-search-paging-fix.yaml
new file mode 100644
index 00000000000..a63588d0a9f
--- /dev/null
+++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5192-include-in-search-paging-fix.yaml
@@ -0,0 +1,7 @@
+---
+type: fix
+issue: 5192
+title: "Fixed a bug where search Bundles with `include` entries from an _include query parameter might
+ trigger a 'next' link to blank pages when
+ no more results `match` results are available.
+"
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java
index 5d2dae49723..51a994e7afa 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java
@@ -125,7 +125,7 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
* of this class, since it's a prototype
*/
private Search mySearchEntity;
- private String myUuid;
+ private final String myUuid;
private SearchCacheStatusEnum myCacheStatus;
private RequestPartitionId myRequestPartitionId;
@@ -259,13 +259,21 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
final ISearchBuilder sb = mySearchBuilderFactory.newSearchBuilder(dao, resourceName, resourceType);
RequestPartitionId requestPartitionId = getRequestPartitionId();
- final List pidsSubList =
- mySearchCoordinatorSvc.getResources(myUuid, theFromIndex, theToIndex, myRequest, requestPartitionId);
+ // we request 1 more resource than we need
+ // this is so we can be sure of when we hit the last page
+ // (when doing offset searches)
+ final List pidsSubList = mySearchCoordinatorSvc.getResources(
+ myUuid, theFromIndex, theToIndex + 1, myRequest, requestPartitionId);
+ // max list size should be either the entire list, or from - to length
+ int maxSize = Math.min(theToIndex - theFromIndex, pidsSubList.size());
+ theResponsePageBuilder.setTotalRequestedResourcesFetched(pidsSubList.size());
+
+ List firstBatchOfPids = pidsSubList.subList(0, maxSize);
List resources = myTxService
.withRequest(myRequest)
.withRequestPartitionId(requestPartitionId)
.execute(() -> {
- return toResourceList(sb, pidsSubList, theResponsePageBuilder);
+ return toResourceList(sb, firstBatchOfPids, theResponsePageBuilder);
});
return resources;
@@ -541,8 +549,8 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
// this can (potentially) change the results being returned.
int precount = resources.size();
resources = ServerInterceptorUtil.fireStoragePreshowResource(resources, myRequest, myInterceptorBroadcaster);
- // we only care about omitted results from *this* page
- theResponsePageBuilder.setToOmittedResourceCount(precount - resources.size());
+ // we only care about omitted results from this page
+ theResponsePageBuilder.setOmittedResourceCount(precount - resources.size());
theResponsePageBuilder.setResources(resources);
theResponsePageBuilder.setIncludedResourceCount(includedPidList.size());
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaSearchFirstPageBundleProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaSearchFirstPageBundleProvider.java
index b1ac13fcbe2..f2357932a0e 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaSearchFirstPageBundleProvider.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaSearchFirstPageBundleProvider.java
@@ -73,16 +73,23 @@ public class PersistedJpaSearchFirstPageBundleProvider extends PersistedJpaBundl
mySearchTask.awaitInitialSync();
+ // request 1 more than we need to, in order to know if there are extra values
ourLog.trace("Fetching search resource PIDs from task: {}", mySearchTask.getClass());
- final List pids = mySearchTask.getResourcePids(theFromIndex, theToIndex);
+ final List pids = mySearchTask.getResourcePids(theFromIndex, theToIndex + 1);
ourLog.trace("Done fetching search resource PIDs");
+ int countOfPids = pids.size();
+ ;
+ int maxSize = Math.min(theToIndex - theFromIndex, countOfPids);
+ thePageBuilder.setTotalRequestedResourcesFetched(countOfPids);
+
RequestPartitionId requestPartitionId = getRequestPartitionId();
+ List firstBatch = pids.subList(0, maxSize);
List retVal = myTxService
.withRequest(myRequest)
.withRequestPartitionId(requestPartitionId)
- .execute(() -> toResourceList(mySearchBuilder, pids, thePageBuilder));
+ .execute(() -> toResourceList(mySearchBuilder, firstBatch, thePageBuilder));
long totalCountWanted = theToIndex - theFromIndex;
long totalCountMatch = (int) retVal.stream().filter(t -> !isInclude(t)).count();
@@ -103,12 +110,15 @@ public class PersistedJpaSearchFirstPageBundleProvider extends PersistedJpaBundl
long remainingWanted = totalCountWanted - totalCountMatch;
long fromIndex = theToIndex - remainingWanted;
- List remaining = super.getResources((int) fromIndex, theToIndex, thePageBuilder);
+ ResponsePage.ResponsePageBuilder pageBuilder = new ResponsePage.ResponsePageBuilder();
+ pageBuilder.setBundleProvider(this);
+ List remaining = super.getResources((int) fromIndex, theToIndex, pageBuilder);
remaining.forEach(t -> {
if (!existingIds.contains(t.getIdElement().getValue())) {
retVal.add(t);
}
});
+ thePageBuilder.combineWith(pageBuilder);
}
}
ourLog.trace("Loaded resources to return");
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SynchronousSearchSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SynchronousSearchSvcImpl.java
index 3627b72a5d4..89f77e51b14 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SynchronousSearchSvcImpl.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SynchronousSearchSvcImpl.java
@@ -115,7 +115,7 @@ public class SynchronousSearchSvcImpl implements ISynchronousSearchSvc {
.execute(() -> {
// Load the results synchronously
- final List pids = new ArrayList<>();
+ List pids = new ArrayList<>();
Long count = 0L;
if (wantCount) {
@@ -145,8 +145,17 @@ public class SynchronousSearchSvcImpl implements ISynchronousSearchSvc {
return bundleProvider;
}
+ // if we have a count, we'll want to request
+ // additional resources
+ SearchParameterMap clonedParams = theParams.clone();
+ Integer requestedCount = clonedParams.getCount();
+ boolean hasACount = requestedCount != null;
+ if (hasACount) {
+ clonedParams.setCount(requestedCount.intValue() + 1);
+ }
+
try (IResultIterator resultIter = theSb.createQuery(
- theParams, searchRuntimeDetails, theRequestDetails, theRequestPartitionId)) {
+ clonedParams, searchRuntimeDetails, theRequestDetails, theRequestPartitionId)) {
while (resultIter.hasNext()) {
pids.add(resultIter.next());
if (theLoadSynchronousUpTo != null && pids.size() >= theLoadSynchronousUpTo) {
@@ -162,6 +171,15 @@ public class SynchronousSearchSvcImpl implements ISynchronousSearchSvc {
throw new InternalErrorException(Msg.code(1164) + e);
}
+ // truncate the list we retrieved - if needed
+ int receivedResourceCount = -1;
+ if (hasACount) {
+ // we want the accurate received resource count
+ receivedResourceCount = pids.size();
+ int resourcesToReturn = Math.min(theParams.getCount(), pids.size());
+ pids = pids.subList(0, resourcesToReturn);
+ }
+
JpaPreResourceAccessDetails accessDetails = new JpaPreResourceAccessDetails(pids, () -> theSb);
HookParams params = new HookParams()
.add(IPreResourceAccessDetails.class, accessDetails)
@@ -228,6 +246,9 @@ public class SynchronousSearchSvcImpl implements ISynchronousSearchSvc {
resources, theRequestDetails, myInterceptorBroadcaster);
SimpleBundleProvider bundleProvider = new SimpleBundleProvider(resources);
+ if (hasACount) {
+ bundleProvider.setTotalResourcesRequestedReturned(receivedResourceCount);
+ }
if (theParams.isOffsetQuery()) {
bundleProvider.setCurrentPageOffset(theParams.getOffset());
bundleProvider.setCurrentPageSize(theParams.getCount());
diff --git a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/search/SynchronousSearchSvcImplTest.java b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/search/SynchronousSearchSvcImplTest.java
index 5c9309b4369..456f42ee90a 100644
--- a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/search/SynchronousSearchSvcImplTest.java
+++ b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/search/SynchronousSearchSvcImplTest.java
@@ -42,14 +42,17 @@ public class SynchronousSearchSvcImplTest extends BaseSearchSvc {
@Test
public void testSynchronousSearch() {
- when(mySearchBuilderFactory.newSearchBuilder(any(), any(), any())).thenReturn(mySearchBuilder);
+ when(mySearchBuilderFactory.newSearchBuilder(any(), any(), any()))
+ .thenReturn(mySearchBuilder);
SearchParameterMap params = new SearchParameterMap();
List pids = createPidSequence(800);
- when(mySearchBuilder.createQuery(same(params), any(), any(), nullable(RequestPartitionId.class))).thenReturn(new BaseSearchSvc.ResultIterator(pids.iterator()));
+ when(mySearchBuilder.createQuery(any(SearchParameterMap.class), any(), any(), nullable(RequestPartitionId.class)))
+ .thenReturn(new BaseSearchSvc.ResultIterator(pids.iterator()));
- doAnswer(loadPids()).when(mySearchBuilder).loadResourcesByPid(any(Collection.class), any(Collection.class), any(List.class), anyBoolean(), any());
+ doAnswer(loadPids()).when(mySearchBuilder)
+ .loadResourcesByPid(any(Collection.class), any(Collection.class), any(List.class), anyBoolean(), any());
IBundleProvider result = mySynchronousSearchSvc.executeQuery( "Patient", params, RequestPartitionId.allPartitions());
assertNull(result.getUuid());
@@ -71,8 +74,8 @@ public class SynchronousSearchSvcImplTest extends BaseSearchSvc {
params.setSearchTotalMode(SearchTotalModeEnum.ACCURATE);
List pids = createPidSequence(30);
- when(mySearchBuilder.createCountQuery(same(params), any(String.class),nullable(RequestDetails.class), nullable(RequestPartitionId.class))).thenReturn(20L);
- when(mySearchBuilder.createQuery(same(params), any(), nullable(RequestDetails.class), nullable(RequestPartitionId.class))).thenReturn(new BaseSearchSvc.ResultIterator(pids.subList(10, 20).iterator()));
+ when(mySearchBuilder.createCountQuery(any(SearchParameterMap.class), any(String.class),nullable(RequestDetails.class), nullable(RequestPartitionId.class))).thenReturn(20L);
+ when(mySearchBuilder.createQuery(any(SearchParameterMap.class), any(), nullable(RequestDetails.class), nullable(RequestPartitionId.class))).thenReturn(new BaseSearchSvc.ResultIterator(pids.subList(10, 20).iterator()));
doAnswer(loadPids()).when(mySearchBuilder).loadResourcesByPid(any(Collection.class), any(Collection.class), any(List.class), anyBoolean(), any());
@@ -92,7 +95,8 @@ public class SynchronousSearchSvcImplTest extends BaseSearchSvc {
params.setLoadSynchronousUpTo(100);
List pids = createPidSequence(800);
- when(mySearchBuilder.createQuery(same(params), any(), nullable(RequestDetails.class), nullable(RequestPartitionId.class))).thenReturn(new BaseSearchSvc.ResultIterator(pids.iterator()));
+ when(mySearchBuilder.createQuery(any(SearchParameterMap.class), any(), nullable(RequestDetails.class), nullable(RequestPartitionId.class)))
+ .thenReturn(new BaseSearchSvc.ResultIterator(pids.iterator()));
pids = createPidSequence(110);
List finalPids = pids;
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/ConsentEventsDaoR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/ConsentEventsDaoR4Test.java
index 0c1e49496f6..b5493dcebd5 100644
--- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/ConsentEventsDaoR4Test.java
+++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/ConsentEventsDaoR4Test.java
@@ -29,11 +29,11 @@ import org.slf4j.LoggerFactory;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
-import javax.servlet.ServletException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Function;
import java.util.stream.Collectors;
import static org.apache.commons.lang3.StringUtils.leftPad;
@@ -54,7 +54,7 @@ public class ConsentEventsDaoR4Test extends BaseJpaR4SystemTest {
private List myPatientIds;
private List myObservationIdsOddOnly;
private List myObservationIdsEvenOnly;
- private List myObservationIdsWithVersions;
+ private List myObservationIdsWithoutVersions;
private List myPatientIdsEvenOnly;
@AfterEach
@@ -64,13 +64,16 @@ public class ConsentEventsDaoR4Test extends BaseJpaR4SystemTest {
}
@BeforeEach
- public void before() throws ServletException {
+ @Override
+ public void beforeInitMocks() throws Exception {
+ super.beforeInitMocks();
RestfulServer restfulServer = new RestfulServer();
restfulServer.setPagingProvider(myPagingProvider);
when(mySrd.getServer()).thenReturn(restfulServer);
myStorageSettings.setSearchPreFetchThresholds(Arrays.asList(20, 50, 190));
+ restfulServer.setDefaultPageSize(null);
}
@Test
@@ -147,6 +150,7 @@ public class ConsentEventsDaoR4Test extends BaseJpaR4SystemTest {
@Test
public void testSearchAndBlockSome_LoadSynchronous() {
+ // setup
create50Observations();
AtomicInteger hitCount = new AtomicInteger(0);
@@ -281,6 +285,7 @@ public class ConsentEventsDaoR4Test extends BaseJpaR4SystemTest {
@Test
public void testSearchAndBlockSomeOnIncludes_LoadSynchronous() {
+ // setup
create50Observations();
AtomicInteger hitCount = new AtomicInteger(0);
@@ -328,9 +333,8 @@ public class ConsentEventsDaoR4Test extends BaseJpaR4SystemTest {
* returned results because we create it then update it in create50Observations()
*/
assertEquals(1, hitCount.get());
- assertEquals(myObservationIdsWithVersions.subList(90, myObservationIdsWithVersions.size()), sort(interceptedResourceIds));
+ assertEquals(sort(myObservationIdsWithoutVersions.subList(90, myObservationIdsWithoutVersions.size())), sort(interceptedResourceIds));
returnedIdValues.forEach(t -> assertTrue(new IdType(t).getIdPartAsLong() % 2 == 0));
-
}
@Test
@@ -363,7 +367,7 @@ public class ConsentEventsDaoR4Test extends BaseJpaR4SystemTest {
private void create50Observations() {
myPatientIds = new ArrayList<>();
myObservationIds = new ArrayList<>();
- myObservationIdsWithVersions = new ArrayList<>();
+ myObservationIdsWithoutVersions = new ArrayList<>();
Patient p = new Patient();
p.setActive(true);
@@ -383,9 +387,9 @@ public class ConsentEventsDaoR4Test extends BaseJpaR4SystemTest {
final Observation obs1 = new Observation();
obs1.setStatus(Observation.ObservationStatus.FINAL);
obs1.addIdentifier().setSystem("urn:system").setValue("I" + leftPad("" + i, 5, '0'));
- IIdType obs1id = myObservationDao.create(obs1).getId().toUnqualifiedVersionless();
+ IIdType obs1id = myObservationDao.create(obs1).getId();
myObservationIds.add(obs1id.toUnqualifiedVersionless().getValue());
- myObservationIdsWithVersions.add(obs1id.toUnqualifiedVersionless().getValue());
+ myObservationIdsWithoutVersions.add(obs1id.toUnqualifiedVersionless().getValue());
obs1.setId(obs1id);
if (obs1id.getIdPartAsLong() % 2 == 0) {
@@ -394,7 +398,7 @@ public class ConsentEventsDaoR4Test extends BaseJpaR4SystemTest {
obs1.getSubject().setReference(oddPid);
}
myObservationDao.update(obs1);
- myObservationIdsWithVersions.add(obs1id.toUnqualifiedVersionless().getValue());
+ myObservationIdsWithoutVersions.add(obs1id.toUnqualifiedVersionless().getValue());
}
@@ -483,14 +487,24 @@ public class ConsentEventsDaoR4Test extends BaseJpaR4SystemTest {
}
}
- private static List sort(List... theLists) {
+ private List sort(List... theLists) {
+ return sort(id -> {
+ String idParsed = id.substring(id.indexOf("/") + 1);
+ if (idParsed.contains("/_history")) {
+ idParsed = idParsed.substring(0, idParsed.indexOf("/"));
+ }
+ return Long.parseLong(idParsed);
+ }, theLists);
+ }
+
+ private List sort(Function theParser, List... theLists) {
ArrayList retVal = new ArrayList<>();
for (List next : theLists) {
retVal.addAll(next);
}
retVal.sort((o0, o1) -> {
- long i0 = Long.parseLong(o0.substring(o0.indexOf('/') + 1));
- long i1 = Long.parseLong(o1.substring(o1.indexOf('/') + 1));
+ long i0 = theParser.apply(o0);
+ long i1 = theParser.apply(o1);
return (int) (i0 - i1);
});
return retVal;
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java
index e1a535b678b..457ba5e943d 100644
--- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java
+++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java
@@ -114,6 +114,7 @@ import static org.hamcrest.Matchers.not;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.ArgumentMatchers.eq;
@@ -1229,6 +1230,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test
nextChunk.forEach(t -> foundIds.add(t.getIdElement().toUnqualifiedVersionless().getValue()));
}
+ assertEquals(ids.size(), foundIds.size());
ids.sort(new ComparableComparator<>());
foundIds.sort(new ComparableComparator<>());
assertEquals(ids, foundIds);
@@ -1327,7 +1329,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test
myCaptureQueriesListener.logSelectQueries();
assertEquals(2, myCaptureQueriesListener.countSelectQueries());
assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("SELECT t0.RES_ID FROM HFJ_SPIDX_TOKEN t0"));
- assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '5'"));
+ assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '6'"));
assertEquals(0, myCaptureQueriesListener.countInsertQueries());
assertEquals(0, myCaptureQueriesListener.countUpdateQueries());
assertEquals(0, myCaptureQueriesListener.countDeleteQueries());
@@ -1343,7 +1345,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test
myCaptureQueriesListener.logSelectQueries();
assertEquals(2, myCaptureQueriesListener.countSelectQueries());
assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("SELECT t0.RES_ID FROM HFJ_SPIDX_TOKEN t0"));
- assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '5'"));
+ assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '6'"));
assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("offset '5'"));
assertEquals(0, myCaptureQueriesListener.countInsertQueries());
assertEquals(0, myCaptureQueriesListener.countUpdateQueries());
@@ -1351,22 +1353,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test
assertEquals(1, myCaptureQueriesListener.countCommits());
assertEquals(0, myCaptureQueriesListener.countRollbacks());
- assertThat(outcome.getLink("next").getUrl(), containsString("Patient?_count=5&_offset=10&active=true"));
-
- // Third page (no results)
-
- myCaptureQueriesListener.clear();
- outcome = myClient.search().forResource("Patient").where(Patient.ACTIVE.exactly().code("true")).offset(10).count(5).returnBundle(Bundle.class).execute();
- assertThat(toUnqualifiedVersionlessIdValues(outcome).toString(), toUnqualifiedVersionlessIdValues(outcome), empty());
- myCaptureQueriesListener.logSelectQueries();
- assertEquals(1, myCaptureQueriesListener.countSelectQueries());
- assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("SELECT t0.RES_ID FROM HFJ_SPIDX_TOKEN t0"));
- assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '5'"));
- assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("offset '10'"));
- assertEquals(0, myCaptureQueriesListener.countInsertQueries());
- assertEquals(0, myCaptureQueriesListener.countUpdateQueries());
- assertEquals(0, myCaptureQueriesListener.countDeleteQueries());
-
+ assertNull(outcome.getLink("next"));
}
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/ForceOffsetSearchModeInterceptorTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/ForceOffsetSearchModeInterceptorTest.java
index 716d631232b..945f6f19472 100644
--- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/ForceOffsetSearchModeInterceptorTest.java
+++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/ForceOffsetSearchModeInterceptorTest.java
@@ -13,10 +13,7 @@ import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.empty;
-import static org.hamcrest.Matchers.emptyOrNullString;
import static org.hamcrest.Matchers.hasSize;
-import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
@@ -66,7 +63,7 @@ public class ForceOffsetSearchModeInterceptorTest extends BaseResourceProviderR4
myCaptureQueriesListener.logSelectQueries();
assertEquals(2, myCaptureQueriesListener.countSelectQueries());
assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("SELECT t0.RES_ID FROM HFJ_SPIDX_TOKEN t0"));
- assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '5'"));
+ assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '6'"));
assertEquals(0, myCaptureQueriesListener.countInsertQueries());
assertEquals(0, myCaptureQueriesListener.countUpdateQueries());
assertEquals(0, myCaptureQueriesListener.countDeleteQueries());
@@ -91,7 +88,7 @@ public class ForceOffsetSearchModeInterceptorTest extends BaseResourceProviderR4
myCaptureQueriesListener.logSelectQueries();
assertEquals(2, myCaptureQueriesListener.countSelectQueries());
assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("SELECT t0.RES_ID FROM HFJ_SPIDX_TOKEN t0"));
- assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '5'"));
+ assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '6'"));
assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("offset '5'"));
assertEquals(0, myCaptureQueriesListener.countInsertQueries());
assertEquals(0, myCaptureQueriesListener.countUpdateQueries());
@@ -99,31 +96,7 @@ public class ForceOffsetSearchModeInterceptorTest extends BaseResourceProviderR4
assertEquals(1, myCaptureQueriesListener.countCommits());
assertEquals(0, myCaptureQueriesListener.countRollbacks());
- assertThat(outcome.getLink("next").getUrl(), containsString("Patient?_count=5&_offset=10&active=true"));
-
- // Third page (no results)
-
- myCaptureQueriesListener.clear();
- Bundle outcome3 = myClient
- .search()
- .forResource("Patient")
- .where(Patient.ACTIVE.exactly().code("true"))
- .offset(10)
- .count(5)
- .returnBundle(Bundle.class)
- .execute();
- assertThat(toUnqualifiedVersionlessIdValues(outcome3).toString(), toUnqualifiedVersionlessIdValues(outcome3), empty());
- myCaptureQueriesListener.logSelectQueries();
- assertEquals(1, myCaptureQueriesListener.countSelectQueries());
- assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("SELECT t0.RES_ID FROM HFJ_SPIDX_TOKEN t0"));
- assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '5'"));
- assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("offset '10'"));
- assertEquals(0, myCaptureQueriesListener.countInsertQueries());
- assertEquals(0, myCaptureQueriesListener.countUpdateQueries());
- assertEquals(0, myCaptureQueriesListener.countDeleteQueries());
-
- assertNull(outcome3.getLink("next"), () -> outcome3.getLink("next").getUrl());
-
+ assertNull(outcome.getLink("next"));
}
@Test
@@ -148,11 +121,7 @@ public class ForceOffsetSearchModeInterceptorTest extends BaseResourceProviderR4
assertThat(secondPageBundle.getEntry(), hasSize(5));
- Bundle thirdPageBundle = myClient.loadPage().next(secondPageBundle).execute();
-
- assertThat(thirdPageBundle.getEntry(), hasSize(0));
- assertNull(thirdPageBundle.getLink("next"), () -> thirdPageBundle.getLink("next").getUrl());
-
+ assertNull(secondPageBundle.getLink("next"));
}
@@ -180,7 +149,7 @@ public class ForceOffsetSearchModeInterceptorTest extends BaseResourceProviderR4
myCaptureQueriesListener.logSelectQueries();
assertEquals(2, myCaptureQueriesListener.countSelectQueries());
assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("SELECT t0.RES_ID FROM HFJ_SPIDX_TOKEN t0"));
- assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '7'"));
+ assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '8'"));
assertEquals(0, myCaptureQueriesListener.countInsertQueries());
assertEquals(0, myCaptureQueriesListener.countUpdateQueries());
assertEquals(0, myCaptureQueriesListener.countDeleteQueries());
@@ -203,7 +172,7 @@ public class ForceOffsetSearchModeInterceptorTest extends BaseResourceProviderR4
myCaptureQueriesListener.logSelectQueries();
assertEquals(2, myCaptureQueriesListener.countSelectQueries());
assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("SELECT t0.RES_ID FROM HFJ_SPIDX_TOKEN t0"));
- assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '7'"));
+ assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '8'"));
assertEquals(0, myCaptureQueriesListener.countInsertQueries());
assertEquals(0, myCaptureQueriesListener.countUpdateQueries());
assertEquals(0, myCaptureQueriesListener.countDeleteQueries());
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderCustomSearchParamR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderCustomSearchParamR4Test.java
index 22f2e3e19df..d5ac558d272 100644
--- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderCustomSearchParamR4Test.java
+++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderCustomSearchParamR4Test.java
@@ -484,7 +484,6 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide
*/
@Test
public void testCustomParameterMatchingManyValues() {
-
List found = new ArrayList<>();
class Interceptor {
@@ -496,7 +495,6 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide
Interceptor interceptor = new Interceptor();
myInterceptorRegistry.registerInterceptor(interceptor);
try {
-
int textIndex = 0;
List ids = new ArrayList<>();
for (int i = 0; i < 200; i++) {
@@ -549,9 +547,8 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide
ourLog.info("Found: {}", found);
runInTransaction(() -> {
-
- List currentResults = myEntityManager.createNativeQuery("select distinct resourceta0_.RES_ID as col_0_0_ from HFJ_RESOURCE resourceta0_ left outer join HFJ_SPIDX_STRING myparamsst1_ on resourceta0_.RES_ID=myparamsst1_.RES_ID where myparamsst1_.HASH_NORM_PREFIX='5901791607832193956' and (myparamsst1_.SP_VALUE_NORMALIZED like 'SECTION%') limit '500'").getResultList();
- List currentResources = myEntityManager.createNativeQuery("select resourceta0_.RES_ID as col_0_0_ from HFJ_RESOURCE resourceta0_").getResultList();
+ List> currentResults = myEntityManager.createNativeQuery("select distinct resourceta0_.RES_ID as col_0_0_ from HFJ_RESOURCE resourceta0_ left outer join HFJ_SPIDX_STRING myparamsst1_ on resourceta0_.RES_ID=myparamsst1_.RES_ID where myparamsst1_.HASH_NORM_PREFIX='5901791607832193956' and (myparamsst1_.SP_VALUE_NORMALIZED like 'SECTION%') limit '500'").getResultList();
+ List> currentResources = myEntityManager.createNativeQuery("select resourceta0_.RES_ID as col_0_0_ from HFJ_RESOURCE resourceta0_").getResultList();
List searches = mySearchEntityDao.findAll();
assertEquals(1, searches.size());
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4EverythingTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4EverythingTest.java
index ea08b3b0b15..171120f54cb 100644
--- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4EverythingTest.java
+++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4EverythingTest.java
@@ -1012,7 +1012,6 @@ public class ResourceProviderR4EverythingTest extends BaseResourceProviderR4Test
assertThat(ids, containsInAnyOrder("Patient/FOO", "Observation/BAZ"));
}
-
@Test
public void testPagingOverEverythingSet() throws InterruptedException {
Patient p = new Patient();
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java
index f2cb94da312..722cc8ae8e1 100644
--- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java
+++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java
@@ -25,11 +25,13 @@ import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.parser.StrictErrorHandler;
+import ca.uhn.fhir.rest.api.CacheControlDirective;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.PreferReturnEnum;
import ca.uhn.fhir.rest.api.SearchTotalModeEnum;
import ca.uhn.fhir.rest.api.SummaryEnum;
+import ca.uhn.fhir.rest.api.server.IRestfulServer;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.client.apache.ResourceEntity;
import ca.uhn.fhir.rest.client.api.IClientInterceptor;
@@ -42,6 +44,7 @@ import ca.uhn.fhir.rest.gclient.NumberClientParam;
import ca.uhn.fhir.rest.gclient.StringClientParam;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
+import ca.uhn.fhir.rest.server.IPagingProvider;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
@@ -159,8 +162,10 @@ import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
+import org.mockito.Spy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.util.AopTestUtils;
import org.springframework.transaction.TransactionStatus;
@@ -220,6 +225,9 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
@SuppressWarnings("Duplicates")
public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
@@ -255,6 +263,8 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
myStorageSettings.setUpdateWithHistoryRewriteEnabled(false);
myStorageSettings.setPreserveRequestIdInResourceBody(false);
+ when(myPagingProvider.canStoreSearchResults())
+ .thenCallRealMethod();
}
@BeforeEach
@@ -2718,6 +2728,90 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
assertEquals(total + 1, ids.size());
}
+
+ @ParameterizedTest
+ @CsvSource({
+ "true,19,10",
+ "false,19,10",
+ "true,20,0",
+ "false,20,0"
+ })
+ public void testPagingWithIncludesReturnsConsistentValues(
+ boolean theAllowStoringSearchResults,
+ int theResourceCount,
+ int theOrgCount
+ ) {
+ // setup
+
+ // create resources
+ {
+ Coding tagCode = new Coding();
+ tagCode.setCode("test");
+ tagCode.setSystem("http://example.com");
+ int orgCount = theOrgCount;
+ for (int i = 0; i < theResourceCount; i++) {
+ Task t = new Task();
+ t.getMeta()
+ .addTag(tagCode);
+ t.setStatus(Task.TaskStatus.REQUESTED);
+ if (orgCount > 0) {
+ Organization org = new Organization();
+ org.setName("ORG");
+ IIdType orgId = myOrganizationDao.create(org).getId().toUnqualifiedVersionless();
+
+ orgCount--;
+ t.getOwner().setReference(orgId.getValue());
+ }
+ myTaskDao.create(t);
+ }
+ }
+
+ // when
+ if (!theAllowStoringSearchResults) {
+ // we don't actually allow this in our current
+ // pagingProvider implementations (except for history).
+ // But we will test with it because our ResponsePage
+ // is what's under test here
+ when(myPagingProvider.canStoreSearchResults())
+ .thenReturn(false);
+ }
+
+ int requestedAmount = 10;
+ Bundle bundle = myClient
+ .search()
+ .byUrl("Task?_count=10&_tag=test&status=requested&_include=Task%3Aowner&_sort=status")
+ .returnBundle(Bundle.class)
+ .execute();
+ int count = bundle.getEntry().size();
+ assertFalse(bundle.getEntry().isEmpty());
+
+ String nextUrl = null;
+ do {
+ Bundle.BundleLinkComponent nextLink = bundle.getLink("next");
+ if (nextLink != null) {
+ nextUrl = nextLink.getUrl();
+
+ // make sure we're always requesting 10
+ assertTrue(nextUrl.contains(String.format("_count=%d", requestedAmount)));
+
+ // get next batch
+ bundle = myClient.fetchResourceFromUrl(Bundle.class, nextUrl);
+ int received = bundle.getEntry().size();
+
+ // every next result should produce results
+ assertFalse(bundle.getEntry().isEmpty());
+ count += received;
+ } else {
+ nextUrl = null;
+ }
+ } while (nextUrl != null);
+
+ // verify
+ // we should receive all resources and linked resources
+ assertEquals(theResourceCount + theOrgCount, count);
+ }
+
+
@Test
public void testPagingWithIncludesReturnsConsistentValues() {
// setup
@@ -3204,7 +3298,11 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
});
myCaptureQueriesListener.logAllQueriesForCurrentThread();
- Bundle bundle = myClient.search().forResource("Patient").returnBundle(Bundle.class).execute();
+ Bundle bundle = myClient
+ .search()
+ .forResource("Patient")
+ .returnBundle(Bundle.class)
+ .execute();
ourLog.debug("Result: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle));
assertEquals(2, bundle.getTotal());
assertEquals(1, bundle.getEntry().size());
diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaR4Test.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaR4Test.java
index 7bf94fc8e07..a0d630c9b12 100644
--- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaR4Test.java
+++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaR4Test.java
@@ -215,7 +215,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
@ExtendWith(SpringExtension.class)
-@ContextConfiguration(classes = {TestR4Config.class})
+@ContextConfiguration(classes = {
+ TestR4Config.class
+})
public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuilder {
public static final String MY_VALUE_SET = "my-value-set";
public static final String URL_MY_VALUE_SET = "http://example.com/my_value_set";
@@ -398,6 +400,7 @@ public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuil
@Autowired
@Qualifier("myOrganizationAffiliationDaoR4")
protected IFhirResourceDao myOrganizationAffiliationDao;
+
@Autowired
protected DatabaseBackedPagingProvider myPagingProvider;
@Autowired
diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestHapiJpaConfig.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestHapiJpaConfig.java
new file mode 100644
index 00000000000..5ce4a94655d
--- /dev/null
+++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestHapiJpaConfig.java
@@ -0,0 +1,21 @@
+package ca.uhn.fhir.jpa.test.config;
+
+import ca.uhn.fhir.jpa.config.HapiJpaConfig;
+import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import static org.mockito.Mockito.spy;
+
+/**
+ * This is a Test configuration class that allows spying underlying JpaConfigs beans
+ */
+@Configuration
+public class TestHapiJpaConfig extends HapiJpaConfig {
+
+ @Override
+ @Bean
+ public DatabaseBackedPagingProvider databaseBackedPagingProvider() {
+ return spy(super.databaseBackedPagingProvider());
+ }
+}
diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR4Config.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR4Config.java
index 98765d50c18..98942ba4d5a 100644
--- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR4Config.java
+++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR4Config.java
@@ -24,7 +24,6 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.batch2.JpaBatch2Config;
import ca.uhn.fhir.jpa.binary.api.IBinaryStorageSvc;
import ca.uhn.fhir.jpa.binstore.MemoryBinaryStorageSvcImpl;
-import ca.uhn.fhir.jpa.config.HapiJpaConfig;
import ca.uhn.fhir.jpa.config.PackageLoaderConfig;
import ca.uhn.fhir.jpa.config.r4.JpaR4Config;
import ca.uhn.fhir.jpa.config.util.HapiEntityManagerFactoryUtil;
@@ -65,7 +64,7 @@ import static org.junit.jupiter.api.Assertions.fail;
@Import({
JpaR4Config.class,
PackageLoaderConfig.class,
- HapiJpaConfig.class,
+ TestHapiJpaConfig.class,
TestJPAConfig.class,
TestHSearchAddInConfig.DefaultLuceneHeap.class,
JpaBatch2Config.class,
diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/IBundleProvider.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/IBundleProvider.java
index 5e1b96d63c7..77c02105714 100644
--- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/IBundleProvider.java
+++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/IBundleProvider.java
@@ -119,6 +119,7 @@ public interface IBundleProvider {
* server's processing rules (e.g. _include'd resources, OperationOutcome, etc.). For example,
* if the method is invoked with index 0,10 the method might return 10 search results, plus an
* additional 20 resources which matched a client's _include specification.
+ *
*
* Note that if this bundle provider was loaded using a
* page ID (i.e. via {@link ca.uhn.fhir.rest.server.IPagingProvider#retrieveResultList(RequestDetails, String, String)}
diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/SimpleBundleProvider.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/SimpleBundleProvider.java
index e9479ccd7ad..588f1909e7a 100644
--- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/SimpleBundleProvider.java
+++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/SimpleBundleProvider.java
@@ -43,6 +43,15 @@ public class SimpleBundleProvider implements IBundleProvider {
private Integer myCurrentPageSize;
private ResponsePage.ResponsePageBuilder myPageBuilder;
+ /**
+ * The actual number of resources we have tried to fetch.
+ * This value will only be populated if there is a
+ * _count query parameter provided.
+ * In which case, it will be the total number of resources
+ * we tried to fetch (should be _count + 1 for accurate paging)
+ */
+ private int myTotalResourcesRequestedReturned = -1;
+
/**
* Constructor
*/
@@ -144,6 +153,7 @@ public class SimpleBundleProvider implements IBundleProvider {
@Override
public List getResources(
int theFromIndex, int theToIndex, @Nonnull ResponsePage.ResponsePageBuilder theResponsePageBuilder) {
+ theResponsePageBuilder.setTotalRequestedResourcesFetched(myTotalResourcesRequestedReturned);
return (List)
myList.subList(Math.min(theFromIndex, myList.size()), Math.min(theToIndex, myList.size()));
}
@@ -153,6 +163,10 @@ public class SimpleBundleProvider implements IBundleProvider {
return myUuid;
}
+ public void setTotalResourcesRequestedReturned(int theAmount) {
+ myTotalResourcesRequestedReturned = theAmount;
+ }
+
/**
* Defaults to null
*/
diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ResponseBundleBuilder.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ResponseBundleBuilder.java
index 0d99af7ea91..b3815d66d45 100644
--- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ResponseBundleBuilder.java
+++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ResponseBundleBuilder.java
@@ -105,8 +105,11 @@ public class ResponseBundleBuilder {
pageSize = pagingCalculatePageSize(requestedPage, server.getPagingProvider());
Integer size = bundleProvider.size();
- numToReturn =
- (size == null) ? pageSize : Math.min(pageSize, size.intValue() - theResponseBundleRequest.offset);
+ if (size == null) {
+ numToReturn = pageSize;
+ } else {
+ numToReturn = Math.min(pageSize, size.intValue() - theResponseBundleRequest.offset);
+ }
resourceList =
pagingBuildResourceList(theResponseBundleRequest, bundleProvider, numToReturn, responsePageBuilder);
@@ -252,6 +255,7 @@ public class ResponseBundleBuilder {
RestfulServerUtils.prettyPrintResponse(server, theResponseBundleRequest.requestDetails),
theResponseBundleRequest.bundleType);
+ // set self link
retval.setSelf(theResponseBundleRequest.linkSelf);
// determine if we are using offset / uncached pages
diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ResponsePage.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ResponsePage.java
index 5f797ffd435..7bfa3294938 100644
--- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ResponsePage.java
+++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ResponsePage.java
@@ -71,6 +71,16 @@ public class ResponsePage {
* even though it will change number of resources returned.
*/
private final int myOmittedResourceCount;
+ /**
+ * This is the total count of requested resources
+ * (ie, non-omitted, non-_include'd resource count).
+ * We typically fetch (for offset queries) 1 more than
+ * we need so we know if there is an additional page
+ * to fetch.
+ * But this is determined by the implementers of
+ * IBundleProvider.
+ */
+ private final int myTotalRequestedResourcesFetched;
/**
* The bundle provider.
@@ -109,6 +119,7 @@ public class ResponsePage {
int theNumToReturn,
int theIncludedResourceCount,
int theOmittedResourceCount,
+ int theTotalRequestedResourcesFetched,
IBundleProvider theBundleProvider) {
mySearchId = theSearchId;
myResourceList = theResourceList;
@@ -116,6 +127,7 @@ public class ResponsePage {
myNumToReturn = theNumToReturn;
myIncludedResourceCount = theIncludedResourceCount;
myOmittedResourceCount = theOmittedResourceCount;
+ myTotalRequestedResourcesFetched = theTotalRequestedResourcesFetched;
myBundleProvider = theBundleProvider;
myNumTotalResults = myBundleProvider.size();
@@ -190,24 +202,16 @@ public class ResponsePage {
return StringUtils.isNotBlank(myBundleProvider.getNextPageId());
case NONCACHED_OFFSET:
if (myNumTotalResults == null) {
- /*
- * Having a null total results is synonymous with
- * having a next link. Once our results are exhausted,
- * we will always have a myNumTotalResults value.
- *
- * Alternatively, if _total=accurate is provided,
- * we'll also have a myNumTotalResults value.
- */
- return true;
+ if (hasNextPageWithoutKnowingTotal()) {
+ return true;
+ }
} else if (myNumTotalResults > myNumToReturn + ObjectUtils.defaultIfNull(myRequestedPage.offset, 0)) {
return true;
}
break;
case SAVED_SEARCH:
if (myNumTotalResults == null) {
- if (myPageSize == myResourceList.size() + myOmittedResourceCount - myIncludedResourceCount) {
- // if the size of the resource list - included resources + omitted resources == pagesize
- // we have more pages
+ if (hasNextPageWithoutKnowingTotal()) {
return true;
}
} else if (myResponseBundleRequest.offset + myNumToReturn < myNumTotalResults) {
@@ -220,6 +224,53 @@ public class ResponsePage {
return false;
}
+ /**
+ * If myNumTotalResults is null, it typically means we don't
+ * have an accurate total.
+ *
+ * Ie, we're in the middle of a set of pages (of non-named page results),
+ * and _total=accurate was not passed.
+ *
+ * This typically always means that a
+ * 'next' link definitely exists.
+ *
+ * But there are cases where this might not be true:
+ * * the last page of a search that also has an _include
+ * query parameter where the total of resources + _include'd
+ * resources is > the page size expected to be returned.
+ * * the last page of a search that returns the exact number
+ * of resources requested
+ *
+ * In these case, we must check to see if the returned
+ * number of *requested* resources.
+ * If our bundleprovider has fetched > requested,
+ * we'll know that there are more resources already.
+ * But if it hasn't, we'll have to check pagesize compared to
+ * _include'd count, omitted count, and resource count.
+ */
+ private boolean hasNextPageWithoutKnowingTotal() {
+ // if we have totalRequestedResource count, and it's not equal to pagesize,
+ // then we can use this, alone, to determine if there are more pages
+ if (myTotalRequestedResourcesFetched >= 0) {
+ if (myPageSize < myTotalRequestedResourcesFetched) {
+ return true;
+ }
+ } else {
+ // otherwise we'll try and determine if there are next links based on the following
+ // calculation:
+ // resourceList.size - included resources + omitted resources == pagesize
+ // -> we (most likely) have more resources
+ if (myPageSize == myResourceList.size() - myIncludedResourceCount + myOmittedResourceCount) {
+ ourLog.warn(
+ "Returning a next page based on calculated resource count."
+ + " This could be inaccurate if the exact number of resources were fetched is equal to the pagesize requested. "
+ + " Consider setting ResponseBundleBuilder.setTotalResourcesFetchedRequest after fetching resources.");
+ return true;
+ }
+ }
+ return false;
+ }
+
public void setNextPageIfNecessary(BundleLinks theLinks) {
if (hasNextPage()) {
String next;
@@ -356,9 +407,10 @@ public class ResponsePage {
private int myIncludedResourceCount;
private int myOmittedResourceCount;
private IBundleProvider myBundleProvider;
+ private int myTotalRequestedResourcesFetched = -1;
- public ResponsePageBuilder setToOmittedResourceCount(int theOmittedResourcesCountToAdd) {
- myOmittedResourceCount = theOmittedResourcesCountToAdd;
+ public ResponsePageBuilder setOmittedResourceCount(int theOmittedResourceCount) {
+ myOmittedResourceCount = theOmittedResourceCount;
return this;
}
@@ -392,6 +444,36 @@ public class ResponsePage {
return this;
}
+ public ResponsePageBuilder setTotalRequestedResourcesFetched(int theTotalRequestedResourcesFetched) {
+ myTotalRequestedResourcesFetched = theTotalRequestedResourcesFetched;
+ return this;
+ }
+
+ /**
+ * Combine this builder with a second buider.
+ * Useful if a second page is requested, but you do not wish to
+ * overwrite the current values.
+ *
+ * Will not replace searchId, nor IBundleProvider (which should be
+ * the exact same for any subsequent searches anyways).
+ *
+ * Will also not copy pageSize nor numToReturn, as these should be
+ * the same for any single search result set.
+ *
+ * @param theSecondBuilder - a second builder (cannot be this one)
+ */
+ public void combineWith(ResponsePageBuilder theSecondBuilder) {
+ assert theSecondBuilder != this; // don't want to combine with itself
+
+ if (myTotalRequestedResourcesFetched != -1 && theSecondBuilder.myTotalRequestedResourcesFetched != -1) {
+ myTotalRequestedResourcesFetched += theSecondBuilder.myTotalRequestedResourcesFetched;
+ }
+
+ // primitives can always be added
+ myIncludedResourceCount += theSecondBuilder.myIncludedResourceCount;
+ myOmittedResourceCount += theSecondBuilder.myOmittedResourceCount;
+ }
+
public ResponsePage build() {
return new ResponsePage(
mySearchId, // search id
@@ -400,6 +482,7 @@ public class ResponsePage {
myNumToReturn, // num to return
myIncludedResourceCount, // included count
myOmittedResourceCount, // omitted resources
+ myTotalRequestedResourcesFetched, // total count of requested resources
myBundleProvider // the bundle provider
);
}
diff --git a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/api/server/method/ResponsePageTest.java b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/api/server/method/ResponsePageTest.java
index 0546cdd8cd5..b5e00d6839b 100644
--- a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/api/server/method/ResponsePageTest.java
+++ b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/api/server/method/ResponsePageTest.java
@@ -161,17 +161,24 @@ public class ResponsePageTest {
*/
@ParameterizedTest
@CsvSource({
- "true,false,true",
- "true,true,true",
- "false,false,false",
- "false,true,false",
- "false,false,true",
- "false,true,true"
+ "true,false,true,true",
+ "true,true,true,true",
+ "false,false,false,true",
+ "false,true,false,true",
+ "false,false,true,true",
+ "false,true,true,true",
+ "true,false,true,false",
+ "true,true,true,false",
+ "false,false,false,false",
+ "false,true,false,false",
+ "false,false,true,false",
+ "false,true,true,false"
})
public void nonCachedOffsetPaging_setsNextPreviousLinks_test(
boolean theNumTotalResultsIsNull,
boolean theHasPreviousBoolean,
- boolean theHasNextBoolean
+ boolean theHasNextBoolean,
+ boolean theHasTotalRequestedCountBool
) {
// setup
myBundleBuilder
@@ -193,6 +200,11 @@ public class ResponsePageTest {
} else {
when(myBundleProvider.size())
.thenReturn(null);
+ if (theHasTotalRequestedCountBool) {
+ myBundleBuilder.setTotalRequestedResourcesFetched(11); // 1 more than pagesize
+ } else {
+ myBundleBuilder.setPageSize(10);
+ }
}
RequestedPage requestedPage = new RequestedPage(
@@ -215,19 +227,28 @@ public class ResponsePageTest {
@ParameterizedTest
@CsvSource({
- "true,false,false",
- "true,true,false",
- "true,false,true",
- "true,true,true",
- "false,false,false",
- "false,true,false",
- "false,false,true",
- "false,true,true"
+ "true,false,false,true",
+ "true,true,false,true",
+ "true,false,true,true",
+ "true,true,true,true",
+ "false,false,false,true",
+ "false,true,false,true",
+ "false,false,true,true",
+ "false,true,true,true",
+ "true,false,false,false",
+ "true,true,false,false",
+ "true,false,true,false",
+ "true,true,true,false",
+ "false,false,false,false",
+ "false,true,false,false",
+ "false,false,true,false",
+ "false,true,true,false"
})
public void savedSearch_setsNextPreviousLinks_test(
boolean theNumTotalResultsIsNull,
boolean theHasPreviousBoolean,
- boolean theHasNextBoolean
+ boolean theHasNextBoolean,
+ boolean theHasTotalRequestedFetched
) {
// setup
int pageSize = myList.size();
@@ -255,6 +276,12 @@ public class ResponsePageTest {
if (!theHasNextBoolean) {
myBundleBuilder.setNumToReturn(pageSize + offset + includeResourceCount);
}
+ } else if (theHasTotalRequestedFetched) {
+ if (theHasNextBoolean) {
+ myBundleBuilder.setTotalRequestedResourcesFetched(pageSize + 1); // 1 more than page size
+ } else {
+ myBundleBuilder.setTotalRequestedResourcesFetched(pageSize);
+ }
}
// when
diff --git a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/BulkDataImportProvider.java b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/BulkDataImportProvider.java
index c75342c414b..6e2396a6401 100644
--- a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/BulkDataImportProvider.java
+++ b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/BulkDataImportProvider.java
@@ -76,13 +76,10 @@ public class BulkDataImportProvider {
public static final String PARAM_INPUT_TYPE = "type";
private static final Logger ourLog = LoggerFactory.getLogger(BulkDataImportProvider.class);
- @Autowired
private IJobCoordinator myJobCoordinator;
- @Autowired
private FhirContext myFhirCtx;
- @Autowired
private IRequestPartitionHelperSvc myRequestPartitionHelperService;
private volatile List myResourceTypeOrder;
@@ -94,14 +91,17 @@ public class BulkDataImportProvider {
super();
}
+ @Autowired
public void setJobCoordinator(IJobCoordinator theJobCoordinator) {
myJobCoordinator = theJobCoordinator;
}
+ @Autowired
public void setFhirContext(FhirContext theCtx) {
myFhirCtx = theCtx;
}
+ @Autowired
public void setRequestPartitionHelperService(IRequestPartitionHelperSvc theRequestPartitionHelperSvc) {
myRequestPartitionHelperService = theRequestPartitionHelperSvc;
}
From 469873aff7a573d9d894bc4e8ff2f6118bdd04b8 Mon Sep 17 00:00:00 2001
From: James Agnew
Date: Wed, 29 Nov 2023 09:16:41 -0500
Subject: [PATCH 04/14] Add migrator to uhnfhirtest (#5508)
* Add migrator to uhnfhirtest
* Fix spotless
* Test fix
---
.../HapiFlywayMigrateDatabaseCommandTest.java | 10 ++--
.../tasks/HapiFhirJpaMigrationTasks.java | 49 ++++++++++--------
.../ca/uhn/fhirtest/config/CommonConfig.java | 6 +++
.../uhn/fhirtest/config/TestAuditConfig.java | 2 +-
.../uhn/fhirtest/config/TestDstu2Config.java | 2 +-
.../uhn/fhirtest/config/TestDstu3Config.java | 2 +-
.../ca/uhn/fhirtest/config/TestR4BConfig.java | 2 +-
.../ca/uhn/fhirtest/config/TestR4Config.java | 2 +-
.../ca/uhn/fhirtest/config/TestR5Config.java | 2 +-
.../migrate/FhirTestAutoMigrator.java | 51 +++++++++++++++++++
10 files changed, 98 insertions(+), 30 deletions(-)
create mode 100644 hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/migrate/FhirTestAutoMigrator.java
diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HapiFlywayMigrateDatabaseCommandTest.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HapiFlywayMigrateDatabaseCommandTest.java
index cebbf9c9a8b..31b74d0b5ab 100644
--- a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HapiFlywayMigrateDatabaseCommandTest.java
+++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HapiFlywayMigrateDatabaseCommandTest.java
@@ -5,11 +5,14 @@ import ca.uhn.fhir.jpa.migrate.JdbcUtils;
import ca.uhn.fhir.jpa.migrate.SchemaMigrator;
import ca.uhn.fhir.jpa.migrate.dao.HapiMigrationDao;
import ca.uhn.fhir.jpa.migrate.entity.HapiMigrationEntity;
+import ca.uhn.fhir.jpa.util.RandomTextUtils;
import ca.uhn.fhir.system.HapiSystemProperties;
import com.google.common.base.Charsets;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
+import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.JdbcTemplate;
@@ -35,10 +38,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
+@TestMethodOrder(MethodOrderer.MethodName.class)
public class HapiFlywayMigrateDatabaseCommandTest {
private static final Logger ourLog = LoggerFactory.getLogger(HapiFlywayMigrateDatabaseCommandTest.class);
- public static final String DB_DIRECTORY = "target/h2_test";
+ private final String myDbDirectory = "target/h2_test/" + RandomTextUtils.newSecureRandomAlphaNumericString(5);
static {
HapiSystemProperties.enableTestMode();
@@ -252,12 +256,12 @@ public class HapiFlywayMigrateDatabaseCommandTest {
@Nonnull
private File getLocation(String theDatabaseName) throws IOException {
- File directory = new File(DB_DIRECTORY);
+ File directory = new File(myDbDirectory);
if (directory.exists()) {
FileUtils.deleteDirectory(directory);
}
- return new File(DB_DIRECTORY + "/" + theDatabaseName);
+ return new File(myDbDirectory + "/" + theDatabaseName);
}
private void seedDatabase340(DriverTypeEnum.ConnectionProperties theConnectionProperties) {
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java
index a0f718dceb8..f480b23ed6e 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java
@@ -977,27 +977,34 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks {
// Ugh. Only oracle supports using IDX_TAG_DEF_TP_CD_SYS to enforce this constraint. The others will
// create another index.
// For Sql Server, should change the index to be unique with include columns. Do this in 6.1
- tagTable.dropIndex("20220429.8", "IDX_TAGDEF_TYPESYSCODE");
- Map addTagDefConstraint = new HashMap<>();
- addTagDefConstraint.put(
- DriverTypeEnum.H2_EMBEDDED,
- "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODE UNIQUE (TAG_TYPE, TAG_CODE, TAG_SYSTEM)");
- addTagDefConstraint.put(
- DriverTypeEnum.MARIADB_10_1,
- "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODE UNIQUE (TAG_TYPE, TAG_CODE, TAG_SYSTEM)");
- addTagDefConstraint.put(
- DriverTypeEnum.MSSQL_2012,
- "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODE UNIQUE (TAG_TYPE, TAG_CODE, TAG_SYSTEM)");
- addTagDefConstraint.put(
- DriverTypeEnum.MYSQL_5_7,
- "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODE UNIQUE (TAG_TYPE, TAG_CODE, TAG_SYSTEM)");
- addTagDefConstraint.put(
- DriverTypeEnum.ORACLE_12C,
- "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODE UNIQUE (TAG_TYPE, TAG_CODE, TAG_SYSTEM)");
- addTagDefConstraint.put(
- DriverTypeEnum.POSTGRES_9_4,
- "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODE UNIQUE (TAG_TYPE, TAG_CODE, TAG_SYSTEM)");
- version.executeRawSql("20220429.9", addTagDefConstraint);
+ // tagTable.dropIndex("20220429.8", "IDX_TAGDEF_TYPESYSCODE");
+ // Map addTagDefConstraint = new HashMap<>();
+ // addTagDefConstraint.put(
+ // DriverTypeEnum.H2_EMBEDDED,
+ // "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODE UNIQUE (TAG_TYPE, TAG_CODE,
+ // TAG_SYSTEM)");
+ // addTagDefConstraint.put(
+ // DriverTypeEnum.MARIADB_10_1,
+ // "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODE UNIQUE (TAG_TYPE, TAG_CODE,
+ // TAG_SYSTEM)");
+ // addTagDefConstraint.put(
+ // DriverTypeEnum.MSSQL_2012,
+ // "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODE UNIQUE (TAG_TYPE, TAG_CODE,
+ // TAG_SYSTEM)");
+ // addTagDefConstraint.put(
+ // DriverTypeEnum.MYSQL_5_7,
+ // "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODE UNIQUE (TAG_TYPE, TAG_CODE,
+ // TAG_SYSTEM)");
+ // addTagDefConstraint.put(
+ // DriverTypeEnum.ORACLE_12C,
+ // "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODE UNIQUE (TAG_TYPE, TAG_CODE,
+ // TAG_SYSTEM)");
+ // addTagDefConstraint.put(
+ // DriverTypeEnum.POSTGRES_9_4,
+ // "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODE UNIQUE (TAG_TYPE, TAG_CODE,
+ // TAG_SYSTEM)");
+ // version.executeRawSql("20220429.9", addTagDefConstraint);
+ version.addNop("20220429.9");
}
// Fix for https://github.com/hapifhir/hapi-fhir-jpaserver-starter/issues/328
diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/CommonConfig.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/CommonConfig.java
index b28a9367462..1d84422e1a2 100644
--- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/CommonConfig.java
+++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/CommonConfig.java
@@ -23,6 +23,7 @@ import ca.uhn.fhir.storage.interceptor.balp.IBalpAuditEventSink;
import ca.uhn.fhirtest.ScheduledSubscriptionDeleter;
import ca.uhn.fhirtest.interceptor.AnalyticsInterceptor;
import ca.uhn.fhirtest.joke.HolyFooCowInterceptor;
+import ca.uhn.fhirtest.migrate.FhirTestAutoMigrator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@@ -131,6 +132,11 @@ public class CommonConfig {
return new FhirTestBalpAuditContextServices();
}
+ @Bean
+ public FhirTestAutoMigrator migrator() {
+ return new FhirTestAutoMigrator();
+ }
+
public static boolean isLocalTestMode() {
return "true".equalsIgnoreCase(System.getProperty("testmode.local"));
}
diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestAuditConfig.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestAuditConfig.java
index bd6c6690f16..03e838b3296 100644
--- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestAuditConfig.java
+++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestAuditConfig.java
@@ -120,7 +120,7 @@ public class TestAuditConfig {
}
extraProperties.put("hibernate.format_sql", "false");
extraProperties.put("hibernate.show_sql", "false");
- extraProperties.put("hibernate.hbm2ddl.auto", "update");
+ extraProperties.put("hibernate.hbm2ddl.auto", "none");
extraProperties.put("hibernate.jdbc.batch_size", "20");
extraProperties.put("hibernate.cache.use_query_cache", "false");
extraProperties.put("hibernate.cache.use_second_level_cache", "false");
diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java
index 7d957240c90..a3e4f203b2a 100644
--- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java
+++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java
@@ -138,7 +138,7 @@ public class TestDstu2Config {
}
extraProperties.put("hibernate.format_sql", "false");
extraProperties.put("hibernate.show_sql", "false");
- extraProperties.put("hibernate.hbm2ddl.auto", "update");
+ extraProperties.put("hibernate.hbm2ddl.auto", "none");
extraProperties.put("hibernate.jdbc.batch_size", "20");
extraProperties.put("hibernate.cache.use_query_cache", "false");
extraProperties.put("hibernate.cache.use_second_level_cache", "false");
diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java
index da6b07ac91b..0e4e285b9a2 100644
--- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java
+++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java
@@ -138,7 +138,7 @@ public class TestDstu3Config {
}
extraProperties.put("hibernate.format_sql", "false");
extraProperties.put("hibernate.show_sql", "false");
- extraProperties.put("hibernate.hbm2ddl.auto", "update");
+ extraProperties.put("hibernate.hbm2ddl.auto", "none");
extraProperties.put("hibernate.jdbc.batch_size", "20");
extraProperties.put("hibernate.cache.use_query_cache", "false");
extraProperties.put("hibernate.cache.use_second_level_cache", "false");
diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4BConfig.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4BConfig.java
index 454faeab90a..bafbdc2daf3 100644
--- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4BConfig.java
+++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4BConfig.java
@@ -136,7 +136,7 @@ public class TestR4BConfig {
}
extraProperties.put("hibernate.format_sql", "false");
extraProperties.put("hibernate.show_sql", "false");
- extraProperties.put("hibernate.hbm2ddl.auto", "update");
+ extraProperties.put("hibernate.hbm2ddl.auto", "none");
extraProperties.put("hibernate.jdbc.batch_size", "20");
extraProperties.put("hibernate.cache.use_query_cache", "false");
extraProperties.put("hibernate.cache.use_second_level_cache", "false");
diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java
index f95782ccc96..1c62b7a5225 100644
--- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java
+++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java
@@ -139,7 +139,7 @@ public class TestR4Config {
}
extraProperties.put("hibernate.format_sql", "false");
extraProperties.put("hibernate.show_sql", "false");
- extraProperties.put("hibernate.hbm2ddl.auto", "update");
+ extraProperties.put("hibernate.hbm2ddl.auto", "none");
extraProperties.put("hibernate.jdbc.batch_size", "20");
extraProperties.put("hibernate.cache.use_query_cache", "false");
extraProperties.put("hibernate.cache.use_second_level_cache", "false");
diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR5Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR5Config.java
index 2870a925c32..e255f2627cb 100644
--- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR5Config.java
+++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR5Config.java
@@ -148,7 +148,7 @@ public class TestR5Config {
}
extraProperties.put("hibernate.format_sql", "false");
extraProperties.put("hibernate.show_sql", "false");
- extraProperties.put("hibernate.hbm2ddl.auto", "update");
+ extraProperties.put("hibernate.hbm2ddl.auto", "none");
extraProperties.put("hibernate.jdbc.batch_size", "20");
extraProperties.put("hibernate.cache.use_query_cache", "false");
extraProperties.put("hibernate.cache.use_second_level_cache", "false");
diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/migrate/FhirTestAutoMigrator.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/migrate/FhirTestAutoMigrator.java
new file mode 100644
index 00000000000..0de60267015
--- /dev/null
+++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/migrate/FhirTestAutoMigrator.java
@@ -0,0 +1,51 @@
+package ca.uhn.fhirtest.migrate;
+
+import ca.uhn.fhir.jpa.migrate.DriverTypeEnum;
+import ca.uhn.fhir.jpa.migrate.HapiMigrationStorageSvc;
+import ca.uhn.fhir.jpa.migrate.MigrationTaskList;
+import ca.uhn.fhir.jpa.migrate.SchemaMigrator;
+import ca.uhn.fhir.jpa.migrate.dao.HapiMigrationDao;
+import ca.uhn.fhir.jpa.migrate.tasks.HapiFhirJpaMigrationTasks;
+import ca.uhn.fhir.util.VersionEnum;
+import ca.uhn.fhirtest.config.CommonConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.util.Properties;
+import java.util.Set;
+import javax.annotation.PostConstruct;
+import javax.sql.DataSource;
+
+public class FhirTestAutoMigrator {
+
+ private static final Logger ourLog = LoggerFactory.getLogger(FhirTestAutoMigrator.class);
+ public static final String MIGRATION_TABLENAME = "MIGRATIONS";
+
+ @Autowired
+ private DataSource myDataSource;
+
+ @PostConstruct
+ public void run() {
+ DriverTypeEnum driver;
+ if (CommonConfig.isLocalTestMode()) {
+ driver = DriverTypeEnum.H2_EMBEDDED;
+ } else {
+ driver = DriverTypeEnum.POSTGRES_9_4;
+ }
+
+ HapiMigrationDao hapiMigrationDao = new HapiMigrationDao(myDataSource, driver, MIGRATION_TABLENAME);
+ HapiMigrationStorageSvc hapiMigrationStorageSvc = new HapiMigrationStorageSvc(hapiMigrationDao);
+
+ MigrationTaskList tasks = new HapiFhirJpaMigrationTasks(Set.of()).getAllTasks(VersionEnum.values());
+
+ SchemaMigrator schemaMigrator = new SchemaMigrator(
+ "HAPI FHIR", MIGRATION_TABLENAME, myDataSource, new Properties(), tasks, hapiMigrationStorageSvc);
+ schemaMigrator.setDriverType(driver);
+
+ ourLog.info("About to run migration...");
+ schemaMigrator.createMigrationTableIfRequired();
+ schemaMigrator.migrate();
+ ourLog.info("Migration complete");
+ }
+}
From e15d0430d0aaac352f4e574773f84375129ef42e Mon Sep 17 00:00:00 2001
From: Ken Stevens
Date: Wed, 29 Nov 2023 18:15:28 -0500
Subject: [PATCH 05/14] Oracle create index migration recovery (#5511)
---
.../5511-oracle-migration-create-index.yaml | 6 ++
.../jpa/migrate/taskdef/AddIndexTaskTest.java | 70 +++++++++++++++++++
.../jpa/migrate/taskdef/AddIndexTask.java | 13 +++-
3 files changed, 86 insertions(+), 3 deletions(-)
create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5511-oracle-migration-create-index.yaml
create mode 100644 hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddIndexTaskTest.java
diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5511-oracle-migration-create-index.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5511-oracle-migration-create-index.yaml
new file mode 100644
index 00000000000..b9eda74e6d7
--- /dev/null
+++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5511-oracle-migration-create-index.yaml
@@ -0,0 +1,6 @@
+---
+type: fix
+issue: 5511
+title: "Previously, when creating an index as a part of a migration, if the index already existed with a different name
+on Oracle, the migration would fail. This has been fixed so that the create index migration task now recovers with
+ a warning message if the index already exists with a different name."
diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddIndexTaskTest.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddIndexTaskTest.java
new file mode 100644
index 00000000000..9e7d5f1ce71
--- /dev/null
+++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddIndexTaskTest.java
@@ -0,0 +1,70 @@
+package ca.uhn.fhir.jpa.migrate.taskdef;
+
+import ca.uhn.fhir.jpa.migrate.DriverTypeEnum;
+import ca.uhn.test.util.LogbackCaptureTestExtension;
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.classic.spi.LoggingEvent;
+import oracle.jdbc.OracleDatabaseException;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.jdbc.UncategorizedSQLException;
+import org.springframework.transaction.TransactionException;
+import org.springframework.transaction.support.TransactionTemplate;
+
+import javax.sql.DataSource;
+import java.sql.SQLException;
+import java.util.Collections;
+import java.util.List;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.hasSize;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+class AddIndexTaskTest {
+ @Mock
+ DriverTypeEnum.ConnectionProperties myConnectionProperties;
+ @Mock
+ DataSource myDataSource;
+ @Mock
+ TransactionTemplate myTransactionTemplate;
+
+ @RegisterExtension
+ LogbackCaptureTestExtension myLogCapture = new LogbackCaptureTestExtension((Logger) AddIndexTask.ourLog, Level.WARN);
+
+
+ @Test
+ void testOracleException() throws SQLException {
+ final AddIndexTask task = new AddIndexTask("1", "1");
+ task.setColumns(Collections.singletonList("COLUMN_NAME"));
+ task.setUnique(true);
+ task.setIndexName("INDEX_NAME");
+ task.setConnectionProperties(myConnectionProperties);
+
+ when(myConnectionProperties.getDataSource()).thenReturn(myDataSource);
+ when(myConnectionProperties.getTxTemplate()).thenReturn(myTransactionTemplate);
+
+ final String sql = "create index INDEX_NAME on TABLE_NAME (COLUMN_NAME)";
+ when(myTransactionTemplate.execute(any()))
+ .thenReturn(Collections.emptySet())
+ .thenThrow(new UncategorizedSQLException("ORA-01408: such column list already indexed", sql, new SQLException("ORA-01408: such column list already indexed", "72000", 1408)));
+
+ myLogCapture.clearEvents();
+
+ // Red-green: this used to throw an exception. Now it logs a warning.
+ task.execute();
+
+ List events = myLogCapture.getLogEvents();
+ assertThat(events, hasSize(1));
+ LoggingEvent event = (LoggingEvent) events.get(0);
+ assertThat(event.getFormattedMessage(), containsString("ORA-01408: such column list already indexed"));
+ }
+}
diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddIndexTask.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddIndexTask.java
index b0d2ae6ccd4..d05b6784e06 100644
--- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddIndexTask.java
+++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddIndexTask.java
@@ -37,7 +37,7 @@ import javax.annotation.Nonnull;
public class AddIndexTask extends BaseTableTask {
- private static final Logger ourLog = LoggerFactory.getLogger(AddIndexTask.class);
+ static final Logger ourLog = LoggerFactory.getLogger(AddIndexTask.class);
private String myIndexName;
private List myColumns;
@@ -97,8 +97,15 @@ public class AddIndexTask extends BaseTableTask {
try {
executeSql(tableName, sql);
} catch (Exception e) {
- if (e.toString().contains("already exists")) {
- ourLog.warn("Index {} already exists", myIndexName);
+ String message = e.toString();
+ if (message.contains("already exists")
+ ||
+ // The Oracle message is ORA-01408: such column list already indexed
+ // TODO KHS consider db-specific handling here that uses the error code instead of the message so
+ // this is language independent
+ // e.g. if the db is Oracle than checking e.getErrorCode() == 1408 should detect this case
+ message.contains("already indexed")) {
+ ourLog.warn("Index {} already exists: {}", myIndexName, e.getMessage());
} else {
throw e;
}
From 8fbbaef0790aadb97fe92dba42ccc1d8da031ee5 Mon Sep 17 00:00:00 2001
From: Luke deGruchy
Date: Thu, 30 Nov 2023 14:56:46 -0500
Subject: [PATCH 06/14] Add a new field to the CLIENT_RESPONSE pointcut in
order to allow clients to mutate an HTTP response from the BaseClient.
(#5488)
* Add a new field to the CLIENT_RESPONSE pointcut in order to allow clients to mutate an HTTP response from the BaseClient.
* Add FhirContext to ClientResponseContext.
* Introduce a ModifiedStringApacheHttpResponse. Run spotless.
* Remove TDOOs, add and update javadoc.
* Spotless and copyright header.
* Add changelog.
* Unique error message code.
* Only trigger the interceptor if the expected return type is a Bundle.
* Fix spotless.
* Code review feedback.
* Spotless.
* Bump to 6.11.3-SNAPSHOT
---
hapi-deployable-pom/pom.xml | 2 +-
hapi-fhir-android/pom.xml | 2 +-
hapi-fhir-base/pom.xml | 2 +-
.../ca/uhn/fhir/interceptor/api/Pointcut.java | 7 +-
.../client/api/ClientResponseContext.java | 103 +++++++++++++
hapi-fhir-bom/pom.xml | 4 +-
hapi-fhir-checkstyle/pom.xml | 2 +-
hapi-fhir-cli/hapi-fhir-cli-api/pom.xml | 2 +-
hapi-fhir-cli/hapi-fhir-cli-app/pom.xml | 2 +-
hapi-fhir-cli/pom.xml | 2 +-
hapi-fhir-client-okhttp/pom.xml | 2 +-
hapi-fhir-client/pom.xml | 2 +-
.../ModifiedStringApacheHttpResponse.java | 135 ++++++++++++++++++
.../uhn/fhir/rest/client/impl/BaseClient.java | 17 +++
hapi-fhir-converter/pom.xml | 2 +-
hapi-fhir-dist/pom.xml | 2 +-
hapi-fhir-docs/pom.xml | 2 +-
...ent-response-pointcut-mutate-response.yaml | 6 +
hapi-fhir-jacoco/pom.xml | 2 +-
hapi-fhir-jaxrsserver-base/pom.xml | 2 +-
hapi-fhir-jpa/pom.xml | 2 +-
hapi-fhir-jpaserver-base/pom.xml | 2 +-
.../pom.xml | 2 +-
hapi-fhir-jpaserver-hfql/pom.xml | 2 +-
hapi-fhir-jpaserver-ips/pom.xml | 2 +-
hapi-fhir-jpaserver-mdm/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-dstu2/pom.xml | 2 +-
hapi-fhir-jpaserver-test-dstu3/pom.xml | 2 +-
hapi-fhir-jpaserver-test-r4/pom.xml | 2 +-
hapi-fhir-jpaserver-test-r4b/pom.xml | 2 +-
hapi-fhir-jpaserver-test-r5/pom.xml | 2 +-
hapi-fhir-jpaserver-test-utilities/pom.xml | 2 +-
hapi-fhir-jpaserver-uhnfhirtest/pom.xml | 2 +-
hapi-fhir-server-cds-hooks/pom.xml | 2 +-
hapi-fhir-server-mdm/pom.xml | 2 +-
hapi-fhir-server-openapi/pom.xml | 2 +-
hapi-fhir-server/pom.xml | 2 +-
.../hapi-fhir-caching-api/pom.xml | 2 +-
.../hapi-fhir-caching-caffeine/pom.xml | 4 +-
.../hapi-fhir-caching-guava/pom.xml | 2 +-
.../hapi-fhir-caching-testing/pom.xml | 2 +-
hapi-fhir-serviceloaders/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-sql-migrate/pom.xml | 2 +-
hapi-fhir-storage-batch2-jobs/pom.xml | 2 +-
.../pom.xml | 2 +-
hapi-fhir-storage-batch2/pom.xml | 2 +-
hapi-fhir-storage-cr/pom.xml | 2 +-
hapi-fhir-storage-mdm/pom.xml | 2 +-
hapi-fhir-storage-test-utilities/pom.xml | 2 +-
hapi-fhir-storage/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-r4b/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-r4b/pom.xml | 2 +-
hapi-fhir-validation-resources-r5/pom.xml | 2 +-
hapi-fhir-validation/pom.xml | 2 +-
hapi-tinder-plugin/pom.xml | 2 +-
hapi-tinder-test/pom.xml | 2 +-
pom.xml | 2 +-
.../pom.xml | 2 +-
.../pom.xml | 2 +-
.../pom.xml | 2 +-
82 files changed, 346 insertions(+), 80 deletions(-)
create mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/ClientResponseContext.java
create mode 100644 hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/ModifiedStringApacheHttpResponse.java
create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml
diff --git a/hapi-deployable-pom/pom.xml b/hapi-deployable-pom/pom.xml
index 14ae49d44f6..25f29f27be9 100644
--- a/hapi-deployable-pom/pom.xml
+++ b/hapi-deployable-pom/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-fhir
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../pom.xml
diff --git a/hapi-fhir-android/pom.xml b/hapi-fhir-android/pom.xml
index c8d934ef99a..6ffe01c5578 100644
--- a/hapi-fhir-android/pom.xml
+++ b/hapi-fhir-android/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml
index 595f263a0df..e67b3027b07 100644
--- a/hapi-fhir-base/pom.xml
+++ b/hapi-fhir-base/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java
index 994ea9cd1cf..64b7267f63f 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java
@@ -93,6 +93,10 @@ public enum Pointcut implements IPointcut {
*
* ca.uhn.fhir.rest.client.api.IRestfulClient - The client object making the request
*
+ *
+ * ca.uhn.fhir.rest.client.api.ClientResponseContext - Contains an IHttpRequest, an IHttpResponse, and an IRestfulClient
+ * and also allows the client to mutate the contained IHttpResponse
+ *
*
*
* Hook methods must return void
.
@@ -101,7 +105,8 @@ public enum Pointcut implements IPointcut {
void.class,
"ca.uhn.fhir.rest.client.api.IHttpRequest",
"ca.uhn.fhir.rest.client.api.IHttpResponse",
- "ca.uhn.fhir.rest.client.api.IRestfulClient"),
+ "ca.uhn.fhir.rest.client.api.IRestfulClient",
+ "ca.uhn.fhir.rest.client.api.ClientResponseContext"),
/**
* Server Hook:
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/ClientResponseContext.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/ClientResponseContext.java
new file mode 100644
index 00000000000..b0748bf2ab7
--- /dev/null
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/ClientResponseContext.java
@@ -0,0 +1,103 @@
+/*-
+ * #%L
+ * HAPI FHIR - Core Library
+ * %%
+ * Copyright (C) 2014 - 2023 Smile CDR, Inc.
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+package ca.uhn.fhir.rest.client.api;
+
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.interceptor.api.Pointcut;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+
+import java.util.Objects;
+import java.util.StringJoiner;
+
+/**
+ * Used to pass context to {@link Pointcut#CLIENT_RESPONSE}, including a mutable {@link IHttpResponse}
+ */
+public class ClientResponseContext {
+ private final IHttpRequest myHttpRequest;
+ private IHttpResponse myHttpResponse;
+ private final IRestfulClient myRestfulClient;
+ private final FhirContext myFhirContext;
+ private final Class extends IBaseResource> myReturnType;
+
+ public ClientResponseContext(
+ IHttpRequest myHttpRequest,
+ IHttpResponse theHttpResponse,
+ IRestfulClient myRestfulClient,
+ FhirContext theFhirContext,
+ Class extends IBaseResource> theReturnType) {
+ this.myHttpRequest = myHttpRequest;
+ this.myHttpResponse = theHttpResponse;
+ this.myRestfulClient = myRestfulClient;
+ this.myFhirContext = theFhirContext;
+ this.myReturnType = theReturnType;
+ }
+
+ public IHttpRequest getHttpRequest() {
+ return myHttpRequest;
+ }
+
+ public IHttpResponse getHttpResponse() {
+ return myHttpResponse;
+ }
+
+ public IRestfulClient getRestfulClient() {
+ return myRestfulClient;
+ }
+
+ public FhirContext getFhirContext() {
+ return myFhirContext;
+ }
+
+ public Class extends IBaseResource> getReturnType() {
+ return myReturnType;
+ }
+
+ public void setHttpResponse(IHttpResponse theHttpResponse) {
+ this.myHttpResponse = theHttpResponse;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ClientResponseContext that = (ClientResponseContext) o;
+ return Objects.equals(myHttpRequest, that.myHttpRequest)
+ && Objects.equals(myHttpResponse, that.myHttpResponse)
+ && Objects.equals(myRestfulClient, that.myRestfulClient)
+ && Objects.equals(myFhirContext, that.myFhirContext)
+ && Objects.equals(myReturnType, that.myReturnType);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(myHttpRequest, myHttpResponse, myRestfulClient, myFhirContext, myReturnType);
+ }
+
+ @Override
+ public String toString() {
+ return new StringJoiner(", ", ClientResponseContext.class.getSimpleName() + "[", "]")
+ .add("myHttpRequest=" + myHttpRequest)
+ .add("myHttpResponse=" + myHttpResponse)
+ .add("myRestfulClient=" + myRestfulClient)
+ .add("myFhirContext=" + myFhirContext)
+ .add("myReturnType=" + myReturnType)
+ .toString();
+ }
+}
diff --git a/hapi-fhir-bom/pom.xml b/hapi-fhir-bom/pom.xml
index d2c92c7102f..8b175d80acc 100644
--- a/hapi-fhir-bom/pom.xml
+++ b/hapi-fhir-bom/pom.xml
@@ -4,7 +4,7 @@
4.0.0
ca.uhn.hapi.fhir
hapi-fhir-bom
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
pom
HAPI FHIR BOM
@@ -12,7 +12,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-checkstyle/pom.xml b/hapi-fhir-checkstyle/pom.xml
index a09540f3cb5..9819c07f04a 100644
--- a/hapi-fhir-checkstyle/pom.xml
+++ b/hapi-fhir-checkstyle/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-fhir
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../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 6cdef901a35..cfcaebca7a9 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
- 6.11.2-SNAPSHOT
+ 6.11.3-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 0dfb56390d6..0bbc80ea717 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
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../pom.xml
diff --git a/hapi-fhir-cli/pom.xml b/hapi-fhir-cli/pom.xml
index d16c69b118e..86345f32a31 100644
--- a/hapi-fhir-cli/pom.xml
+++ b/hapi-fhir-cli/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-fhir
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../pom.xml
diff --git a/hapi-fhir-client-okhttp/pom.xml b/hapi-fhir-client-okhttp/pom.xml
index 5b7c57a6612..0d687f7b1b0 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
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-client/pom.xml b/hapi-fhir-client/pom.xml
index a0acac6d5bc..0f0e49a5f1b 100644
--- a/hapi-fhir-client/pom.xml
+++ b/hapi-fhir-client/pom.xml
@@ -4,7 +4,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/ModifiedStringApacheHttpResponse.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/ModifiedStringApacheHttpResponse.java
new file mode 100644
index 00000000000..28ea8163668
--- /dev/null
+++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/ModifiedStringApacheHttpResponse.java
@@ -0,0 +1,135 @@
+/*
+ * #%L
+ * HAPI FHIR - Client Framework
+ * %%
+ * Copyright (C) 2014 - 2023 Smile CDR, Inc.
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+package ca.uhn.fhir.rest.client.apache;
+
+import ca.uhn.fhir.i18n.Msg;
+import ca.uhn.fhir.rest.client.api.IHttpResponse;
+import ca.uhn.fhir.rest.client.impl.BaseHttpResponse;
+import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
+import ca.uhn.fhir.util.StopWatch;
+import org.apache.commons.io.IOUtils;
+import org.apache.http.client.methods.CloseableHttpResponse;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Process a modified copy of an existing {@link IHttpResponse} with a String containing new content.
+ *
+ * Meant to be used with custom interceptors that need to hijack an existing IHttpResponse with new content.
+ */
+public class ModifiedStringApacheHttpResponse extends BaseHttpResponse implements IHttpResponse {
+ private static final org.slf4j.Logger ourLog =
+ org.slf4j.LoggerFactory.getLogger(ModifiedStringApacheHttpResponse.class);
+ private boolean myEntityBuffered = false;
+ private final String myNewContent;
+ private final IHttpResponse myOrigHttpResponse;
+ private byte[] myEntityBytes = null;
+
+ public ModifiedStringApacheHttpResponse(
+ IHttpResponse theOrigHttpResponse, String theNewContent, StopWatch theResponseStopWatch) {
+ super(theResponseStopWatch);
+ myOrigHttpResponse = theOrigHttpResponse;
+ myNewContent = theNewContent;
+ }
+
+ @Override
+ public void bufferEntity() throws IOException {
+ if (myEntityBuffered) {
+ return;
+ }
+ try (InputStream respEntity = readEntity()) {
+ if (respEntity != null) {
+ try {
+ myEntityBytes = IOUtils.toByteArray(respEntity);
+ } catch (IllegalStateException exception) {
+ throw new InternalErrorException(Msg.code(2447) + exception);
+ }
+ myEntityBuffered = true;
+ }
+ }
+ }
+
+ @Override
+ public void close() {
+ if (myOrigHttpResponse instanceof CloseableHttpResponse) {
+ try {
+ ((CloseableHttpResponse) myOrigHttpResponse).close();
+ } catch (IOException exception) {
+ ourLog.debug("Failed to close response", exception);
+ }
+ }
+ }
+
+ @Override
+ public Reader createReader() throws IOException {
+ return new InputStreamReader(readEntity(), StandardCharsets.UTF_8);
+ }
+
+ @Override
+ public Map> getAllHeaders() {
+ return myOrigHttpResponse.getAllHeaders();
+ }
+
+ @Override
+ public List getHeaders(String theName) {
+ return myOrigHttpResponse.getHeaders(theName);
+ }
+
+ @Override
+ public String getMimeType() {
+ return myOrigHttpResponse.getMimeType();
+ }
+
+ @Override
+ public StopWatch getRequestStopWatch() {
+ return myOrigHttpResponse.getRequestStopWatch();
+ }
+
+ @Override
+ public Object getResponse() {
+ return null;
+ }
+
+ @Override
+ public int getStatus() {
+ return myOrigHttpResponse.getStatus();
+ }
+
+ @Override
+ public String getStatusInfo() {
+ return myOrigHttpResponse.getStatusInfo();
+ }
+
+ @Override
+ public InputStream readEntity() {
+ if (myEntityBuffered) {
+ return new ByteArrayInputStream(myEntityBytes);
+ } else {
+ return new ByteArrayInputStream(myNewContent.getBytes());
+ }
+ }
+}
diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java
index 857fc4181b5..5aa920c920e 100644
--- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java
+++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java
@@ -36,6 +36,7 @@ import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.api.RequestFormatParamStyleEnum;
import ca.uhn.fhir.rest.api.SummaryEnum;
+import ca.uhn.fhir.rest.client.api.ClientResponseContext;
import ca.uhn.fhir.rest.client.api.IHttpClient;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.api.IHttpResponse;
@@ -352,12 +353,24 @@ public abstract class BaseClient implements IRestfulClient {
response = httpRequest.execute();
+ final Class extends IBaseResource> returnType = (binding instanceof ResourceResponseHandler)
+ ? ((ResourceResponseHandler extends IBaseResource>) binding).getReturnType()
+ : null;
+
+ final ClientResponseContext clientResponseContext =
+ new ClientResponseContext(httpRequest, response, this, getFhirContext(), returnType);
HookParams responseParams = new HookParams();
responseParams.add(IHttpRequest.class, httpRequest);
responseParams.add(IHttpResponse.class, response);
responseParams.add(IRestfulClient.class, this);
+ responseParams.add(ClientResponseContext.class, clientResponseContext);
+
getInterceptorService().callHooks(Pointcut.CLIENT_RESPONSE, responseParams);
+ // Replace the contents of the response with whatever the hook returned, or the same response as before if
+ // it no-op'd
+ response = clientResponseContext.getHttpResponse();
+
String mimeType;
if (Constants.STATUS_HTTP_204_NO_CONTENT == response.getStatus()) {
mimeType = null;
@@ -645,6 +658,10 @@ public abstract class BaseClient implements IRestfulClient {
myAllowHtmlResponse = theAllowHtmlResponse;
}
+ public Class getReturnType() {
+ return myReturnType;
+ }
+
@Override
public T invokeClient(
String theResponseMimeType,
diff --git a/hapi-fhir-converter/pom.xml b/hapi-fhir-converter/pom.xml
index 199daabb89d..b92665512e4 100644
--- a/hapi-fhir-converter/pom.xml
+++ b/hapi-fhir-converter/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-dist/pom.xml b/hapi-fhir-dist/pom.xml
index ed159f4d302..0b3d6c514db 100644
--- a/hapi-fhir-dist/pom.xml
+++ b/hapi-fhir-dist/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-fhir
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../pom.xml
diff --git a/hapi-fhir-docs/pom.xml b/hapi-fhir-docs/pom.xml
index c8fb88de598..1031d739903 100644
--- a/hapi-fhir-docs/pom.xml
+++ b/hapi-fhir-docs/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml
new file mode 100644
index 00000000000..31114533369
--- /dev/null
+++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml
@@ -0,0 +1,6 @@
+---
+type: add
+issue: 5502
+jira: SMILE-7262
+title: "It is now possible to mutate an HTTP response from the CLIENT_RESPONSE Pointcut, and pass this mutated response
+ to downstream processing."
diff --git a/hapi-fhir-jacoco/pom.xml b/hapi-fhir-jacoco/pom.xml
index 29520193ebd..5c60903d052 100644
--- a/hapi-fhir-jacoco/pom.xml
+++ b/hapi-fhir-jacoco/pom.xml
@@ -11,7 +11,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jaxrsserver-base/pom.xml b/hapi-fhir-jaxrsserver-base/pom.xml
index e217e6b86b9..3b065ee4a6e 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
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpa/pom.xml b/hapi-fhir-jpa/pom.xml
index 79d5c793ed2..c2c94682ffa 100644
--- a/hapi-fhir-jpa/pom.xml
+++ b/hapi-fhir-jpa/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-base/pom.xml b/hapi-fhir-jpaserver-base/pom.xml
index 1ebd7add4ce..0f2f3d46345 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
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml b/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml
index 7e0dfd800ab..8180a9d47de 100644
--- a/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml
+++ b/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml
@@ -6,7 +6,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-hfql/pom.xml b/hapi-fhir-jpaserver-hfql/pom.xml
index bac3221b4e4..05869115705 100644
--- a/hapi-fhir-jpaserver-hfql/pom.xml
+++ b/hapi-fhir-jpaserver-hfql/pom.xml
@@ -3,7 +3,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-ips/pom.xml b/hapi-fhir-jpaserver-ips/pom.xml
index 747bea9ba73..d470c942b64 100644
--- a/hapi-fhir-jpaserver-ips/pom.xml
+++ b/hapi-fhir-jpaserver-ips/pom.xml
@@ -3,7 +3,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-mdm/pom.xml b/hapi-fhir-jpaserver-mdm/pom.xml
index 699b9e6e1a8..60c8a22e5ca 100644
--- a/hapi-fhir-jpaserver-mdm/pom.xml
+++ b/hapi-fhir-jpaserver-mdm/pom.xml
@@ -6,7 +6,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-model/pom.xml b/hapi-fhir-jpaserver-model/pom.xml
index dbcafa398b4..32074d95ef6 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
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-searchparam/pom.xml b/hapi-fhir-jpaserver-searchparam/pom.xml
index 9e204079c6d..54a2010e026 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
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-subscription/pom.xml b/hapi-fhir-jpaserver-subscription/pom.xml
index c88d574ed07..92e6483c21a 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
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-test-dstu2/pom.xml b/hapi-fhir-jpaserver-test-dstu2/pom.xml
index b0408c3f65e..e11d32efadd 100644
--- a/hapi-fhir-jpaserver-test-dstu2/pom.xml
+++ b/hapi-fhir-jpaserver-test-dstu2/pom.xml
@@ -6,7 +6,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-test-dstu3/pom.xml b/hapi-fhir-jpaserver-test-dstu3/pom.xml
index 5ac8bc1e406..9431d68d8ae 100644
--- a/hapi-fhir-jpaserver-test-dstu3/pom.xml
+++ b/hapi-fhir-jpaserver-test-dstu3/pom.xml
@@ -6,7 +6,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-test-r4/pom.xml b/hapi-fhir-jpaserver-test-r4/pom.xml
index 7712a6347ba..afbf5b0acd2 100644
--- a/hapi-fhir-jpaserver-test-r4/pom.xml
+++ b/hapi-fhir-jpaserver-test-r4/pom.xml
@@ -6,7 +6,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-test-r4b/pom.xml b/hapi-fhir-jpaserver-test-r4b/pom.xml
index 41bcc967b15..b79574ffee6 100644
--- a/hapi-fhir-jpaserver-test-r4b/pom.xml
+++ b/hapi-fhir-jpaserver-test-r4b/pom.xml
@@ -6,7 +6,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-test-r5/pom.xml b/hapi-fhir-jpaserver-test-r5/pom.xml
index 4e0b6f578c5..a7070be80b0 100644
--- a/hapi-fhir-jpaserver-test-r5/pom.xml
+++ b/hapi-fhir-jpaserver-test-r5/pom.xml
@@ -6,7 +6,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.2-SNAPSHOT
+ 6.11.3-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 f9cb2801cba..ae20eb92d4c 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
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml
index 0bc7f2bb52a..2820eb69e50 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
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../pom.xml
diff --git a/hapi-fhir-server-cds-hooks/pom.xml b/hapi-fhir-server-cds-hooks/pom.xml
index 13964da20d3..790ea7408be 100644
--- a/hapi-fhir-server-cds-hooks/pom.xml
+++ b/hapi-fhir-server-cds-hooks/pom.xml
@@ -7,7 +7,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-server-mdm/pom.xml b/hapi-fhir-server-mdm/pom.xml
index 43be75c5975..c940c9a1095 100644
--- a/hapi-fhir-server-mdm/pom.xml
+++ b/hapi-fhir-server-mdm/pom.xml
@@ -7,7 +7,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-server-openapi/pom.xml b/hapi-fhir-server-openapi/pom.xml
index 4b257238089..bb8306309e0 100644
--- a/hapi-fhir-server-openapi/pom.xml
+++ b/hapi-fhir-server-openapi/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-server/pom.xml b/hapi-fhir-server/pom.xml
index 87545c43398..14226a4136d 100644
--- a/hapi-fhir-server/pom.xml
+++ b/hapi-fhir-server/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml
index d9bd49738d6..bb80f784fd3 100644
--- a/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml
+++ b/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml
@@ -7,7 +7,7 @@
hapi-fhir-serviceloaders
ca.uhn.hapi.fhir
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../pom.xml
diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-caffeine/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-caffeine/pom.xml
index 088209cc351..8fd0cc9c89e 100644
--- a/hapi-fhir-serviceloaders/hapi-fhir-caching-caffeine/pom.xml
+++ b/hapi-fhir-serviceloaders/hapi-fhir-caching-caffeine/pom.xml
@@ -7,7 +7,7 @@
hapi-fhir-serviceloaders
ca.uhn.hapi.fhir
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../pom.xml
@@ -21,7 +21,7 @@
ca.uhn.hapi.fhir
hapi-fhir-caching-api
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml
index 687afbf9a17..a0a001b2159 100644
--- a/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml
+++ b/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml
@@ -7,7 +7,7 @@
hapi-fhir-serviceloaders
ca.uhn.hapi.fhir
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../pom.xml
diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-testing/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-testing/pom.xml
index 87ea52486a0..0f56910a478 100644
--- a/hapi-fhir-serviceloaders/hapi-fhir-caching-testing/pom.xml
+++ b/hapi-fhir-serviceloaders/hapi-fhir-caching-testing/pom.xml
@@ -7,7 +7,7 @@
hapi-fhir
ca.uhn.hapi.fhir
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../../pom.xml
diff --git a/hapi-fhir-serviceloaders/pom.xml b/hapi-fhir-serviceloaders/pom.xml
index 13dd141193d..3e84d9f6b65 100644
--- a/hapi-fhir-serviceloaders/pom.xml
+++ b/hapi-fhir-serviceloaders/pom.xml
@@ -5,7 +5,7 @@
hapi-deployable-pom
ca.uhn.hapi.fhir
- 6.11.2-SNAPSHOT
+ 6.11.3-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 e6c86b9552e..bb234e607ea 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
- 6.11.2-SNAPSHOT
+ 6.11.3-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 f19a6bda58f..a576a9a2a4e 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
- 6.11.2-SNAPSHOT
+ 6.11.3-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 f88d87a0b98..58ce092f00d 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
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
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 2e09e1b0da9..0804a3fe706 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
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
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 a8ea0668e3d..719aa8d1726 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
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
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 6da838865ba..527947da607 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
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-spring-boot/pom.xml b/hapi-fhir-spring-boot/pom.xml
index 5308ca8d290..3fa3a852d1c 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
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../pom.xml
diff --git a/hapi-fhir-sql-migrate/pom.xml b/hapi-fhir-sql-migrate/pom.xml
index ad912b14e25..809ed62b838 100644
--- a/hapi-fhir-sql-migrate/pom.xml
+++ b/hapi-fhir-sql-migrate/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-storage-batch2-jobs/pom.xml b/hapi-fhir-storage-batch2-jobs/pom.xml
index 665b79c1640..9ab3b2f2903 100644
--- a/hapi-fhir-storage-batch2-jobs/pom.xml
+++ b/hapi-fhir-storage-batch2-jobs/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-storage-batch2-test-utilities/pom.xml b/hapi-fhir-storage-batch2-test-utilities/pom.xml
index 5c28ea9bcd4..a470df0d3f5 100644
--- a/hapi-fhir-storage-batch2-test-utilities/pom.xml
+++ b/hapi-fhir-storage-batch2-test-utilities/pom.xml
@@ -7,7 +7,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-storage-batch2/pom.xml b/hapi-fhir-storage-batch2/pom.xml
index e502c1a2cd8..7f32f86eae4 100644
--- a/hapi-fhir-storage-batch2/pom.xml
+++ b/hapi-fhir-storage-batch2/pom.xml
@@ -7,7 +7,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-storage-cr/pom.xml b/hapi-fhir-storage-cr/pom.xml
index 592593f7fb6..eb8714f900e 100644
--- a/hapi-fhir-storage-cr/pom.xml
+++ b/hapi-fhir-storage-cr/pom.xml
@@ -7,7 +7,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-storage-mdm/pom.xml b/hapi-fhir-storage-mdm/pom.xml
index e6437a1f45d..ece2c90b097 100644
--- a/hapi-fhir-storage-mdm/pom.xml
+++ b/hapi-fhir-storage-mdm/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-storage-test-utilities/pom.xml b/hapi-fhir-storage-test-utilities/pom.xml
index fb29ef17446..49c62f02771 100644
--- a/hapi-fhir-storage-test-utilities/pom.xml
+++ b/hapi-fhir-storage-test-utilities/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-storage/pom.xml b/hapi-fhir-storage/pom.xml
index e6c24290983..15157a0e06b 100644
--- a/hapi-fhir-storage/pom.xml
+++ b/hapi-fhir-storage/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-structures-dstu2.1/pom.xml b/hapi-fhir-structures-dstu2.1/pom.xml
index 3db931c5d24..cb78fbabc92 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
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-structures-dstu2/pom.xml b/hapi-fhir-structures-dstu2/pom.xml
index b606a900cd7..a082d97a136 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
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-structures-dstu3/pom.xml b/hapi-fhir-structures-dstu3/pom.xml
index 6b64efeb95c..de42c6f8a55 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
- 6.11.2-SNAPSHOT
+ 6.11.3-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 f0e3e2214b8..fccff9966f2 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
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-structures-r4/pom.xml b/hapi-fhir-structures-r4/pom.xml
index 95bf17a3aa3..e9d282a5a0a 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
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-structures-r4b/pom.xml b/hapi-fhir-structures-r4b/pom.xml
index 14a7cbc6011..7db28482218 100644
--- a/hapi-fhir-structures-r4b/pom.xml
+++ b/hapi-fhir-structures-r4b/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-structures-r5/pom.xml b/hapi-fhir-structures-r5/pom.xml
index 80ea9d8936b..2b0d862f79a 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
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-test-utilities/pom.xml b/hapi-fhir-test-utilities/pom.xml
index 41e519eb390..90ea49f97e2 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
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-testpage-overlay/pom.xml b/hapi-fhir-testpage-overlay/pom.xml
index e12e18330c8..1179767e72c 100644
--- a/hapi-fhir-testpage-overlay/pom.xml
+++ b/hapi-fhir-testpage-overlay/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-fhir
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../pom.xml
diff --git a/hapi-fhir-validation-resources-dstu2.1/pom.xml b/hapi-fhir-validation-resources-dstu2.1/pom.xml
index 97ea5b5fb54..8472e3bcd52 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
- 6.11.2-SNAPSHOT
+ 6.11.3-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 83b7ddb4706..59231fef232 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
- 6.11.2-SNAPSHOT
+ 6.11.3-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 c4c98ed5d6f..da2f4a4dcf0 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
- 6.11.2-SNAPSHOT
+ 6.11.3-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 63bf9d4eae2..2755e94c2dd 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
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-validation-resources-r4b/pom.xml b/hapi-fhir-validation-resources-r4b/pom.xml
index 3d544b03e9d..f6f6ca40000 100644
--- a/hapi-fhir-validation-resources-r4b/pom.xml
+++ b/hapi-fhir-validation-resources-r4b/pom.xml
@@ -4,7 +4,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.2-SNAPSHOT
+ 6.11.3-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 ec5a833c234..87408d2e414 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
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml
index 80b4cc6a3a1..2d34f47aca0 100644
--- a/hapi-fhir-validation/pom.xml
+++ b/hapi-fhir-validation/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-tinder-plugin/pom.xml b/hapi-tinder-plugin/pom.xml
index 1b06c741ab1..1f991cf66e9 100644
--- a/hapi-tinder-plugin/pom.xml
+++ b/hapi-tinder-plugin/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-fhir
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../pom.xml
diff --git a/hapi-tinder-test/pom.xml b/hapi-tinder-test/pom.xml
index 339591a3322..9d249173349 100644
--- a/hapi-tinder-test/pom.xml
+++ b/hapi-tinder-test/pom.xml
@@ -4,7 +4,7 @@
ca.uhn.hapi.fhir
hapi-fhir
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../pom.xml
diff --git a/pom.xml b/pom.xml
index cc15c9fe356..a9debe628e1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -9,7 +9,7 @@
ca.uhn.hapi.fhir
hapi-fhir
pom
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
HAPI-FHIR
An open-source implementation of the FHIR specification in Java.
diff --git a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml
index 1af297a0476..91c7def0493 100644
--- a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml
+++ b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml
@@ -7,7 +7,7 @@
ca.uhn.hapi.fhir
hapi-fhir
- 6.11.2-SNAPSHOT
+ 6.11.3-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 9996e300079..b07c2857bf4 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
- 6.11.2-SNAPSHOT
+ 6.11.3-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 249b5358d82..24120ff1236 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
- 6.11.2-SNAPSHOT
+ 6.11.3-SNAPSHOT
../../pom.xml
From fab00ca3a06e39263be7d039fd9e150a1117ef5f Mon Sep 17 00:00:00 2001
From: Michael Buckley
Date: Thu, 30 Nov 2023 16:43:10 -0500
Subject: [PATCH 07/14] Expose the desired Propagation in a tx template (#5516)
---
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-checkstyle/pom.xml | 2 +-
hapi-fhir-cli/hapi-fhir-cli-api/pom.xml | 2 +-
hapi-fhir-cli/hapi-fhir-cli-app/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 | 2 +-
hapi-fhir-jacoco/pom.xml | 2 +-
hapi-fhir-jaxrsserver-base/pom.xml | 2 +-
hapi-fhir-jpa/pom.xml | 2 +-
hapi-fhir-jpaserver-base/pom.xml | 2 +-
.../pom.xml | 2 +-
hapi-fhir-jpaserver-hfql/pom.xml | 2 +-
hapi-fhir-jpaserver-ips/pom.xml | 2 +-
hapi-fhir-jpaserver-mdm/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-dstu2/pom.xml | 2 +-
hapi-fhir-jpaserver-test-dstu3/pom.xml | 2 +-
hapi-fhir-jpaserver-test-r4/pom.xml | 2 +-
hapi-fhir-jpaserver-test-r4b/pom.xml | 2 +-
hapi-fhir-jpaserver-test-r5/pom.xml | 2 +-
hapi-fhir-jpaserver-test-utilities/pom.xml | 2 +-
.../util/JpaHapiTransactionServiceTest.java | 115 ++++++++++++++++++
hapi-fhir-jpaserver-uhnfhirtest/pom.xml | 2 +-
hapi-fhir-server-cds-hooks/pom.xml | 2 +-
hapi-fhir-server-mdm/pom.xml | 2 +-
hapi-fhir-server-openapi/pom.xml | 2 +-
hapi-fhir-server/pom.xml | 2 +-
.../hapi-fhir-caching-api/pom.xml | 2 +-
.../hapi-fhir-caching-caffeine/pom.xml | 4 +-
.../hapi-fhir-caching-guava/pom.xml | 2 +-
.../hapi-fhir-caching-testing/pom.xml | 2 +-
hapi-fhir-serviceloaders/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-sql-migrate/pom.xml | 2 +-
hapi-fhir-storage-batch2-jobs/pom.xml | 2 +-
.../pom.xml | 2 +-
hapi-fhir-storage-batch2/pom.xml | 2 +-
hapi-fhir-storage-cr/pom.xml | 2 +-
hapi-fhir-storage-mdm/pom.xml | 2 +-
hapi-fhir-storage-test-utilities/pom.xml | 2 +-
hapi-fhir-storage/pom.xml | 2 +-
.../jpa/dao/tx/HapiTransactionService.java | 4 +
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-r4b/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-r4b/pom.xml | 2 +-
hapi-fhir-validation-resources-r5/pom.xml | 2 +-
hapi-fhir-validation/pom.xml | 2 +-
hapi-tinder-plugin/pom.xml | 2 +-
hapi-tinder-test/pom.xml | 2 +-
pom.xml | 2 +-
.../pom.xml | 2 +-
.../pom.xml | 2 +-
.../pom.xml | 2 +-
79 files changed, 198 insertions(+), 79 deletions(-)
create mode 100644 hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/util/JpaHapiTransactionServiceTest.java
diff --git a/hapi-deployable-pom/pom.xml b/hapi-deployable-pom/pom.xml
index 25f29f27be9..7828ab2b979 100644
--- a/hapi-deployable-pom/pom.xml
+++ b/hapi-deployable-pom/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-fhir
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../pom.xml
diff --git a/hapi-fhir-android/pom.xml b/hapi-fhir-android/pom.xml
index 6ffe01c5578..c80236908a1 100644
--- a/hapi-fhir-android/pom.xml
+++ b/hapi-fhir-android/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml
index e67b3027b07..ddb95cf5e41 100644
--- a/hapi-fhir-base/pom.xml
+++ b/hapi-fhir-base/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-bom/pom.xml b/hapi-fhir-bom/pom.xml
index 8b175d80acc..80805431db2 100644
--- a/hapi-fhir-bom/pom.xml
+++ b/hapi-fhir-bom/pom.xml
@@ -4,7 +4,7 @@
4.0.0
ca.uhn.hapi.fhir
hapi-fhir-bom
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
pom
HAPI FHIR BOM
@@ -12,7 +12,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-checkstyle/pom.xml b/hapi-fhir-checkstyle/pom.xml
index 9819c07f04a..75efb8b7607 100644
--- a/hapi-fhir-checkstyle/pom.xml
+++ b/hapi-fhir-checkstyle/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-fhir
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../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 cfcaebca7a9..953d36a6f18 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
- 6.11.3-SNAPSHOT
+ 6.11.4-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 0bbc80ea717..dd37903542f 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
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../pom.xml
diff --git a/hapi-fhir-cli/pom.xml b/hapi-fhir-cli/pom.xml
index 86345f32a31..b2fe5bc90b3 100644
--- a/hapi-fhir-cli/pom.xml
+++ b/hapi-fhir-cli/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-fhir
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../pom.xml
diff --git a/hapi-fhir-client-okhttp/pom.xml b/hapi-fhir-client-okhttp/pom.xml
index 0d687f7b1b0..70061eae755 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
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-client/pom.xml b/hapi-fhir-client/pom.xml
index 0f0e49a5f1b..1f8a6b2d2f6 100644
--- a/hapi-fhir-client/pom.xml
+++ b/hapi-fhir-client/pom.xml
@@ -4,7 +4,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-converter/pom.xml b/hapi-fhir-converter/pom.xml
index b92665512e4..bcaa65bb8cb 100644
--- a/hapi-fhir-converter/pom.xml
+++ b/hapi-fhir-converter/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-dist/pom.xml b/hapi-fhir-dist/pom.xml
index 0b3d6c514db..bd14759edc9 100644
--- a/hapi-fhir-dist/pom.xml
+++ b/hapi-fhir-dist/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-fhir
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../pom.xml
diff --git a/hapi-fhir-docs/pom.xml b/hapi-fhir-docs/pom.xml
index 1031d739903..ce9d6c844ce 100644
--- a/hapi-fhir-docs/pom.xml
+++ b/hapi-fhir-docs/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jacoco/pom.xml b/hapi-fhir-jacoco/pom.xml
index 5c60903d052..47c0a8dc304 100644
--- a/hapi-fhir-jacoco/pom.xml
+++ b/hapi-fhir-jacoco/pom.xml
@@ -11,7 +11,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jaxrsserver-base/pom.xml b/hapi-fhir-jaxrsserver-base/pom.xml
index 3b065ee4a6e..e4b20f53161 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
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpa/pom.xml b/hapi-fhir-jpa/pom.xml
index c2c94682ffa..076d8328a69 100644
--- a/hapi-fhir-jpa/pom.xml
+++ b/hapi-fhir-jpa/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-base/pom.xml b/hapi-fhir-jpaserver-base/pom.xml
index 0f2f3d46345..da09b2a7d82 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
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml b/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml
index 8180a9d47de..6fcd53ab9ab 100644
--- a/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml
+++ b/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml
@@ -6,7 +6,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-hfql/pom.xml b/hapi-fhir-jpaserver-hfql/pom.xml
index 05869115705..1d71e2ac1e7 100644
--- a/hapi-fhir-jpaserver-hfql/pom.xml
+++ b/hapi-fhir-jpaserver-hfql/pom.xml
@@ -3,7 +3,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-ips/pom.xml b/hapi-fhir-jpaserver-ips/pom.xml
index d470c942b64..5c28e18021a 100644
--- a/hapi-fhir-jpaserver-ips/pom.xml
+++ b/hapi-fhir-jpaserver-ips/pom.xml
@@ -3,7 +3,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-mdm/pom.xml b/hapi-fhir-jpaserver-mdm/pom.xml
index 60c8a22e5ca..2fb8fa175ad 100644
--- a/hapi-fhir-jpaserver-mdm/pom.xml
+++ b/hapi-fhir-jpaserver-mdm/pom.xml
@@ -6,7 +6,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-model/pom.xml b/hapi-fhir-jpaserver-model/pom.xml
index 32074d95ef6..e161b491e6b 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
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-searchparam/pom.xml b/hapi-fhir-jpaserver-searchparam/pom.xml
index 54a2010e026..051a3069018 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
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-subscription/pom.xml b/hapi-fhir-jpaserver-subscription/pom.xml
index 92e6483c21a..9b119ecea50 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
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-test-dstu2/pom.xml b/hapi-fhir-jpaserver-test-dstu2/pom.xml
index e11d32efadd..177675ad5a5 100644
--- a/hapi-fhir-jpaserver-test-dstu2/pom.xml
+++ b/hapi-fhir-jpaserver-test-dstu2/pom.xml
@@ -6,7 +6,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-test-dstu3/pom.xml b/hapi-fhir-jpaserver-test-dstu3/pom.xml
index 9431d68d8ae..83dde3e9706 100644
--- a/hapi-fhir-jpaserver-test-dstu3/pom.xml
+++ b/hapi-fhir-jpaserver-test-dstu3/pom.xml
@@ -6,7 +6,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-test-r4/pom.xml b/hapi-fhir-jpaserver-test-r4/pom.xml
index afbf5b0acd2..79a1bddbeaa 100644
--- a/hapi-fhir-jpaserver-test-r4/pom.xml
+++ b/hapi-fhir-jpaserver-test-r4/pom.xml
@@ -6,7 +6,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-test-r4b/pom.xml b/hapi-fhir-jpaserver-test-r4b/pom.xml
index b79574ffee6..df49d1e866a 100644
--- a/hapi-fhir-jpaserver-test-r4b/pom.xml
+++ b/hapi-fhir-jpaserver-test-r4b/pom.xml
@@ -6,7 +6,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-test-r5/pom.xml b/hapi-fhir-jpaserver-test-r5/pom.xml
index a7070be80b0..3e7ea400eca 100644
--- a/hapi-fhir-jpaserver-test-r5/pom.xml
+++ b/hapi-fhir-jpaserver-test-r5/pom.xml
@@ -6,7 +6,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.3-SNAPSHOT
+ 6.11.4-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 ae20eb92d4c..a1bf33ca001 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
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/util/JpaHapiTransactionServiceTest.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/util/JpaHapiTransactionServiceTest.java
new file mode 100644
index 00000000000..9f3d588d923
--- /dev/null
+++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/util/JpaHapiTransactionServiceTest.java
@@ -0,0 +1,115 @@
+package ca.uhn.fhir.jpa.util;
+
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
+import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService;
+import ca.uhn.fhir.jpa.test.BaseJpaTest;
+import ca.uhn.fhir.jpa.test.config.TestR4Config;
+import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
+import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
+import org.hl7.fhir.instance.model.api.IIdType;
+import org.hl7.fhir.r4.model.Observation;
+import org.hl7.fhir.r4.model.Patient;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.annotation.Propagation;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+@ExtendWith(SpringExtension.class)
+@ContextConfiguration(classes = {
+ TestR4Config.class
+})
+public class JpaHapiTransactionServiceTest extends BaseJpaTest {
+ @Autowired PlatformTransactionManager myTxManager;
+ @Autowired IFhirResourceDao myPatientDao;
+ @Autowired IFhirResourceDao myObservationDao;
+ SystemRequestDetails myRequestDetails = new SystemRequestDetails();
+ final AtomicReference myObservationId = new AtomicReference<>();
+ final AtomicReference myPatientId = new AtomicReference<>();
+
+ @Override
+ protected FhirContext getFhirContext() {
+ return myFhirContext;
+ }
+
+ @Override
+ protected PlatformTransactionManager getTxManager() {
+ return myTxManager;
+ }
+
+ @Autowired
+ HapiTransactionService myHapiTransactionService;
+
+ @Test
+ void testNewTransactionCommitInsideOldTransactionRollback() {
+
+ try {
+ myHapiTransactionService.withSystemRequest().withPropagation(Propagation.REQUIRED).execute(()->{
+ myObservationId.set(myObservationDao.create(new Observation(), myRequestDetails).getId());
+
+ myHapiTransactionService.withSystemRequest().withPropagation(Propagation.REQUIRES_NEW)
+ .execute(()-> myPatientId.set(myPatientDao.create(new Patient(), myRequestDetails).getId()));
+ // roll back the Observation. The Patient has committed
+ throw new RuntimeException("roll back the Observation.");
+ });
+ } catch (RuntimeException e) {
+ // expected
+ }
+
+ assertNotFound(myObservationDao, myObservationId.get());
+ assertFound(myPatientDao, myPatientId.get());
+ }
+
+
+
+ @Test
+ void testRequiredTransactionCommitInsideExistingTx_rollsBackWithMainTx() {
+ // given
+
+ try {
+ myHapiTransactionService.withSystemRequest().withPropagation(Propagation.REQUIRED).execute(()->{
+ myObservationId.set(myObservationDao.create(new Observation(), myRequestDetails).getId());
+
+ myHapiTransactionService.withSystemRequest().withPropagation(Propagation.REQUIRED).execute(()-> myPatientId.set(myPatientDao.create(new Patient(), myRequestDetails).getId()));
+ throw new RuntimeException("roll back both.");
+ });
+ } catch (RuntimeException e) {
+ // expected
+ }
+
+ assertNotFound(myObservationDao, myObservationId.get());
+ assertNotFound(myPatientDao, myPatientId.get());
+ }
+
+ @Test
+ void testTransactionCommitRespectsRollbackOnly() {
+
+ try {
+ myHapiTransactionService.withSystemRequest().withPropagation(Propagation.REQUIRED).execute((theTransactionStatus)->{
+ myObservationId.set(myObservationDao.create(new Observation(), myRequestDetails).getId());
+ theTransactionStatus.setRollbackOnly();
+ return null;
+ });
+ } catch (RuntimeException e) {
+ // expected
+ }
+
+ assertNotFound(myObservationDao, myObservationId.get());
+ }
+
+ void assertNotFound(IFhirResourceDao> theDao, IIdType id) {
+ assertThrows(ResourceNotFoundException.class, ()-> theDao.read(id, myRequestDetails));
+ }
+
+ void assertFound(IFhirResourceDao> theDao, IIdType theId) {
+ assertNotNull(theDao.read(theId, myRequestDetails));
+ }
+}
diff --git a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml
index 2820eb69e50..539f240c9a6 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
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../pom.xml
diff --git a/hapi-fhir-server-cds-hooks/pom.xml b/hapi-fhir-server-cds-hooks/pom.xml
index 790ea7408be..4ab4abd28ef 100644
--- a/hapi-fhir-server-cds-hooks/pom.xml
+++ b/hapi-fhir-server-cds-hooks/pom.xml
@@ -7,7 +7,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-server-mdm/pom.xml b/hapi-fhir-server-mdm/pom.xml
index c940c9a1095..35f51c77d5b 100644
--- a/hapi-fhir-server-mdm/pom.xml
+++ b/hapi-fhir-server-mdm/pom.xml
@@ -7,7 +7,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-server-openapi/pom.xml b/hapi-fhir-server-openapi/pom.xml
index bb8306309e0..5d58f58def4 100644
--- a/hapi-fhir-server-openapi/pom.xml
+++ b/hapi-fhir-server-openapi/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-server/pom.xml b/hapi-fhir-server/pom.xml
index 14226a4136d..aaa176420d4 100644
--- a/hapi-fhir-server/pom.xml
+++ b/hapi-fhir-server/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml
index bb80f784fd3..2efd21502bb 100644
--- a/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml
+++ b/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml
@@ -7,7 +7,7 @@
hapi-fhir-serviceloaders
ca.uhn.hapi.fhir
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../pom.xml
diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-caffeine/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-caffeine/pom.xml
index 8fd0cc9c89e..3db18cec3bb 100644
--- a/hapi-fhir-serviceloaders/hapi-fhir-caching-caffeine/pom.xml
+++ b/hapi-fhir-serviceloaders/hapi-fhir-caching-caffeine/pom.xml
@@ -7,7 +7,7 @@
hapi-fhir-serviceloaders
ca.uhn.hapi.fhir
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../pom.xml
@@ -21,7 +21,7 @@
ca.uhn.hapi.fhir
hapi-fhir-caching-api
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml
index a0a001b2159..341ac8b5cfd 100644
--- a/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml
+++ b/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml
@@ -7,7 +7,7 @@
hapi-fhir-serviceloaders
ca.uhn.hapi.fhir
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../pom.xml
diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-testing/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-testing/pom.xml
index 0f56910a478..ef1e667f1e8 100644
--- a/hapi-fhir-serviceloaders/hapi-fhir-caching-testing/pom.xml
+++ b/hapi-fhir-serviceloaders/hapi-fhir-caching-testing/pom.xml
@@ -7,7 +7,7 @@
hapi-fhir
ca.uhn.hapi.fhir
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../../pom.xml
diff --git a/hapi-fhir-serviceloaders/pom.xml b/hapi-fhir-serviceloaders/pom.xml
index 3e84d9f6b65..b2ffa705596 100644
--- a/hapi-fhir-serviceloaders/pom.xml
+++ b/hapi-fhir-serviceloaders/pom.xml
@@ -5,7 +5,7 @@
hapi-deployable-pom
ca.uhn.hapi.fhir
- 6.11.3-SNAPSHOT
+ 6.11.4-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 bb234e607ea..627cefbce72 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
- 6.11.3-SNAPSHOT
+ 6.11.4-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 a576a9a2a4e..1c49210557f 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
- 6.11.3-SNAPSHOT
+ 6.11.4-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 58ce092f00d..e7e3ddb3c43 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
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
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 0804a3fe706..90d0122dc8c 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
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
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 719aa8d1726..ee7267fda57 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
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
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 527947da607..9c442072b80 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
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-spring-boot/pom.xml b/hapi-fhir-spring-boot/pom.xml
index 3fa3a852d1c..4e9363295a7 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
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../pom.xml
diff --git a/hapi-fhir-sql-migrate/pom.xml b/hapi-fhir-sql-migrate/pom.xml
index 809ed62b838..53ddb291362 100644
--- a/hapi-fhir-sql-migrate/pom.xml
+++ b/hapi-fhir-sql-migrate/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-storage-batch2-jobs/pom.xml b/hapi-fhir-storage-batch2-jobs/pom.xml
index 9ab3b2f2903..87c80e9aea2 100644
--- a/hapi-fhir-storage-batch2-jobs/pom.xml
+++ b/hapi-fhir-storage-batch2-jobs/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-storage-batch2-test-utilities/pom.xml b/hapi-fhir-storage-batch2-test-utilities/pom.xml
index a470df0d3f5..ab92cdf2bff 100644
--- a/hapi-fhir-storage-batch2-test-utilities/pom.xml
+++ b/hapi-fhir-storage-batch2-test-utilities/pom.xml
@@ -7,7 +7,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-storage-batch2/pom.xml b/hapi-fhir-storage-batch2/pom.xml
index 7f32f86eae4..5329617d4fc 100644
--- a/hapi-fhir-storage-batch2/pom.xml
+++ b/hapi-fhir-storage-batch2/pom.xml
@@ -7,7 +7,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-storage-cr/pom.xml b/hapi-fhir-storage-cr/pom.xml
index eb8714f900e..aaa18119b78 100644
--- a/hapi-fhir-storage-cr/pom.xml
+++ b/hapi-fhir-storage-cr/pom.xml
@@ -7,7 +7,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-storage-mdm/pom.xml b/hapi-fhir-storage-mdm/pom.xml
index ece2c90b097..2af9a4e6dbc 100644
--- a/hapi-fhir-storage-mdm/pom.xml
+++ b/hapi-fhir-storage-mdm/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-storage-test-utilities/pom.xml b/hapi-fhir-storage-test-utilities/pom.xml
index 49c62f02771..9a46e263b1e 100644
--- a/hapi-fhir-storage-test-utilities/pom.xml
+++ b/hapi-fhir-storage-test-utilities/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-storage/pom.xml b/hapi-fhir-storage/pom.xml
index 15157a0e06b..d5a2887d24f 100644
--- a/hapi-fhir-storage/pom.xml
+++ b/hapi-fhir-storage/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/HapiTransactionService.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/HapiTransactionService.java
index 58a7ee5c679..348dce762be 100644
--- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/HapiTransactionService.java
+++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/HapiTransactionService.java
@@ -489,6 +489,10 @@ public class HapiTransactionService implements IHapiTransactionService {
public RequestDetails getRequestDetailsForTesting() {
return myRequestDetails;
}
+
+ public Propagation getPropagation() {
+ return myPropagation;
+ }
}
/**
diff --git a/hapi-fhir-structures-dstu2.1/pom.xml b/hapi-fhir-structures-dstu2.1/pom.xml
index cb78fbabc92..58a42fc146f 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
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-structures-dstu2/pom.xml b/hapi-fhir-structures-dstu2/pom.xml
index a082d97a136..94b617d3ad7 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
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-structures-dstu3/pom.xml b/hapi-fhir-structures-dstu3/pom.xml
index de42c6f8a55..380194ed555 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
- 6.11.3-SNAPSHOT
+ 6.11.4-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 fccff9966f2..4f4e5b841be 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
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-structures-r4/pom.xml b/hapi-fhir-structures-r4/pom.xml
index e9d282a5a0a..b537170eff6 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
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-structures-r4b/pom.xml b/hapi-fhir-structures-r4b/pom.xml
index 7db28482218..41b8084fbc6 100644
--- a/hapi-fhir-structures-r4b/pom.xml
+++ b/hapi-fhir-structures-r4b/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-structures-r5/pom.xml b/hapi-fhir-structures-r5/pom.xml
index 2b0d862f79a..45390f8b466 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
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-test-utilities/pom.xml b/hapi-fhir-test-utilities/pom.xml
index 90ea49f97e2..6da65fbc247 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
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-testpage-overlay/pom.xml b/hapi-fhir-testpage-overlay/pom.xml
index 1179767e72c..e4e80515b04 100644
--- a/hapi-fhir-testpage-overlay/pom.xml
+++ b/hapi-fhir-testpage-overlay/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-fhir
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../pom.xml
diff --git a/hapi-fhir-validation-resources-dstu2.1/pom.xml b/hapi-fhir-validation-resources-dstu2.1/pom.xml
index 8472e3bcd52..efe3441f8b6 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
- 6.11.3-SNAPSHOT
+ 6.11.4-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 59231fef232..d93073774c7 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
- 6.11.3-SNAPSHOT
+ 6.11.4-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 da2f4a4dcf0..1edb8b6baff 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
- 6.11.3-SNAPSHOT
+ 6.11.4-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 2755e94c2dd..3bfc892fb39 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
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-validation-resources-r4b/pom.xml b/hapi-fhir-validation-resources-r4b/pom.xml
index f6f6ca40000..cb8e68e7ae2 100644
--- a/hapi-fhir-validation-resources-r4b/pom.xml
+++ b/hapi-fhir-validation-resources-r4b/pom.xml
@@ -4,7 +4,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.3-SNAPSHOT
+ 6.11.4-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 87408d2e414..9598eebf464 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
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml
index 2d34f47aca0..6713e8e8969 100644
--- a/hapi-fhir-validation/pom.xml
+++ b/hapi-fhir-validation/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-tinder-plugin/pom.xml b/hapi-tinder-plugin/pom.xml
index 1f991cf66e9..bcad4078b61 100644
--- a/hapi-tinder-plugin/pom.xml
+++ b/hapi-tinder-plugin/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-fhir
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../pom.xml
diff --git a/hapi-tinder-test/pom.xml b/hapi-tinder-test/pom.xml
index 9d249173349..abe72e3b4d9 100644
--- a/hapi-tinder-test/pom.xml
+++ b/hapi-tinder-test/pom.xml
@@ -4,7 +4,7 @@
ca.uhn.hapi.fhir
hapi-fhir
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../pom.xml
diff --git a/pom.xml b/pom.xml
index a9debe628e1..c0d8d46983e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -9,7 +9,7 @@
ca.uhn.hapi.fhir
hapi-fhir
pom
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
HAPI-FHIR
An open-source implementation of the FHIR specification in Java.
diff --git a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml
index 91c7def0493..e3d6f1f5c15 100644
--- a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml
+++ b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml
@@ -7,7 +7,7 @@
ca.uhn.hapi.fhir
hapi-fhir
- 6.11.3-SNAPSHOT
+ 6.11.4-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 b07c2857bf4..88a64d42780 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
- 6.11.3-SNAPSHOT
+ 6.11.4-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 24120ff1236..4ad712cf23d 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
- 6.11.3-SNAPSHOT
+ 6.11.4-SNAPSHOT
../../pom.xml
From ee4ecacbdb9a96e63ddd3f75afeaea44cf781582 Mon Sep 17 00:00:00 2001
From: Luke deGruchy
Date: Thu, 30 Nov 2023 18:10:05 -0500
Subject: [PATCH 08/14] Revert changes for 5502.
---
.../java/ca/uhn/fhir/interceptor/api/Pointcut.java | 7 +------
.../java/ca/uhn/fhir/rest/client/impl/BaseClient.java | 11 -----------
...ient-client-response-pointcut-mutate-response.yaml | 6 ------
3 files changed, 1 insertion(+), 23 deletions(-)
delete mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java
index 64b7267f63f..994ea9cd1cf 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java
@@ -93,10 +93,6 @@ public enum Pointcut implements IPointcut {
*
* ca.uhn.fhir.rest.client.api.IRestfulClient - The client object making the request
*
- *
- * ca.uhn.fhir.rest.client.api.ClientResponseContext - Contains an IHttpRequest, an IHttpResponse, and an IRestfulClient
- * and also allows the client to mutate the contained IHttpResponse
- *
*
*
* Hook methods must return void
.
@@ -105,8 +101,7 @@ public enum Pointcut implements IPointcut {
void.class,
"ca.uhn.fhir.rest.client.api.IHttpRequest",
"ca.uhn.fhir.rest.client.api.IHttpResponse",
- "ca.uhn.fhir.rest.client.api.IRestfulClient",
- "ca.uhn.fhir.rest.client.api.ClientResponseContext"),
+ "ca.uhn.fhir.rest.client.api.IRestfulClient"),
/**
* Server Hook:
diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java
index 5aa920c920e..4d05511d724 100644
--- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java
+++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java
@@ -353,24 +353,13 @@ public abstract class BaseClient implements IRestfulClient {
response = httpRequest.execute();
- final Class extends IBaseResource> returnType = (binding instanceof ResourceResponseHandler)
- ? ((ResourceResponseHandler extends IBaseResource>) binding).getReturnType()
- : null;
-
- final ClientResponseContext clientResponseContext =
- new ClientResponseContext(httpRequest, response, this, getFhirContext(), returnType);
HookParams responseParams = new HookParams();
responseParams.add(IHttpRequest.class, httpRequest);
responseParams.add(IHttpResponse.class, response);
responseParams.add(IRestfulClient.class, this);
- responseParams.add(ClientResponseContext.class, clientResponseContext);
getInterceptorService().callHooks(Pointcut.CLIENT_RESPONSE, responseParams);
- // Replace the contents of the response with whatever the hook returned, or the same response as before if
- // it no-op'd
- response = clientResponseContext.getHttpResponse();
-
String mimeType;
if (Constants.STATUS_HTTP_204_NO_CONTENT == response.getStatus()) {
mimeType = null;
diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml
deleted file mode 100644
index 31114533369..00000000000
--- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-type: add
-issue: 5502
-jira: SMILE-7262
-title: "It is now possible to mutate an HTTP response from the CLIENT_RESPONSE Pointcut, and pass this mutated response
- to downstream processing."
From 8237c0795d74b43d6676b92041188cec04a5fcda Mon Sep 17 00:00:00 2001
From: Luke deGruchy
Date: Thu, 30 Nov 2023 18:23:13 -0500
Subject: [PATCH 09/14] test change
---
.../src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java | 1 -
1 file changed, 1 deletion(-)
diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java
index 4d05511d724..f9bcbd48a00 100644
--- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java
+++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java
@@ -36,7 +36,6 @@ import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.api.RequestFormatParamStyleEnum;
import ca.uhn.fhir.rest.api.SummaryEnum;
-import ca.uhn.fhir.rest.client.api.ClientResponseContext;
import ca.uhn.fhir.rest.client.api.IHttpClient;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.api.IHttpResponse;
From 0c8258f93ce55dc4febc6f4cdc6dcd9f1b0b179b Mon Sep 17 00:00:00 2001
From: Luke deGruchy
Date: Thu, 30 Nov 2023 18:54:49 -0500
Subject: [PATCH 10/14] Revert the revert
---
.../java/ca/uhn/fhir/interceptor/api/Pointcut.java | 7 ++++++-
.../ca/uhn/fhir/rest/client/impl/BaseClient.java | 12 ++++++++++++
...ent-client-response-pointcut-mutate-response.yaml | 6 ++++++
3 files changed, 24 insertions(+), 1 deletion(-)
create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java
index 994ea9cd1cf..64b7267f63f 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java
@@ -93,6 +93,10 @@ public enum Pointcut implements IPointcut {
*
* ca.uhn.fhir.rest.client.api.IRestfulClient - The client object making the request
*
+ *
+ * ca.uhn.fhir.rest.client.api.ClientResponseContext - Contains an IHttpRequest, an IHttpResponse, and an IRestfulClient
+ * and also allows the client to mutate the contained IHttpResponse
+ *
*
*
* Hook methods must return void
.
@@ -101,7 +105,8 @@ public enum Pointcut implements IPointcut {
void.class,
"ca.uhn.fhir.rest.client.api.IHttpRequest",
"ca.uhn.fhir.rest.client.api.IHttpResponse",
- "ca.uhn.fhir.rest.client.api.IRestfulClient"),
+ "ca.uhn.fhir.rest.client.api.IRestfulClient",
+ "ca.uhn.fhir.rest.client.api.ClientResponseContext"),
/**
* Server Hook:
diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java
index f9bcbd48a00..5aa920c920e 100644
--- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java
+++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java
@@ -36,6 +36,7 @@ import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.api.RequestFormatParamStyleEnum;
import ca.uhn.fhir.rest.api.SummaryEnum;
+import ca.uhn.fhir.rest.client.api.ClientResponseContext;
import ca.uhn.fhir.rest.client.api.IHttpClient;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.api.IHttpResponse;
@@ -352,13 +353,24 @@ public abstract class BaseClient implements IRestfulClient {
response = httpRequest.execute();
+ final Class extends IBaseResource> returnType = (binding instanceof ResourceResponseHandler)
+ ? ((ResourceResponseHandler extends IBaseResource>) binding).getReturnType()
+ : null;
+
+ final ClientResponseContext clientResponseContext =
+ new ClientResponseContext(httpRequest, response, this, getFhirContext(), returnType);
HookParams responseParams = new HookParams();
responseParams.add(IHttpRequest.class, httpRequest);
responseParams.add(IHttpResponse.class, response);
responseParams.add(IRestfulClient.class, this);
+ responseParams.add(ClientResponseContext.class, clientResponseContext);
getInterceptorService().callHooks(Pointcut.CLIENT_RESPONSE, responseParams);
+ // Replace the contents of the response with whatever the hook returned, or the same response as before if
+ // it no-op'd
+ response = clientResponseContext.getHttpResponse();
+
String mimeType;
if (Constants.STATUS_HTTP_204_NO_CONTENT == response.getStatus()) {
mimeType = null;
diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml
new file mode 100644
index 00000000000..31114533369
--- /dev/null
+++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml
@@ -0,0 +1,6 @@
+---
+type: add
+issue: 5502
+jira: SMILE-7262
+title: "It is now possible to mutate an HTTP response from the CLIENT_RESPONSE Pointcut, and pass this mutated response
+ to downstream processing."
From 59936ee70ca13dc5c393e2d42c3cbb6a9bb52005 Mon Sep 17 00:00:00 2001
From: Luke deGruchy
Date: Fri, 1 Dec 2023 04:02:54 -0500
Subject: [PATCH 11/14] Revert 5502. (#5521)
---
.../main/java/ca/uhn/fhir/interceptor/api/Pointcut.java | 3 +--
.../java/ca/uhn/fhir/rest/client/impl/BaseClient.java | 8 --------
...e-client-client-response-pointcut-mutate-response.yaml | 6 ------
3 files changed, 1 insertion(+), 16 deletions(-)
delete mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java
index 64b7267f63f..388f65b4c33 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java
@@ -105,8 +105,7 @@ public enum Pointcut implements IPointcut {
void.class,
"ca.uhn.fhir.rest.client.api.IHttpRequest",
"ca.uhn.fhir.rest.client.api.IHttpResponse",
- "ca.uhn.fhir.rest.client.api.IRestfulClient",
- "ca.uhn.fhir.rest.client.api.ClientResponseContext"),
+ "ca.uhn.fhir.rest.client.api.IRestfulClient"),
/**
* Server Hook:
diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java
index 5aa920c920e..8acd05255c2 100644
--- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java
+++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java
@@ -36,7 +36,6 @@ import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.api.RequestFormatParamStyleEnum;
import ca.uhn.fhir.rest.api.SummaryEnum;
-import ca.uhn.fhir.rest.client.api.ClientResponseContext;
import ca.uhn.fhir.rest.client.api.IHttpClient;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.api.IHttpResponse;
@@ -357,20 +356,13 @@ public abstract class BaseClient implements IRestfulClient {
? ((ResourceResponseHandler extends IBaseResource>) binding).getReturnType()
: null;
- final ClientResponseContext clientResponseContext =
- new ClientResponseContext(httpRequest, response, this, getFhirContext(), returnType);
HookParams responseParams = new HookParams();
responseParams.add(IHttpRequest.class, httpRequest);
responseParams.add(IHttpResponse.class, response);
responseParams.add(IRestfulClient.class, this);
- responseParams.add(ClientResponseContext.class, clientResponseContext);
getInterceptorService().callHooks(Pointcut.CLIENT_RESPONSE, responseParams);
- // Replace the contents of the response with whatever the hook returned, or the same response as before if
- // it no-op'd
- response = clientResponseContext.getHttpResponse();
-
String mimeType;
if (Constants.STATUS_HTTP_204_NO_CONTENT == response.getStatus()) {
mimeType = null;
diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml
deleted file mode 100644
index 31114533369..00000000000
--- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-type: add
-issue: 5502
-jira: SMILE-7262
-title: "It is now possible to mutate an HTTP response from the CLIENT_RESPONSE Pointcut, and pass this mutated response
- to downstream processing."
From ec5402abd4222b2cc486fdf55d70b712e2481c52 Mon Sep 17 00:00:00 2001
From: Luke deGruchy
Date: Fri, 1 Dec 2023 10:16:56 -0500
Subject: [PATCH 12/14] Restore changes for 5502. (#5525)
* Restore changes for 5502.
* Spotless.
---
.../main/java/ca/uhn/fhir/interceptor/api/Pointcut.java | 3 ++-
.../java/ca/uhn/fhir/rest/client/impl/BaseClient.java | 8 ++++++++
...e-client-client-response-pointcut-mutate-response.yaml | 6 ++++++
3 files changed, 16 insertions(+), 1 deletion(-)
create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java
index 388f65b4c33..64b7267f63f 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java
@@ -105,7 +105,8 @@ public enum Pointcut implements IPointcut {
void.class,
"ca.uhn.fhir.rest.client.api.IHttpRequest",
"ca.uhn.fhir.rest.client.api.IHttpResponse",
- "ca.uhn.fhir.rest.client.api.IRestfulClient"),
+ "ca.uhn.fhir.rest.client.api.IRestfulClient",
+ "ca.uhn.fhir.rest.client.api.ClientResponseContext"),
/**
* Server Hook:
diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java
index 8acd05255c2..5aa920c920e 100644
--- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java
+++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java
@@ -36,6 +36,7 @@ import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.api.RequestFormatParamStyleEnum;
import ca.uhn.fhir.rest.api.SummaryEnum;
+import ca.uhn.fhir.rest.client.api.ClientResponseContext;
import ca.uhn.fhir.rest.client.api.IHttpClient;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.api.IHttpResponse;
@@ -356,13 +357,20 @@ public abstract class BaseClient implements IRestfulClient {
? ((ResourceResponseHandler extends IBaseResource>) binding).getReturnType()
: null;
+ final ClientResponseContext clientResponseContext =
+ new ClientResponseContext(httpRequest, response, this, getFhirContext(), returnType);
HookParams responseParams = new HookParams();
responseParams.add(IHttpRequest.class, httpRequest);
responseParams.add(IHttpResponse.class, response);
responseParams.add(IRestfulClient.class, this);
+ responseParams.add(ClientResponseContext.class, clientResponseContext);
getInterceptorService().callHooks(Pointcut.CLIENT_RESPONSE, responseParams);
+ // Replace the contents of the response with whatever the hook returned, or the same response as before if
+ // it no-op'd
+ response = clientResponseContext.getHttpResponse();
+
String mimeType;
if (Constants.STATUS_HTTP_204_NO_CONTENT == response.getStatus()) {
mimeType = null;
diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml
new file mode 100644
index 00000000000..31114533369
--- /dev/null
+++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml
@@ -0,0 +1,6 @@
+---
+type: add
+issue: 5502
+jira: SMILE-7262
+title: "It is now possible to mutate an HTTP response from the CLIENT_RESPONSE Pointcut, and pass this mutated response
+ to downstream processing."
From d597cf2763f55c55f21d9dab857f67619d5aca05 Mon Sep 17 00:00:00 2001
From: James Agnew
Date: Fri, 1 Dec 2023 15:19:51 -0500
Subject: [PATCH 13/14] Allow chained searches in Bundles where the fullUrl is
fully qualified (#5529)
* Allow chained searches in Bundles where the fullUrl is fully qualified
* Add changelog
* Spotless
---
...-in-bundle-where-fullurl-is-qualified.yaml | 4 ++
.../extractor/BaseSearchParamExtractor.java | 19 +++++-
.../r4/FhirResourceDaoR4SearchNoFtTest.java | 65 ++++++++++++-------
3 files changed, 62 insertions(+), 26 deletions(-)
create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5529-allow-chained-search-in-bundle-where-fullurl-is-qualified.yaml
diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5529-allow-chained-search-in-bundle-where-fullurl-is-qualified.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5529-allow-chained-search-in-bundle-where-fullurl-is-qualified.yaml
new file mode 100644
index 00000000000..2fdbb4cb53d
--- /dev/null
+++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5529-allow-chained-search-in-bundle-where-fullurl-is-qualified.yaml
@@ -0,0 +1,4 @@
+---
+type: fix
+issue: 5529
+title: "When using a chained SearchParameter to search within a Bundle as [described here](https://smilecdr.com/docs/fhir_storage_relational/chained_searches_and_sorts.html#document-and-message-search-parameters), if the `Bundle.entry.fullUrl` was fully qualified but the reference was not, the search did not work. This has been corrected."
diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractor.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractor.java
index a4a4425071c..8ed714eb385 100644
--- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractor.java
+++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractor.java
@@ -63,6 +63,7 @@ import org.hl7.fhir.instance.model.api.IBaseReference;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
+import org.hl7.fhir.r4.model.IdType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
@@ -2010,17 +2011,31 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
* references within a Bundle
*/
if (theAppContext instanceof IBaseBundle && isNotBlank(theUrl) && !theUrl.startsWith("#")) {
+ String unqualifiedVersionlessReference;
+ boolean isPlaceholderReference;
+ if (theUrl.startsWith("urn:")) {
+ isPlaceholderReference = true;
+ unqualifiedVersionlessReference = null;
+ } else {
+ isPlaceholderReference = false;
+ unqualifiedVersionlessReference =
+ new IdType(theUrl).toUnqualifiedVersionless().getValue();
+ }
+
List entries = BundleUtil.toListOfEntries(getContext(), (IBaseBundle) theAppContext);
for (BundleEntryParts next : entries) {
if (next.getResource() != null) {
- if (theUrl.startsWith("urn:uuid:")) {
+ if (isPlaceholderReference) {
if (theUrl.equals(next.getUrl())
|| theUrl.equals(
next.getResource().getIdElement().getValue())) {
return (T) next.getResource();
}
} else {
- if (theUrl.equals(next.getResource().getIdElement().getValue())) {
+ if (unqualifiedVersionlessReference.equals(next.getResource()
+ .getIdElement()
+ .toUnqualifiedVersionless()
+ .getValue())) {
return (T) next.getResource();
}
}
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java
index 779cd067177..826f6ffad95 100644
--- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java
+++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java
@@ -5793,8 +5793,15 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
* [base]/Bundle?composition.patient.identifier=foo
*/
@ParameterizedTest
- @CsvSource({"urn:uuid:5c34dc2c-9b5d-4ec1-b30b-3e2d4371508b", "Patient/ABC"})
- public void testCreateAndSearchForFullyChainedSearchParameter(String thePatientId) {
+ @CsvSource({
+ "true , urn:uuid:5c34dc2c-9b5d-4ec1-b30b-3e2d4371508b , urn:uuid:5c34dc2c-9b5d-4ec1-b30b-3e2d4371508b",
+ "false, urn:uuid:5c34dc2c-9b5d-4ec1-b30b-3e2d4371508b , urn:uuid:5c34dc2c-9b5d-4ec1-b30b-3e2d4371508b",
+ "true , Patient/ABC , Patient/ABC ",
+ "false, Patient/ABC , Patient/ABC ",
+ "true , Patient/ABC , http://example.com/fhir/Patient/ABC ",
+ "false, Patient/ABC , http://example.com/fhir/Patient/ABC ",
+ })
+ public void testCreateAndSearchForFullyChainedSearchParameter(boolean theUseFullChainInName, String thePatientId, String theFullUrl) {
// Setup 1
myStorageSettings.setIndexMissingFields(JpaStorageSettings.IndexEnabledEnum.DISABLED);
@@ -5819,13 +5826,18 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
composition.setSubject(new Reference(thePatientId));
Patient patient = new Patient();
- patient.setId(new IdType(thePatientId));
+ patient.setId(new IdType(theFullUrl));
patient.addIdentifier().setSystem("http://foo").setValue("bar");
Bundle bundle = new Bundle();
bundle.setType(Bundle.BundleType.DOCUMENT);
- bundle.addEntry().setResource(composition);
- bundle.addEntry().setResource(patient);
+ bundle
+ .addEntry()
+ .setResource(composition);
+ bundle
+ .addEntry()
+ .setFullUrl(theFullUrl)
+ .setResource(patient);
myBundleDao.create(bundle, mySrd);
@@ -5833,35 +5845,40 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
bundle2.setType(Bundle.BundleType.DOCUMENT);
myBundleDao.create(bundle2, mySrd);
- // Verify 1
- runInTransaction(() -> {
+ // Test
+
+ SearchParameterMap map;
+ if (theUseFullChainInName) {
+ map = SearchParameterMap.newSynchronous("composition.patient.identifier", new TokenParam("http://foo", "bar"));
+ } else {
+ map = SearchParameterMap.newSynchronous("composition", new ReferenceParam("patient.identifier", "http://foo|bar"));
+ }
+ IBundleProvider outcome = myBundleDao.search(map, mySrd);
+
+ // Verify
+
+ List params = extractAllTokenIndexes();
+ assertThat(params.toString(), params, containsInAnyOrder(
+ "composition.patient.identifier http://foo|bar"
+ ));
+ assertEquals(1, outcome.size());
+ }
+
+ private List extractAllTokenIndexes() {
+ List params = runInTransaction(() -> {
logAllTokenIndexes();
- List params = myResourceIndexedSearchParamTokenDao
+ return myResourceIndexedSearchParamTokenDao
.findAll()
.stream()
.filter(t -> t.getParamName().contains("."))
.map(t -> t.getParamName() + " " + t.getSystem() + "|" + t.getValue())
.toList();
- assertThat(params.toString(), params, containsInAnyOrder(
- "composition.patient.identifier http://foo|bar"
- ));
});
-
- // Test 2
- IBundleProvider outcome;
-
- SearchParameterMap map = SearchParameterMap
- .newSynchronous("composition.patient.identifier", new TokenParam("http://foo", "bar"));
- outcome = myBundleDao.search(map, mySrd);
- assertEquals(1, outcome.size());
-
- map = SearchParameterMap
- .newSynchronous("composition", new ReferenceParam("patient.identifier", "http://foo|bar"));
- outcome = myBundleDao.search(map, mySrd);
- assertEquals(1, outcome.size());
+ return params;
}
+
@Nested
public class TagBelowTests {
From 113cf4002d91408f13741d7c69ef2bf25e66a76d Mon Sep 17 00:00:00 2001
From: Luke deGruchy
Date: Fri, 1 Dec 2023 17:00:05 -0500
Subject: [PATCH 14/14] First commit with very rough partial solution and test.
---
.../ca/uhn/fhir/jpa/migrate/taskdef/BaseTableColumnTask.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTableColumnTask.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTableColumnTask.java
index c7616bde567..702430aa4b6 100644
--- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTableColumnTask.java
+++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTableColumnTask.java
@@ -44,7 +44,7 @@ public abstract class BaseTableColumnTask extends BaseTableTask {
}
public BaseTableColumnTask setColumnName(String theColumnName) {
- myColumnName = theColumnName.toUpperCase();
+ myColumnName = theColumnName.toLowerCase();
return this;
}