diff --git a/hapi-deployable-pom/pom.xml b/hapi-deployable-pom/pom.xml index b6ca37efe20..30e1ccb5d6d 100644 --- a/hapi-deployable-pom/pom.xml +++ b/hapi-deployable-pom/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-android/pom.xml b/hapi-fhir-android/pom.xml index 42ccb3686e8..9060665d1b3 100644 --- a/hapi-fhir-android/pom.xml +++ b/hapi-fhir-android/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml index a1bd95cc46e..fcc60e05e2b 100644 --- a/hapi-fhir-base/pom.xml +++ b/hapi-fhir-base/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-bom/pom.xml b/hapi-fhir-bom/pom.xml index 4b061a19654..3a00980cded 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 - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT pom HAPI FHIR BOM @@ -12,7 +12,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-checkstyle/pom.xml b/hapi-fhir-checkstyle/pom.xml index b096fc097ae..2a21a1796fe 100644 --- a/hapi-fhir-checkstyle/pom.xml +++ b/hapi-fhir-checkstyle/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.3.6-SNAPSHOT + 7.3.7-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 05b014f3e96..3555f67f63c 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 - 7.3.6-SNAPSHOT + 7.3.7-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 c3018a1107f..b6c73678960 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 - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-cli/pom.xml b/hapi-fhir-cli/pom.xml index fc9943437a8..6745d62518b 100644 --- a/hapi-fhir-cli/pom.xml +++ b/hapi-fhir-cli/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-client-okhttp/pom.xml b/hapi-fhir-client-okhttp/pom.xml index 2f8c28eacbe..9a7ac810865 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 - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-client/pom.xml b/hapi-fhir-client/pom.xml index 7591bfec07b..de0d7f14afe 100644 --- a/hapi-fhir-client/pom.xml +++ b/hapi-fhir-client/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-converter/pom.xml b/hapi-fhir-converter/pom.xml index 18864821cbe..9883e898092 100644 --- a/hapi-fhir-converter/pom.xml +++ b/hapi-fhir-converter/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-dist/pom.xml b/hapi-fhir-dist/pom.xml index 4f097a72d29..9e001be27ab 100644 --- a/hapi-fhir-dist/pom.xml +++ b/hapi-fhir-dist/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-docs/pom.xml b/hapi-fhir-docs/pom.xml index dfed085f6d7..ba6c6912dbc 100644 --- a/hapi-fhir-docs/pom.xml +++ b/hapi-fhir-docs/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_4_0/6003-improve-versioned-canonical-ref-handling.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_4_0/6003-improve-versioned-canonical-ref-handling.yaml new file mode 100644 index 00000000000..2af4bed1ea9 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_4_0/6003-improve-versioned-canonical-ref-handling.yaml @@ -0,0 +1,12 @@ +--- +type: add +issue: 6003 +title: "When performing a search for a resource which uses versioned canonical + references to another resource (e.g. a QuestionnaireResponse with a + questionnaire reference of `http://example.org/my-questionnaire|1.0`), + the server failed to process `_include` directives which should bring in + these references. Includes will now fetch all versions of a canonical + reference, which is still not perfect but is an improvement over the + current behaviour. A future update may add more targeted behaviour to + address including versioned reference targets, but this will require a + significant re-architecture of the loading module." diff --git a/hapi-fhir-jacoco/pom.xml b/hapi-fhir-jacoco/pom.xml index ba974845636..7d35c5c03fa 100644 --- a/hapi-fhir-jacoco/pom.xml +++ b/hapi-fhir-jacoco/pom.xml @@ -11,7 +11,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jaxrsserver-base/pom.xml b/hapi-fhir-jaxrsserver-base/pom.xml index 68156dd9cd4..aa02e821f97 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 - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpa/pom.xml b/hapi-fhir-jpa/pom.xml index 0a7b2a73cc7..145cdb4224f 100644 --- a/hapi-fhir-jpa/pom.xml +++ b/hapi-fhir-jpa/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-base/pom.xml b/hapi-fhir-jpaserver-base/pom.xml index a04314b0a66..d628e03ee5c 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 - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/custom/IResourceTableDaoImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/custom/IResourceTableDaoImpl.java index 5532a4771d0..d05aac4b708 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/custom/IResourceTableDaoImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/custom/IResourceTableDaoImpl.java @@ -42,6 +42,7 @@ public class IResourceTableDaoImpl implements IForcedIdQueries { * is an object array, where the order matters (the array represents columns returned by the query). * Deleted resources are not filtered. */ + @Override public Collection findAndResolveByForcedIdWithNoTypeIncludeDeleted( String theResourceType, Collection theForcedIds) { return findAndResolveByForcedIdWithNoType(theResourceType, theForcedIds, false); @@ -52,6 +53,7 @@ public class IResourceTableDaoImpl implements IForcedIdQueries { * is an object array, where the order matters (the array represents columns returned by the query). * Deleted resources are optionally filtered. Be careful if you change this query in any way. */ + @Override public Collection findAndResolveByForcedIdWithNoType( String theResourceType, Collection theForcedIds, boolean theExcludeDeleted) { String query = "SELECT t.myResourceType, t.myId, t.myFhirId, t.myDeleted " @@ -74,6 +76,7 @@ public class IResourceTableDaoImpl implements IForcedIdQueries { * is an object array, where the order matters (the array represents columns returned by the query). * Deleted resources are optionally filtered. Be careful if you change this query in any way. */ + @Override public Collection findAndResolveByForcedIdWithNoTypeInPartition( String theResourceType, Collection theForcedIds, @@ -100,6 +103,7 @@ public class IResourceTableDaoImpl implements IForcedIdQueries { * is an object array, where the order matters (the array represents columns returned by the query). * Deleted resources are optionally filtered. Be careful if you change this query in any way. */ + @Override public Collection findAndResolveByForcedIdWithNoTypeInPartitionNull( String theResourceType, Collection theForcedIds, boolean theExcludeDeleted) { String query = "SELECT t.myResourceType, t.myId, t.myFhirId, t.myDeleted " @@ -122,6 +126,7 @@ public class IResourceTableDaoImpl implements IForcedIdQueries { * is an object array, where the order matters (the array represents columns returned by the query). * Deleted resources are optionally filtered. Be careful if you change this query in any way. */ + @Override public Collection findAndResolveByForcedIdWithNoTypeInPartitionIdOrNullPartitionId( String theResourceType, Collection theForcedIds, diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java index 064e34b5942..076c6217660 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java @@ -19,6 +19,8 @@ */ package ca.uhn.fhir.jpa.search.builder; +import ca.uhn.fhir.context.BaseRuntimeChildDefinition; +import ca.uhn.fhir.context.BaseRuntimeElementDefinition; import ca.uhn.fhir.context.ComboSearchParamType; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; @@ -163,7 +165,7 @@ public class SearchBuilder implements ISearchBuilder { public static boolean myUseMaxPageSize50ForTest = false; protected final IInterceptorBroadcaster myInterceptorBroadcaster; protected final IResourceTagDao myResourceTagDao; - private final String myResourceName; + String myResourceName; private final Class myResourceType; private final HapiFhirLocalContainerEntityManagerFactoryBean myEntityManagerFactory; private final SqlObjectFactory mySqlBuilderFactory; @@ -195,6 +197,9 @@ public class SearchBuilder implements ISearchBuilder { @Autowired(required = false) private IElasticsearchSvc myIElasticsearchSvc; + @Autowired + private FhirContext myCtx; + @Autowired private IJpaStorageResourceParser myJpaStorageResourceParser; @@ -1309,7 +1314,7 @@ public class SearchBuilder implements ISearchBuilder { Pointcut.JPA_PERFTRACE_RAW_SQL, myInterceptorBroadcaster, theParameters.getRequestDetails())) { CurrentThreadCaptureQueriesListener.startCapturing(); } - if (matches.size() == 0) { + if (matches.isEmpty()) { return new HashSet<>(); } if (currentIncludes == null || currentIncludes.isEmpty()) { @@ -1356,196 +1361,32 @@ public class SearchBuilder implements ISearchBuilder { } if (matchAll) { - StringBuilder sqlBuilder = new StringBuilder(); - sqlBuilder.append("SELECT r.").append(findPidFieldName); - sqlBuilder.append(", r.").append(findResourceTypeFieldName); - if (findVersionFieldName != null) { - sqlBuilder.append(", r.").append(findVersionFieldName); - } - sqlBuilder.append(" FROM ResourceLink r WHERE "); - - sqlBuilder.append("r."); - sqlBuilder.append(searchPidFieldName); // (rev mode) target_resource_id | source_resource_id - sqlBuilder.append(" IN (:target_pids)"); - - /* - * We need to set the resource type in 2 cases only: - * 1) we are in $everything mode - * (where we only want to fetch specific resource types, regardless of what is - * available to fetch) - * 2) we are doing revincludes - * - * Technically if the request is a qualified star (e.g. _include=Observation:*) we - * should always be checking the source resource type on the resource link. We don't - * actually index that column though by default, so in order to try and be efficient - * we don't actually include it for includes (but we do for revincludes). This is - * because for an include, it doesn't really make sense to include a different - * resource type than the one you are searching on. - */ - if (wantResourceType != null - && (reverseMode || (myParams != null && myParams.getEverythingMode() != null))) { - // because mySourceResourceType is not part of the HFJ_RES_LINK - // index, this might not be the most optimal performance. - // but it is for an $everything operation (and maybe we should update the index) - sqlBuilder.append(" AND r.mySourceResourceType = :want_resource_type"); - } else { - wantResourceType = null; - } - - // When calling $everything on a Patient instance, we don't want to recurse into new Patient - // resources - // (e.g. via Provenance, List, or Group) when in an $everything operation - if (myParams != null - && myParams.getEverythingMode() == SearchParameterMap.EverythingModeEnum.PATIENT_INSTANCE) { - sqlBuilder.append(" AND r.myTargetResourceType != 'Patient'"); - sqlBuilder.append(UNDESIRED_RESOURCE_LINKAGES_FOR_EVERYTHING_ON_PATIENT_INSTANCE.stream() - .collect(Collectors.joining("', '", " AND r.mySourceResourceType NOT IN ('", "')"))); - } - if (hasDesiredResourceTypes) { - sqlBuilder.append(" AND r.myTargetResourceType IN (:desired_target_resource_types)"); - } - - String sql = sqlBuilder.toString(); - List> partitions = partition(nextRoundMatches, getMaximumPageSize()); - for (Collection nextPartition : partitions) { - TypedQuery q = entityManager.createQuery(sql, Object[].class); - q.setParameter("target_pids", JpaPid.toLongList(nextPartition)); - if (wantResourceType != null) { - q.setParameter("want_resource_type", wantResourceType); - } - if (maxCount != null) { - q.setMaxResults(maxCount); - } - if (hasDesiredResourceTypes) { - q.setParameter("desired_target_resource_types", desiredResourceTypes); - } - List results = q.getResultList(); - for (Object nextRow : results) { - if (nextRow == null) { - // This can happen if there are outgoing references which are canonical or point to - // other servers - continue; - } - - Long version = null; - Long resourceLink = (Long) ((Object[]) nextRow)[0]; - String resourceType = (String) ((Object[]) nextRow)[1]; - if (findVersionFieldName != null) { - version = (Long) ((Object[]) nextRow)[2]; - } - - if (resourceLink != null) { - JpaPid pid = - JpaPid.fromIdAndVersionAndResourceType(resourceLink, version, resourceType); - pidsToInclude.add(pid); - } - } - } + loadIncludesMatchAll( + findPidFieldName, + findResourceTypeFieldName, + findVersionFieldName, + searchPidFieldName, + wantResourceType, + reverseMode, + hasDesiredResourceTypes, + nextRoundMatches, + entityManager, + maxCount, + desiredResourceTypes, + pidsToInclude, + request); } else { - List paths; - - // Start replace - RuntimeSearchParam param; - String resType = nextInclude.getParamType(); - if (isBlank(resType)) { - continue; - } - RuntimeResourceDefinition def = fhirContext.getResourceDefinition(resType); - if (def == null) { - ourLog.warn("Unknown resource type in include/revinclude=" + nextInclude.getValue()); - continue; - } - - String paramName = nextInclude.getParamName(); - if (isNotBlank(paramName)) { - param = mySearchParamRegistry.getActiveSearchParam(resType, paramName); - } else { - param = null; - } - if (param == null) { - ourLog.warn("Unknown param name in include/revinclude=" + nextInclude.getValue()); - continue; - } - - paths = param.getPathsSplitForResourceType(resType); - // end replace - - Set targetResourceTypes = computeTargetResourceTypes(nextInclude, param); - - for (String nextPath : paths) { - String findPidFieldSqlColumn = findPidFieldName.equals(MY_SOURCE_RESOURCE_PID) - ? "src_resource_id" - : "target_resource_id"; - String fieldsToLoad = "r." + findPidFieldSqlColumn + " AS " + RESOURCE_ID_ALIAS; - if (findVersionFieldName != null) { - fieldsToLoad += ", r.target_resource_version AS " + RESOURCE_VERSION_ALIAS; - } - - // Query for includes lookup has 2 cases - // Case 1: Where target_resource_id is available in hfj_res_link table for local references - // Case 2: Where target_resource_id is null in hfj_res_link table and referred by a canonical - // url in target_resource_url - - // Case 1: - Map localReferenceQueryParams = new HashMap<>(); - - String searchPidFieldSqlColumn = searchPidFieldName.equals(MY_TARGET_RESOURCE_PID) - ? "target_resource_id" - : "src_resource_id"; - StringBuilder localReferenceQuery = - new StringBuilder("SELECT " + fieldsToLoad + " FROM hfj_res_link r " - + " WHERE r.src_path = :src_path AND " - + " r.target_resource_id IS NOT NULL AND " - + " r." - + searchPidFieldSqlColumn + " IN (:target_pids) "); - localReferenceQueryParams.put("src_path", nextPath); - // we loop over target_pids later. - if (targetResourceTypes != null) { - if (targetResourceTypes.size() == 1) { - localReferenceQuery.append(" AND r.target_resource_type = :target_resource_type "); - localReferenceQueryParams.put( - "target_resource_type", - targetResourceTypes.iterator().next()); - } else { - localReferenceQuery.append(" AND r.target_resource_type in (:target_resource_types) "); - localReferenceQueryParams.put("target_resource_types", targetResourceTypes); - } - } - - // Case 2: - Pair> canonicalQuery = buildCanonicalUrlQuery( - findVersionFieldName, searchPidFieldSqlColumn, targetResourceTypes); - - // @formatter:on - - String sql = localReferenceQuery + " UNION " + canonicalQuery.getLeft(); - - List> partitions = partition(nextRoundMatches, getMaximumPageSize()); - for (Collection nextPartition : partitions) { - Query q = entityManager.createNativeQuery(sql, Tuple.class); - q.setParameter("target_pids", JpaPid.toLongList(nextPartition)); - localReferenceQueryParams.forEach(q::setParameter); - canonicalQuery.getRight().forEach(q::setParameter); - - if (maxCount != null) { - q.setMaxResults(maxCount); - } - @SuppressWarnings("unchecked") - List results = q.getResultList(); - for (Tuple result : results) { - if (result != null) { - Long resourceId = - NumberUtils.createLong(String.valueOf(result.get(RESOURCE_ID_ALIAS))); - Long resourceVersion = null; - if (findVersionFieldName != null && result.get(RESOURCE_VERSION_ALIAS) != null) { - resourceVersion = NumberUtils.createLong( - String.valueOf(result.get(RESOURCE_VERSION_ALIAS))); - } - pidsToInclude.add(JpaPid.fromIdAndVersion(resourceId, resourceVersion)); - } - } - } - } + loadIncludesMatchSpecific( + nextInclude, + fhirContext, + findPidFieldName, + findVersionFieldName, + searchPidFieldName, + reverseMode, + nextRoundMatches, + entityManager, + maxCount, + pidsToInclude); } } @@ -1609,9 +1450,264 @@ public class SearchBuilder implements ISearchBuilder { return allAdded; } + private void loadIncludesMatchSpecific( + Include nextInclude, + FhirContext fhirContext, + String findPidFieldName, + String findVersionFieldName, + String searchPidFieldName, + boolean reverseMode, + List nextRoundMatches, + EntityManager entityManager, + Integer maxCount, + HashSet pidsToInclude) { + List paths; + + // Start replace + RuntimeSearchParam param; + String resType = nextInclude.getParamType(); + if (isBlank(resType)) { + return; + } + RuntimeResourceDefinition def = fhirContext.getResourceDefinition(resType); + if (def == null) { + ourLog.warn("Unknown resource type in include/revinclude=" + nextInclude.getValue()); + return; + } + + String paramName = nextInclude.getParamName(); + if (isNotBlank(paramName)) { + param = mySearchParamRegistry.getActiveSearchParam(resType, paramName); + } else { + param = null; + } + if (param == null) { + ourLog.warn("Unknown param name in include/revinclude=" + nextInclude.getValue()); + return; + } + + paths = param.getPathsSplitForResourceType(resType); + // end replace + + Set targetResourceTypes = computeTargetResourceTypes(nextInclude, param); + + for (String nextPath : paths) { + String findPidFieldSqlColumn = + findPidFieldName.equals(MY_SOURCE_RESOURCE_PID) ? "src_resource_id" : "target_resource_id"; + String fieldsToLoad = "r." + findPidFieldSqlColumn + " AS " + RESOURCE_ID_ALIAS; + if (findVersionFieldName != null) { + fieldsToLoad += ", r.target_resource_version AS " + RESOURCE_VERSION_ALIAS; + } + + // Query for includes lookup has 2 cases + // Case 1: Where target_resource_id is available in hfj_res_link table for local references + // Case 2: Where target_resource_id is null in hfj_res_link table and referred by a canonical + // url in target_resource_url + + // Case 1: + Map localReferenceQueryParams = new HashMap<>(); + + String searchPidFieldSqlColumn = + searchPidFieldName.equals(MY_TARGET_RESOURCE_PID) ? "target_resource_id" : "src_resource_id"; + StringBuilder localReferenceQuery = new StringBuilder("SELECT " + fieldsToLoad + " FROM hfj_res_link r " + + " WHERE r.src_path = :src_path AND " + + " r.target_resource_id IS NOT NULL AND " + + " r." + + searchPidFieldSqlColumn + " IN (:target_pids) "); + localReferenceQueryParams.put("src_path", nextPath); + // we loop over target_pids later. + if (targetResourceTypes != null) { + if (targetResourceTypes.size() == 1) { + localReferenceQuery.append(" AND r.target_resource_type = :target_resource_type "); + localReferenceQueryParams.put( + "target_resource_type", + targetResourceTypes.iterator().next()); + } else { + localReferenceQuery.append(" AND r.target_resource_type in (:target_resource_types) "); + localReferenceQueryParams.put("target_resource_types", targetResourceTypes); + } + } + + // Case 2: + Pair> canonicalQuery = + buildCanonicalUrlQuery(findVersionFieldName, targetResourceTypes, reverseMode); + + String sql = localReferenceQuery + " UNION " + canonicalQuery.getLeft(); + + List> partitions = partition(nextRoundMatches, getMaximumPageSize()); + for (Collection nextPartition : partitions) { + Query q = entityManager.createNativeQuery(sql, Tuple.class); + q.setParameter("target_pids", JpaPid.toLongList(nextPartition)); + localReferenceQueryParams.forEach(q::setParameter); + canonicalQuery.getRight().forEach(q::setParameter); + + if (maxCount != null) { + q.setMaxResults(maxCount); + } + @SuppressWarnings("unchecked") + List results = q.getResultList(); + for (Tuple result : results) { + if (result != null) { + Long resourceId = NumberUtils.createLong(String.valueOf(result.get(RESOURCE_ID_ALIAS))); + Long resourceVersion = null; + if (findVersionFieldName != null && result.get(RESOURCE_VERSION_ALIAS) != null) { + resourceVersion = + NumberUtils.createLong(String.valueOf(result.get(RESOURCE_VERSION_ALIAS))); + } + pidsToInclude.add(JpaPid.fromIdAndVersion(resourceId, resourceVersion)); + } + } + } + } + } + + private void loadIncludesMatchAll( + String findPidFieldName, + String findResourceTypeFieldName, + String findVersionFieldName, + String searchPidFieldName, + String wantResourceType, + boolean reverseMode, + boolean hasDesiredResourceTypes, + List nextRoundMatches, + EntityManager entityManager, + Integer maxCount, + List desiredResourceTypes, + HashSet pidsToInclude, + RequestDetails request) { + StringBuilder sqlBuilder = new StringBuilder(); + sqlBuilder.append("SELECT r.").append(findPidFieldName); + sqlBuilder.append(", r.").append(findResourceTypeFieldName); + sqlBuilder.append(", r.myTargetResourceUrl"); + if (findVersionFieldName != null) { + sqlBuilder.append(", r.").append(findVersionFieldName); + } + sqlBuilder.append(" FROM ResourceLink r WHERE "); + + sqlBuilder.append("r."); + sqlBuilder.append(searchPidFieldName); // (rev mode) target_resource_id | source_resource_id + sqlBuilder.append(" IN (:target_pids)"); + + /* + * We need to set the resource type in 2 cases only: + * 1) we are in $everything mode + * (where we only want to fetch specific resource types, regardless of what is + * available to fetch) + * 2) we are doing revincludes + * + * Technically if the request is a qualified star (e.g. _include=Observation:*) we + * should always be checking the source resource type on the resource link. We don't + * actually index that column though by default, so in order to try and be efficient + * we don't actually include it for includes (but we do for revincludes). This is + * because for an include, it doesn't really make sense to include a different + * resource type than the one you are searching on. + */ + if (wantResourceType != null && (reverseMode || (myParams != null && myParams.getEverythingMode() != null))) { + // because mySourceResourceType is not part of the HFJ_RES_LINK + // index, this might not be the most optimal performance. + // but it is for an $everything operation (and maybe we should update the index) + sqlBuilder.append(" AND r.mySourceResourceType = :want_resource_type"); + } else { + wantResourceType = null; + } + + // When calling $everything on a Patient instance, we don't want to recurse into new Patient + // resources + // (e.g. via Provenance, List, or Group) when in an $everything operation + if (myParams != null + && myParams.getEverythingMode() == SearchParameterMap.EverythingModeEnum.PATIENT_INSTANCE) { + sqlBuilder.append(" AND r.myTargetResourceType != 'Patient'"); + sqlBuilder.append(UNDESIRED_RESOURCE_LINKAGES_FOR_EVERYTHING_ON_PATIENT_INSTANCE.stream() + .collect(Collectors.joining("', '", " AND r.mySourceResourceType NOT IN ('", "')"))); + } + if (hasDesiredResourceTypes) { + sqlBuilder.append(" AND r.myTargetResourceType IN (:desired_target_resource_types)"); + } + + String sql = sqlBuilder.toString(); + List> partitions = partition(nextRoundMatches, getMaximumPageSize()); + for (Collection nextPartition : partitions) { + TypedQuery q = entityManager.createQuery(sql, Object[].class); + q.setParameter("target_pids", JpaPid.toLongList(nextPartition)); + if (wantResourceType != null) { + q.setParameter("want_resource_type", wantResourceType); + } + if (maxCount != null) { + q.setMaxResults(maxCount); + } + if (hasDesiredResourceTypes) { + q.setParameter("desired_target_resource_types", desiredResourceTypes); + } + List results = q.getResultList(); + Set canonicalUrls = null; + for (Object nextRow : results) { + if (nextRow == null) { + // This can happen if there are outgoing references which are canonical or point to + // other servers + continue; + } + + Long version = null; + Long resourceId = (Long) ((Object[]) nextRow)[0]; + String resourceType = (String) ((Object[]) nextRow)[1]; + String resourceCanonicalUrl = (String) ((Object[]) nextRow)[2]; + if (findVersionFieldName != null) { + version = (Long) ((Object[]) nextRow)[3]; + } + + if (resourceId != null) { + JpaPid pid = JpaPid.fromIdAndVersionAndResourceType(resourceId, version, resourceType); + pidsToInclude.add(pid); + } else if (resourceCanonicalUrl != null) { + if (canonicalUrls == null) { + canonicalUrls = new HashSet<>(); + } + canonicalUrls.add(resourceCanonicalUrl); + } + } + + if (canonicalUrls != null) { + String message = + "Search with _include=* can be inefficient when references using canonical URLs are detected. Use more specific _include values instead."; + firePerformanceWarning(request, message); + loadCanonicalUrls(canonicalUrls, entityManager, pidsToInclude, reverseMode); + } + } + } + + private void loadCanonicalUrls( + Set theCanonicalUrls, + EntityManager theEntityManager, + HashSet thePidsToInclude, + boolean theReverse) { + StringBuilder sqlBuilder; + Set identityHashesForTypes = calculateIndexUriIdentityHashesForResourceTypes(null, theReverse); + List> canonicalUrlPartitions = + partition(theCanonicalUrls, getMaximumPageSize() - identityHashesForTypes.size()); + + sqlBuilder = new StringBuilder(); + sqlBuilder.append("SELECT i.myResourcePid "); + sqlBuilder.append("FROM ResourceIndexedSearchParamUri i "); + sqlBuilder.append("WHERE i.myHashIdentity IN (:hash_identity) "); + sqlBuilder.append("AND i.myUri IN (:uris)"); + + String canonicalResSql = sqlBuilder.toString(); + + for (Collection nextCanonicalUrlList : canonicalUrlPartitions) { + TypedQuery canonicalResIdQuery = theEntityManager.createQuery(canonicalResSql, Long.class); + canonicalResIdQuery.setParameter("hash_identity", identityHashesForTypes); + canonicalResIdQuery.setParameter("uris", nextCanonicalUrlList); + List resIds = canonicalResIdQuery.getResultList(); + for (var next : resIds) { + if (next != null) { + thePidsToInclude.add(JpaPid.fromId(next)); + } + } + } + } + /** - * Given a - * @param request + * Sends a raw SQL query to the Pointcut for raw SQL queries. */ private void callRawSqlHookWithCurrentThreadQueries(RequestDetails request) { SqlQueryList capturedQueries = CurrentThreadCaptureQueriesListener.getCurrentQueueAndStopCapturing(); @@ -1641,30 +1737,22 @@ public class SearchBuilder implements ISearchBuilder { @Nonnull private Pair> buildCanonicalUrlQuery( - String theVersionFieldName, String thePidFieldSqlColumn, Set theTargetResourceTypes) { - String fieldsToLoadFromSpidxUriTable = "rUri.res_id"; + String theVersionFieldName, Set theTargetResourceTypes, boolean theReverse) { + String fieldsToLoadFromSpidxUriTable = theReverse ? "r.src_resource_id" : "rUri.res_id"; if (theVersionFieldName != null) { // canonical-uri references aren't versioned, but we need to match the column count for the UNION fieldsToLoadFromSpidxUriTable += ", NULL"; } // The logical join will be by hfj_spidx_uri on sp_name='uri' and sp_uri=target_resource_url. // But sp_name isn't indexed, so we use hash_identity instead. - if (theTargetResourceTypes == null) { - // hash_identity includes the resource type. So a null wildcard must be replaced with a list of all types. - theTargetResourceTypes = myDaoRegistry.getRegisteredDaoTypes(); - } - assert !theTargetResourceTypes.isEmpty(); - - Set identityHashesForTypes = theTargetResourceTypes.stream() - .map(type -> BaseResourceIndexedSearchParam.calculateHashIdentity( - myPartitionSettings, myRequestPartitionId, type, "url")) - .collect(Collectors.toSet()); + Set identityHashesForTypes = + calculateIndexUriIdentityHashesForResourceTypes(theTargetResourceTypes, theReverse); Map canonicalUriQueryParams = new HashMap<>(); StringBuilder canonicalUrlQuery = new StringBuilder( "SELECT " + fieldsToLoadFromSpidxUriTable + " FROM hfj_res_link r " + " JOIN hfj_spidx_uri rUri ON ( "); // join on hash_identity and sp_uri - indexed in IDX_SP_URI_HASH_IDENTITY_V2 - if (theTargetResourceTypes.size() == 1) { + if (theTargetResourceTypes != null && theTargetResourceTypes.size() == 1) { canonicalUrlQuery.append(" rUri.hash_identity = :uri_identity_hash "); canonicalUriQueryParams.put( "uri_identity_hash", identityHashesForTypes.iterator().next()); @@ -1673,21 +1761,102 @@ public class SearchBuilder implements ISearchBuilder { canonicalUriQueryParams.put("uri_identity_hashes", identityHashesForTypes); } - canonicalUrlQuery.append(" AND r.target_resource_url = rUri.sp_uri )" + " WHERE r.src_path = :src_path AND " - + " r.target_resource_id IS NULL AND " - + " r." - + thePidFieldSqlColumn + " IN (:target_pids) "); + canonicalUrlQuery.append(" AND r.target_resource_url = rUri.sp_uri )"); + canonicalUrlQuery.append(" WHERE r.src_path = :src_path AND "); + canonicalUrlQuery.append(" r.target_resource_id IS NULL "); + canonicalUrlQuery.append(" AND "); + if (theReverse) { + canonicalUrlQuery.append("rUri.res_id"); + } else { + canonicalUrlQuery.append("r.src_resource_id"); + } + canonicalUrlQuery.append(" IN (:target_pids) "); + return Pair.of(canonicalUrlQuery.toString(), canonicalUriQueryParams); } - private List> partition(Collection theNextRoundMatches, int theMaxLoad) { + @Nonnull + Set calculateIndexUriIdentityHashesForResourceTypes(Set theTargetResourceTypes, boolean theReverse) { + Set targetResourceTypes = theTargetResourceTypes; + if (targetResourceTypes == null) { + /* + * If we don't have a list of valid target types, we need to figure out a list of all + * possible target types in order to perform the search of the URI index table. This is + * because the hash_identity column encodes the resource type, so we'll need a hash + * value for each possible target type. + */ + targetResourceTypes = new HashSet<>(); + Set possibleTypes = myDaoRegistry.getRegisteredDaoTypes(); + if (theReverse) { + // For reverse includes, it is really hard to figure out what types + // are actually potentially pointing to the type we're searching for + // in this context, so let's just assume it could be anything. + targetResourceTypes = possibleTypes; + } else { + for (var next : mySearchParamRegistry.getActiveSearchParams(myResourceName).values().stream() + .filter(t -> t.getParamType().equals(RestSearchParameterTypeEnum.REFERENCE)) + .collect(Collectors.toList())) { + + // If the reference points to a Reference (ie not a canonical or CanonicalReference) + // then it doesn't matter here anyhow. The logic here only works for elements at the + // root level of the document (e.g. QuestionnaireResponse.subject or + // QuestionnaireResponse.subject.where(...)) but this is just an optimization + // anyhow. + if (next.getPath().startsWith(myResourceName + ".")) { + String elementName = + next.getPath().substring(next.getPath().indexOf('.') + 1); + int secondDotIndex = elementName.indexOf('.'); + if (secondDotIndex != -1) { + elementName = elementName.substring(0, secondDotIndex); + } + BaseRuntimeChildDefinition child = + myContext.getResourceDefinition(myResourceName).getChildByName(elementName); + if (child != null) { + BaseRuntimeElementDefinition childDef = child.getChildByName(elementName); + if (childDef != null) { + if (childDef.getName().equals("Reference")) { + continue; + } + } + } + } + + if (!next.getTargets().isEmpty()) { + // For each reference parameter on the resource type we're searching for, + // add all the potential target types to the list of possible target + // resource types we can look up. + for (var nextTarget : next.getTargets()) { + if (possibleTypes.contains(nextTarget)) { + targetResourceTypes.add(nextTarget); + } + } + } else { + // If we have any references that don't define any target types, then + // we need to assume that all enabled resource types are possible target + // types + targetResourceTypes.addAll(possibleTypes); + break; + } + } + } + } + assert !targetResourceTypes.isEmpty(); + + Set identityHashesForTypes = targetResourceTypes.stream() + .map(type -> BaseResourceIndexedSearchParam.calculateHashIdentity( + myPartitionSettings, myRequestPartitionId, type, "url")) + .collect(Collectors.toSet()); + return identityHashesForTypes; + } + + private List> partition(Collection theNextRoundMatches, int theMaxLoad) { if (theNextRoundMatches.size() <= theMaxLoad) { return Collections.singletonList(theNextRoundMatches); } else { - List> retVal = new ArrayList<>(); - Collection current = null; - for (JpaPid next : theNextRoundMatches) { + List> retVal = new ArrayList<>(); + Collection current = null; + for (T next : theNextRoundMatches) { if (current == null) { current = new ArrayList<>(theMaxLoad); retVal.add(current); @@ -2121,19 +2290,11 @@ public class SearchBuilder implements ISearchBuilder { } private void sendProcessingMsgAndFirePerformanceHook() { - StorageProcessingMessage message = new StorageProcessingMessage(); String msg = "Pass completed with no matching results seeking rows " + myPidSet.size() + "-" + mySkipCount + ". This indicates an inefficient query! Retrying with new max count of " + myMaxResultsToFetch; - ourLog.warn(msg); - message.setMessage(msg); - HookParams params = new HookParams() - .add(RequestDetails.class, myRequest) - .addIfMatchesType(ServletRequestDetails.class, myRequest) - .add(StorageProcessingMessage.class, message); - CompositeInterceptorBroadcaster.doCallHooks( - myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_WARNING, params); + firePerformanceWarning(myRequest, msg); } private void initializeIteratorQuery(Integer theOffset, Integer theMaxResultsToFetch) { @@ -2208,6 +2369,18 @@ public class SearchBuilder implements ISearchBuilder { } } + private void firePerformanceWarning(RequestDetails theRequest, String theMessage) { + ourLog.warn(theMessage); + StorageProcessingMessage message = new StorageProcessingMessage(); + message.setMessage(theMessage); + HookParams params = new HookParams() + .add(RequestDetails.class, theRequest) + .addIfMatchesType(ServletRequestDetails.class, theRequest) + .add(StorageProcessingMessage.class, message); + CompositeInterceptorBroadcaster.doCallHooks( + myInterceptorBroadcaster, theRequest, Pointcut.JPA_PERFTRACE_WARNING, params); + } + public static int getMaximumPageSize() { if (myUseMaxPageSize50ForTest) { return MAXIMUM_PAGE_SIZE_FOR_TESTING; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/builder/SearchBuilderTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/builder/SearchBuilderTest.java new file mode 100644 index 00000000000..fd8c3948478 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/builder/SearchBuilderTest.java @@ -0,0 +1,70 @@ +package ca.uhn.fhir.jpa.search.builder; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.api.dao.DaoRegistry; +import ca.uhn.fhir.jpa.model.config.PartitionSettings; +import ca.uhn.fhir.rest.server.util.FhirContextSearchParamRegistry; +import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class SearchBuilderTest { + + public static final FhirContext ourCtx = FhirContext.forR4Cached(); + @Spy + private FhirContext myFhirContext = ourCtx; + + @Spy + private ISearchParamRegistry mySearchParamRegistry = new FhirContextSearchParamRegistry(myFhirContext); + + @Spy + private PartitionSettings myPartitionSettings = new PartitionSettings(); + + @Mock(strictness = Mock.Strictness.LENIENT) + private DaoRegistry myDaoRegistry; + + @InjectMocks + private SearchBuilder mySearchBuilder; + + @BeforeEach + public void beforeEach() { + mySearchBuilder.myResourceName = "QuestionnaireResponse"; + when(myDaoRegistry.getRegisteredDaoTypes()).thenReturn(ourCtx.getResourceTypes()); + } + + @Test + void testCalculateIndexUriIdentityHashesForResourceTypes_Include_Null() { + Set types = mySearchBuilder.calculateIndexUriIdentityHashesForResourceTypes(null, false); + // There are only 12 resource types that actually can be linked to by the QuestionnaireResponse + // resource via canonical references in any parameters + assertThat(types).hasSize(1); + } + + @Test + void testCalculateIndexUriIdentityHashesForResourceTypes_Include_Nonnull() { + Set inputTypes = Set.of("Questionnaire"); + Set types = mySearchBuilder.calculateIndexUriIdentityHashesForResourceTypes(inputTypes, false); + // Just the one that we actually specified + assertThat(types).hasSize(1); + } + + @Test + void testCalculateIndexUriIdentityHashesForResourceTypes_RevInclude_Null() { + Set types = mySearchBuilder.calculateIndexUriIdentityHashesForResourceTypes(null, true); + // Revincludes are really hard to figure out the potential resource types for, so we just need to + // use all active resource types + assertThat(types).hasSize(146); + } + +} diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml b/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml index 1f7b6661ec3..e3ba27996ee 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 - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-hfql/pom.xml b/hapi-fhir-jpaserver-hfql/pom.xml index 002371d9271..2ae9ad02678 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 - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-ips/pom.xml b/hapi-fhir-jpaserver-ips/pom.xml index 3f44000878f..88e32f7e2d6 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 - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-mdm/pom.xml b/hapi-fhir-jpaserver-mdm/pom.xml index c3ca893a4f9..669f52ea6ba 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 - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-model/pom.xml b/hapi-fhir-jpaserver-model/pom.xml index a1a87693d93..1df82dac9f4 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 - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamUri.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamUri.java index dd58fb0175b..d16396269b4 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamUri.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamUri.java @@ -228,6 +228,7 @@ public class ResourceIndexedSearchParamUri extends BaseResourceIndexedSearchPara b.append("paramName", getParamName()); b.append("uri", myUri); b.append("hashUri", myHashUri); + b.append("hashIdentity", myHashIdentity); return b.toString(); } diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceLink.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceLink.java index 13ec8045993..7c47c07025d 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceLink.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceLink.java @@ -304,11 +304,11 @@ public class ResourceLink extends BaseResourceIndex { StringBuilder b = new StringBuilder(); b.append("ResourceLink["); b.append("path=").append(mySourcePath); - b.append(", src=").append(mySourceResourcePid); - b.append(", target=").append(myTargetResourcePid); - b.append(", targetType=").append(myTargetResourceType); - b.append(", targetVersion=").append(myTargetResourceVersion); - b.append(", targetUrl=").append(myTargetResourceUrl); + b.append(", srcResId=").append(mySourceResourcePid); + b.append(", targetResId=").append(myTargetResourcePid); + b.append(", targetResType=").append(myTargetResourceType); + b.append(", targetResVersion=").append(myTargetResourceVersion); + b.append(", targetResUrl=").append(myTargetResourceUrl); b.append("]"); return b.toString(); diff --git a/hapi-fhir-jpaserver-searchparam/pom.xml b/hapi-fhir-jpaserver-searchparam/pom.xml index 2c6ce3d9798..675432baedc 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 - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../hapi-deployable-pom/pom.xml 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 3608d944fc0..fe7e89a58c1 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 @@ -2161,6 +2161,12 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor } if (parsed.isAbsolute()) { + String refValue = + fakeReference.getReferenceElement().getValue(); + if (refValue.contains("|")) { + fakeReference.setReference(refValue.substring(0, refValue.indexOf('|'))); + } + myPathAndRef = new PathAndRef(theSearchParam.getName(), thePath, fakeReference, true); theParams.add(myPathAndRef); break; diff --git a/hapi-fhir-jpaserver-subscription/pom.xml b/hapi-fhir-jpaserver-subscription/pom.xml index 7c80ef59b37..928e4754d88 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 - 7.3.6-SNAPSHOT + 7.3.7-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 8f49a5ce201..6d526c2967a 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 - 7.3.6-SNAPSHOT + 7.3.7-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 0208c5bfb32..75747b68a7f 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 - 7.3.6-SNAPSHOT + 7.3.7-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 b0d67b97265..9ac14b62183 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 - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../hapi-deployable-pom/pom.xml 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 8a966eed8e8..640d2a45398 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 @@ -13,6 +13,7 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.context.support.ValueSetExpansionOptions; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.api.dao.ReindexParameters; import ca.uhn.fhir.jpa.api.model.DeleteMethodOutcome; import ca.uhn.fhir.jpa.api.model.ExpungeOptions; @@ -72,6 +73,8 @@ import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.Practitioner; import org.hl7.fhir.r4.model.Provenance; import org.hl7.fhir.r4.model.Quantity; +import org.hl7.fhir.r4.model.Questionnaire; +import org.hl7.fhir.r4.model.QuestionnaireResponse; import org.hl7.fhir.r4.model.Reference; import org.hl7.fhir.r4.model.ServiceRequest; import org.hl7.fhir.r4.model.StringType; @@ -1505,6 +1508,40 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test } + @Test + public void testSearchWithRevInclude() { + Questionnaire q = new Questionnaire(); + q.setId("q"); + q.setUrl("http://foo"); + q.setVersion("1.0"); + myQuestionnaireDao.update(q, mySrd); + + QuestionnaireResponse qr = new QuestionnaireResponse(); + qr.setId("qr"); + qr.setQuestionnaire("http://foo"); + myQuestionnaireResponseDao.update(qr, mySrd); + + logAllResourceLinks(); + + SearchParameterMap map = new SearchParameterMap(); + map.setLoadSynchronous(true); + map.add("_id", new ReferenceParam("Questionnaire/q")); + map.addRevInclude(QuestionnaireResponse.INCLUDE_QUESTIONNAIRE); + IFhirResourceDao dao = myQuestionnaireDao; + dao.search(map, mySrd); + + myCaptureQueriesListener.clear(); + IBundleProvider outcome = dao.search(map, mySrd); + toUnqualifiedVersionlessIdValues(outcome); + assertEquals(4, myCaptureQueriesListener.countSelectQueries()); + + myCaptureQueriesListener.clear(); + outcome = dao.search(map, mySrd); + toUnqualifiedVersionlessIdValues(outcome); + assertEquals(4, myCaptureQueriesListener.countSelectQueries()); + } + + /** * See the class javadoc before changing the counts in this test! */ diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchIncludeTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchIncludeTest.java index 6f36a323890..a51f5423add 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchIncludeTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchIncludeTest.java @@ -1,10 +1,15 @@ package ca.uhn.fhir.jpa.dao.r4; -import static org.junit.jupiter.api.Assertions.assertEquals; +import ca.uhn.fhir.interceptor.api.HookParams; +import ca.uhn.fhir.interceptor.api.IAnonymousInterceptor; +import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; +import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage; import ca.uhn.fhir.jpa.search.PersistedJpaSearchFirstPageBundleProvider; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.test.BaseJpaR4Test; +import ca.uhn.fhir.jpa.util.SqlQuery; import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.param.DateParam; @@ -12,9 +17,7 @@ import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.ParamPrefixEnum; import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.server.SimpleBundleProvider; -import org.hamcrest.Matcher; -import org.hamcrest.Matchers; -import org.hamcrest.collection.IsIterableContainingInAnyOrder; +import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.BodyStructure; import org.hl7.fhir.r4.model.CarePlan; @@ -23,34 +26,169 @@ import org.hl7.fhir.r4.model.EpisodeOfCare; import org.hl7.fhir.r4.model.Organization; import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.Procedure; +import org.hl7.fhir.r4.model.Questionnaire; +import org.hl7.fhir.r4.model.QuestionnaireResponse; import org.hl7.fhir.r4.model.Reference; import org.hl7.fhir.r4.model.SearchParameter; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; import org.springframework.transaction.support.TransactionTemplate; -import java.sql.Date; import java.time.Instant; import java.time.temporal.ChronoUnit; -import java.util.Collection; +import java.util.Date; import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; -import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; -@SuppressWarnings({"unchecked", "Duplicates"}) +@SuppressWarnings({"Duplicates"}) public class FhirResourceDaoR4SearchIncludeTest extends BaseJpaR4Test { - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4SearchIncludeTest.class); + + @Mock + private IAnonymousInterceptor myAnonymousInterceptor; + @Captor + private ArgumentCaptor myParamsCaptor; @AfterEach public void afterEach() { myStorageSettings.setMaximumIncludesToLoadPerPage(JpaStorageSettings.DEFAULT_MAXIMUM_INCLUDES_TO_LOAD_PER_PAGE); + myInterceptorRegistry.unregisterInterceptor(myAnonymousInterceptor); } + @ParameterizedTest + @CsvSource({ + // theQuestionnaireRespId, theReverse, theMatchAll + "QuestionnaireResponse/qr , false, false", + "QuestionnaireResponse/qr2 , false, false", + "QuestionnaireResponse/qr , true, false", + "QuestionnaireResponse/qr2 , true, false", + "QuestionnaireResponse/qr , false, true", + "QuestionnaireResponse/qr2 , false, true", +// "QuestionnaireResponse/qr , true, true", // Not yet supported +// "QuestionnaireResponse/qr2 , true, true", // Not yet supported + }) + public void testIncludeCanonicalReference(String theQuestionnaireRespId, boolean theReverse, boolean theMatchAll) { + Questionnaire qWrongVersion = new Questionnaire(); + qWrongVersion.setId("qWrongVersion"); + qWrongVersion.setUrl("http://foo"); + qWrongVersion.setVersion("99.0"); + myQuestionnaireDao.update(qWrongVersion, mySrd); + + Questionnaire q = new Questionnaire(); + q.setId("q"); + q.setUrl("http://foo"); + q.setVersion("1.0"); + myQuestionnaireDao.update(q, mySrd); + + if (theQuestionnaireRespId.equals("QuestionnaireResponse/qr")) { + QuestionnaireResponse qr = new QuestionnaireResponse(); + qr.setId("qr"); + qr.setQuestionnaire("http://foo"); + myQuestionnaireResponseDao.update(qr, mySrd); + } else { + QuestionnaireResponse qr2 = new QuestionnaireResponse(); + qr2.setId("qr2"); + qr2.setQuestionnaire("http://foo|1.0"); + myQuestionnaireResponseDao.update(qr2, mySrd); + } + + logAllUriIndexes(); + logAllResourceLinks(); + + // Create a QR and Q that have other URLs and shouldn't be turned up in searches here + Questionnaire qIrrelevant = new Questionnaire(); + qIrrelevant.setId("qIrrelevant"); + qIrrelevant.setUrl("http://fooIrrelevant"); + qIrrelevant.setVersion("1.0"); + myQuestionnaireDao.update(qIrrelevant, mySrd); + + QuestionnaireResponse qrIrrelevant = new QuestionnaireResponse(); + qrIrrelevant.setId("qrIrrelevant"); + qrIrrelevant.setQuestionnaire("http://fooIrrelevant"); + myQuestionnaireResponseDao.update(qrIrrelevant, mySrd); + + + IBundleProvider outcome; + IFhirResourceDao dao; + SearchParameterMap map; + String expectWarning = null; + if (theReverse) { + map = new SearchParameterMap(); + map.add("_id", new TokenParam("Questionnaire/q")); + if (theMatchAll) { + map.addRevInclude(IBaseResource.INCLUDE_ALL); + } else { + map.addRevInclude(QuestionnaireResponse.INCLUDE_QUESTIONNAIRE); + } + dao = myQuestionnaireDao; + } else { + map = new SearchParameterMap(); + map.add("_id", new TokenParam(theQuestionnaireRespId)); + if (theMatchAll) { + map.addInclude(IBaseResource.INCLUDE_ALL); + } else { + map.addInclude(QuestionnaireResponse.INCLUDE_QUESTIONNAIRE); + } + dao = myQuestionnaireResponseDao; + } + + if (theMatchAll) { + expectWarning = "Search with _include=* can be inefficient"; + } + + myCaptureQueriesListener.clear(); + myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.JPA_PERFTRACE_WARNING, myAnonymousInterceptor); + + map.setLoadSynchronous(true); + outcome = dao.search(map, mySrd); + List outcomeValues = toUnqualifiedVersionlessIdValues(outcome); + myCaptureQueriesListener.logSelectQueries(); + + if (theReverse) { + assertThat(outcomeValues).as(outcomeValues.toString()).containsExactlyInAnyOrder( + theQuestionnaireRespId, "Questionnaire/q" + ); + } else { + assertThat(outcomeValues).as(outcomeValues.toString()).containsExactlyInAnyOrder( + theQuestionnaireRespId, "Questionnaire/q", "Questionnaire/qWrongVersion" + ); + } + + if (expectWarning == null) { + verify(myAnonymousInterceptor, never()).invoke(eq(Pointcut.JPA_PERFTRACE_WARNING), myParamsCaptor.capture()); + } else { + verify(myAnonymousInterceptor, times(1)).invoke(eq(Pointcut.JPA_PERFTRACE_WARNING), myParamsCaptor.capture()); + HookParams params = myParamsCaptor.getValue(); + assertThat(params.get(StorageProcessingMessage.class).getMessage()).contains(expectWarning); + } + + if (!theReverse && theMatchAll) { + SqlQuery searchForCanonicalReferencesQuery = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(3); + // Make sure we have the right query - If this ever fails, maybe we have optimized the queries + // (or somehow made things worse) and the search for the canonical target is no longer the 4th + // SQL query + assertThat(searchForCanonicalReferencesQuery.getSql(true, false)).contains("rispu1_0.HASH_IDENTITY in ('-600769180185160063')"); + assertThat(searchForCanonicalReferencesQuery.getSql(true, false)).contains("rispu1_0.SP_URI in ('http://foo')"); + } + + } + + + @Test public void testIncludesNotAppliedToIncludedResources() { createOrganizationWithReferencingEpisodesOfCare(10); @@ -59,7 +197,7 @@ public class FhirResourceDaoR4SearchIncludeTest extends BaseJpaR4Test { .add("_id", new TokenParam("EOC-0")) .addInclude(new Include("*")) .addRevInclude(new Include("*").setRecurse(true)); - IBundleProvider results = myEpisodeOfCareDao.search(map); + IBundleProvider results = myEpisodeOfCareDao.search(map, mySrd); List ids = toUnqualifiedVersionlessIdValues(results); assertThat(ids).as(ids.toString()).containsExactlyInAnyOrder("EpisodeOfCare/EOC-0", "Organization/ORG-0"); } @@ -72,7 +210,7 @@ public class FhirResourceDaoR4SearchIncludeTest extends BaseJpaR4Test { .setCount(10) .addInclude(new Include("*")) .addRevInclude(new Include("*").setRecurse(true)); - IBundleProvider results = myOrganizationDao.search(map); + IBundleProvider results = myOrganizationDao.search(map, mySrd); List ids = toUnqualifiedVersionlessIdValues(results); List expected = IntStream.range(0, 10) @@ -97,7 +235,7 @@ public class FhirResourceDaoR4SearchIncludeTest extends BaseJpaR4Test { .add("_id", new TokenParam("ORG-0")) .addInclude(new Include("*")) .addRevInclude(new Include("*").setRecurse(true)); - IBundleProvider results = myOrganizationDao.search(map); + IBundleProvider results = myOrganizationDao.search(map, mySrd); List ids = toUnqualifiedVersionlessIdValues(results); assertThat(ids).as(ids.toString()).containsExactlyInAnyOrder("EpisodeOfCare/EOC-0", "EpisodeOfCare/EOC-1", "EpisodeOfCare/EOC-2", "EpisodeOfCare/EOC-3", "EpisodeOfCare/EOC-4", "Organization/ORG-0"); } @@ -110,7 +248,7 @@ public class FhirResourceDaoR4SearchIncludeTest extends BaseJpaR4Test { SearchParameterMap map = SearchParameterMap.newSynchronous() .addInclude(new Include("CarePlan.patient")); try { - myCarePlanDao.search(map); + myCarePlanDao.search(map, mySrd); fail(); } catch (Exception e) { // good @@ -120,7 +258,7 @@ public class FhirResourceDaoR4SearchIncludeTest extends BaseJpaR4Test { SearchParameterMap map2 = SearchParameterMap.newSynchronous() .addInclude(new Include("CarePlan:patient")); try { - IBundleProvider results = myCarePlanDao.search(map2); + IBundleProvider results = myCarePlanDao.search(map2, mySrd); List ids = toUnqualifiedVersionlessIdValues(results); assertThat(ids).as(ids.toString()).containsExactlyInAnyOrder("CarePlan/CP-1", "Patient/PAT-1"); } catch (Exception e) { @@ -161,7 +299,7 @@ public class FhirResourceDaoR4SearchIncludeTest extends BaseJpaR4Test { logAllResources(); logAllResourceLinks(); - // Non synchronous + // Non-synchronous SearchParameterMap map = new SearchParameterMap(); map.add("_id", new TokenParam("PRA8780542726")); map.addRevInclude(new Include("Procedure:part-of")); @@ -194,7 +332,7 @@ public class FhirResourceDaoR4SearchIncludeTest extends BaseJpaR4Test { .setCount(10) .addInclude(new Include("*")) .addRevInclude(new Include("*").setRecurse(true)); - IBundleProvider results = myOrganizationDao.search(map); + IBundleProvider results = myOrganizationDao.search(map, mySrd); List ids = toUnqualifiedVersionlessIdValues(results); assertThat(ids).as(ids.toString()).containsExactlyInAnyOrder("EpisodeOfCare/EOC-0", "EpisodeOfCare/EOC-1", "EpisodeOfCare/EOC-2", "EpisodeOfCare/EOC-3", "Organization/ORG-0", "Organization/ORG-P"); @@ -210,7 +348,7 @@ public class FhirResourceDaoR4SearchIncludeTest extends BaseJpaR4Test { .add("_id", new TokenParam("ORG-0")) .addRevInclude(EpisodeOfCare.INCLUDE_ORGANIZATION); myCaptureQueriesListener.clear(); - IBundleProvider results = myOrganizationDao.search(map); + IBundleProvider results = myOrganizationDao.search(map, mySrd); List ids = toUnqualifiedVersionlessIdValues(results); myCaptureQueriesListener.logSelectQueries(); assertThat(ids).as(ids.toString()).containsExactlyInAnyOrder("EpisodeOfCare/EOC-0", "EpisodeOfCare/EOC-1", "EpisodeOfCare/EOC-2", "EpisodeOfCare/EOC-3", "EpisodeOfCare/EOC-4", "EpisodeOfCare/EOC-5", "EpisodeOfCare/EOC-6", "EpisodeOfCare/EOC-7", "EpisodeOfCare/EOC-8", "EpisodeOfCare/EOC-9", "Organization/ORG-0"); @@ -223,38 +361,40 @@ public class FhirResourceDaoR4SearchIncludeTest extends BaseJpaR4Test { Organization org = new Organization(); org.setId("Organization/ORG-P"); org.setName("ORG-P"); - myOrganizationDao.update(org); + myOrganizationDao.update(org, mySrd); org = new Organization(); org.setId("Organization/ORG-0"); org.setName("ORG-0"); org.setPartOf(new Reference("Organization/ORG-P")); - myOrganizationDao.update(org); + myOrganizationDao.update(org, mySrd); for (int i = 0; i < theEocCount; i++) { EpisodeOfCare eoc = new EpisodeOfCare(); eoc.setId("EpisodeOfCare/EOC-" + i); eoc.getManagingOrganization().setReference("Organization/ORG-0"); - myEpisodeOfCareDao.update(eoc); + myEpisodeOfCareDao.update(eoc, mySrd); } } + @SuppressWarnings("SameParameterValue") private void createPatientWithReferencingCarePlan(int theCount) { org.hl7.fhir.r4.model.Patient patient = new Patient(); patient.setId("Patient/PAT-1"); - myPatientDao.update(patient); + myPatientDao.update(patient, mySrd); for (int i = 1; i <= theCount; i++) { CarePlan carePlan = new CarePlan(); carePlan.setId("CarePlan/CP-" + i); carePlan.getSubject().setReference("Patient/PAT-1"); - myCarePlanDao.update(carePlan); + myCarePlanDao.update(carePlan, mySrd); } } /** - * https://github.com/hapifhir/hapi-fhir/issues/4896 + * #4896 */ + @SuppressWarnings("DataFlowIssue") @Test void testLastUpdatedDoesNotApplyToForwardOrRevIncludes() { // given @@ -277,7 +417,7 @@ public class FhirResourceDaoR4SearchIncludeTest extends BaseJpaR4Test { // when // "Patient?_lastUpdated=gt2023-01-01&_revinclude=Group:member&_revinclude=CareTeam:subject&_include=Patient:organization"); SearchParameterMap map = new SearchParameterMap(); - map.setLastUpdated(new DateRangeParam(new DateParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, Date.from(now)))); + map.setLastUpdated(new DateRangeParam(new DateParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, java.util.Date.from(now)))); map.addInclude(new Include("Patient:organization")); map.addRevInclude(new Include("Group:member")); map.addRevInclude(new Include("CareTeam:subject")); diff --git a/hapi-fhir-jpaserver-test-r4b/pom.xml b/hapi-fhir-jpaserver-test-r4b/pom.xml index e0b5b75476a..75d284dddda 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 - 7.3.6-SNAPSHOT + 7.3.7-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 80abd1e6704..d8ed3a11ea3 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 - 7.3.6-SNAPSHOT + 7.3.7-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 55e3f40798e..323a1e00051 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 - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml index 114ef345192..da796517f5b 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 - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-server-cds-hooks/pom.xml b/hapi-fhir-server-cds-hooks/pom.xml index bd228ed42aa..3bef5a5c5df 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 - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server-mdm/pom.xml b/hapi-fhir-server-mdm/pom.xml index 97d071922d9..bddca577b5b 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 - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server-openapi/pom.xml b/hapi-fhir-server-openapi/pom.xml index 6edafce3142..c1f03e6eca8 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 - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server/pom.xml b/hapi-fhir-server/pom.xml index 2749ec51073..bc844b33a29 100644 --- a/hapi-fhir-server/pom.xml +++ b/hapi-fhir-server/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.6-SNAPSHOT + 7.3.7-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 86c26e93fd7..a4eb90d2992 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 - 7.3.6-SNAPSHOT + 7.3.7-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 c85f20d82c8..614cfc967b7 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 - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../pom.xml @@ -21,7 +21,7 @@ ca.uhn.hapi.fhir hapi-fhir-caching-api - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml index 5a74eb3e0d5..322eb544e6c 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 - 7.3.6-SNAPSHOT + 7.3.7-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 bd42b34c6fb..782403fec87 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 - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../../pom.xml diff --git a/hapi-fhir-serviceloaders/pom.xml b/hapi-fhir-serviceloaders/pom.xml index 2e527c8721d..9a56ecfd799 100644 --- a/hapi-fhir-serviceloaders/pom.xml +++ b/hapi-fhir-serviceloaders/pom.xml @@ -5,7 +5,7 @@ hapi-deployable-pom ca.uhn.hapi.fhir - 7.3.6-SNAPSHOT + 7.3.7-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 477d27abfed..3e2354e4901 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 - 7.3.6-SNAPSHOT + 7.3.7-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 77fdd243953..12079ac6303 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 - 7.3.6-SNAPSHOT + 7.3.7-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 65be7ab71b1..f7631efb99b 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 - 7.3.6-SNAPSHOT + 7.3.7-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 ec92d9280a0..e7be98afcd8 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 - 7.3.6-SNAPSHOT + 7.3.7-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 b23066edee6..3172d609d8a 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 - 7.3.6-SNAPSHOT + 7.3.7-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 43ffa609e5e..ff62090b5d1 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 - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/pom.xml b/hapi-fhir-spring-boot/pom.xml index f54b330dcfb..afb870730a1 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 - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-sql-migrate/pom.xml b/hapi-fhir-sql-migrate/pom.xml index 9806c7454ab..ce8c9c06fe6 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 - 7.3.6-SNAPSHOT + 7.3.7-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 a2f0152e5fe..951683d6335 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 - 7.3.6-SNAPSHOT + 7.3.7-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 73b59043093..499a9c799f5 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 - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-batch2/pom.xml b/hapi-fhir-storage-batch2/pom.xml index 99a56d87288..b9445f9c590 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 - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-cr/pom.xml b/hapi-fhir-storage-cr/pom.xml index 39dc4d5cd04..8c717536d95 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 - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-mdm/pom.xml b/hapi-fhir-storage-mdm/pom.xml index 4eb48e53131..ebd4cf3cf89 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 - 7.3.6-SNAPSHOT + 7.3.7-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 5f0824ced45..b67d78bb04d 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 - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage/pom.xml b/hapi-fhir-storage/pom.xml index 9dd655ce395..5aa1daab056 100644 --- a/hapi-fhir-storage/pom.xml +++ b/hapi-fhir-storage/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.6-SNAPSHOT + 7.3.7-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 2fbd76025a2..d83f98ec648 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 - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu2/pom.xml b/hapi-fhir-structures-dstu2/pom.xml index 36209f512ec..5d8654e87ba 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 - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu3/pom.xml b/hapi-fhir-structures-dstu3/pom.xml index 391783bf337..11a5d0e063a 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 - 7.3.6-SNAPSHOT + 7.3.7-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 8fccc47eaa6..1d849cbeba1 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 - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4/pom.xml b/hapi-fhir-structures-r4/pom.xml index fa506960c63..4a90cf8be3d 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 - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4b/pom.xml b/hapi-fhir-structures-r4b/pom.xml index 4f3b7cf86ff..cca72114d9c 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 - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r5/pom.xml b/hapi-fhir-structures-r5/pom.xml index 88a0e3f2025..99f5b18a411 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 - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-test-utilities/pom.xml b/hapi-fhir-test-utilities/pom.xml index 8b67363d6c8..bcc6eda773a 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 - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-testpage-overlay/pom.xml b/hapi-fhir-testpage-overlay/pom.xml index 6644bec6aa4..7834ca35c4a 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 - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-validation-resources-dstu2.1/pom.xml b/hapi-fhir-validation-resources-dstu2.1/pom.xml index 14166c8104b..0790e96f1df 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 - 7.3.6-SNAPSHOT + 7.3.7-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 37c2671ba02..71f137c80ba 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 - 7.3.6-SNAPSHOT + 7.3.7-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 2bebe052b9e..1d0819a3af0 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 - 7.3.6-SNAPSHOT + 7.3.7-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 aef76bf3688..b81fd93c621 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 - 7.3.6-SNAPSHOT + 7.3.7-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 f7a99fae513..f0b2c9fe7b1 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 - 7.3.6-SNAPSHOT + 7.3.7-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 c71882f7e70..61828536241 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 - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml index 8c54e8e06ae..91a608190b8 100644 --- a/hapi-fhir-validation/pom.xml +++ b/hapi-fhir-validation/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-tinder-plugin/pom.xml b/hapi-tinder-plugin/pom.xml index 71a668c2b5d..454a4537616 100644 --- a/hapi-tinder-plugin/pom.xml +++ b/hapi-tinder-plugin/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../pom.xml diff --git a/hapi-tinder-test/pom.xml b/hapi-tinder-test/pom.xml index 13ecb6f9830..d1f52593bf5 100644 --- a/hapi-tinder-test/pom.xml +++ b/hapi-tinder-test/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index a2ccf718a43..915a1c75af2 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ ca.uhn.hapi.fhir hapi-fhir pom - 7.3.6-SNAPSHOT + 7.3.7-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 5bdf6df18a6..99ebdb57ef7 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 - 7.3.6-SNAPSHOT + 7.3.7-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 715aee56025..c7a989f642b 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 - 7.3.6-SNAPSHOT + 7.3.7-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 885bbc82535..da5b9aadeeb 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 - 7.3.6-SNAPSHOT + 7.3.7-SNAPSHOT ../../pom.xml