Merge pull request #2767 from hapifhir/2765-mdm-paging

Paging implementation for MDM query links operation
This commit is contained in:
Tadgh 2021-07-01 23:16:47 -04:00 committed by GitHub
commit a4fb72975d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
81 changed files with 785 additions and 131 deletions

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -350,7 +350,6 @@ public class ParametersUtil {
} else {
partChildElem.getChildByName("value[x]").getMutator().addValue(part, theValue);
}
}
public static void addPartResource(FhirContext theContext, IBase theParameter, String theName, IBaseResource theValue) {

View File

@ -3,14 +3,14 @@
<modelVersion>4.0.0</modelVersion>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-bom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<packaging>pom</packaging>
<name>HAPI FHIR BOM</name>
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-cli</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../../hapi-deployable-pom</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -0,0 +1,6 @@
---
type: add
issue: 2767
title: "`$mdm-query-links` and `$mdm-duplicate-golden-resources` now enforce paging via parameters `_offset` and `_count`. More
details can be found in the [MDM Operations documentation](/hapi-fhir/docs/server_jpa_mdm/mdm_operations.html)."

View File

@ -4,6 +4,53 @@ MDM links are managed by MDM Operations. These operations are supplied by a [pla
In cases where the operation changes data, if a resource id parameter contains a version (e.g. `Patient/123/_history/1`), then the operation will fail with a 409 CONFLICT if that is not the latest version of that resource. This feature can be used to prevent update conflicts in an environment where multiple users are working on the same set of mdm links.
## Pagination
In both the `$query-links` operation, and the `$mdm-duplicate-golden-resources` paging is supported via `_count` and `_offset` parameters. By default, if you omit page information from your query, default pagination values will be used.
The response will return you the next/self/previous links as part of the parameters response. Here are examples of pagination in these MDM queries.
```http request
GET http://example.com/$mdm-query-links?_offset=0&_count=2
```
Or if you are making a POST request
```http request
POST http://example.com/$mdm-query-links
```
With request body:
```json
{
"resourceType": "Parameters",
"parameter": [ {
"name": "_offset",
"valueInteger": 10
}, {
"name": "_count",
"valueInteger": 10
} ]
}
```
The returning response will contain links to the current, next, and previous pages. If there is no previous/next link, it means there is no previous/next page of data available.
```text
{
"resourceType": "Parameters",
"parameter": [ {
"name": "prev",
"valueUri": "http://example.com/$mdm-query-links?_offset=0&_count=10"
}, {
"name": "self",
"valueUri": "http://example.com/$mdm-query-links?_offset=10&_count=10"
}, {
"name": "next",
"valueUri": "http://example.com/$mdm-query-links?_offset=20&_count=10"
},...(truncated)
```
## Query links
Use the `$mdm-query-links` operation to view MDM links. The results returned are based on the parameters provided. All parameters are optional. This operation takes the following parameters:
@ -50,6 +97,22 @@ Use the `$mdm-query-links` operation to view MDM links. The results returned are
AUTO, MANUAL.
</td>
</tr>
<tr>
<td>_offset</td>
<td>int</td>
<td>0..1</td>
<td>
the offset to begin returning records at.
</td>
</tr>
<tr>
<td>_count</td>
<td>int</td>
<td>0..1</td>
<td>
The number of links to be returned in a page.
</td>
</tr>
</tbody>
</table>
@ -58,7 +121,7 @@ Use the `$mdm-query-links` operation to view MDM links. The results returned are
Use an HTTP GET like `http://example.com/$mdm-query-links?matchResult=POSSIBLE_MATCH` or an HTTP POST to the following URL to invoke this operation:
```url
http://example.com/$mdm-query-links
http://example.com/$mdm-query-links?_offset=10&_count=2
```
The following request body could be used to find all POSSIBLE_MATCH links in the system:
@ -67,6 +130,15 @@ The following request body could be used to find all POSSIBLE_MATCH links in the
{
"resourceType": "Parameters",
"parameter": [ {
"name": "prev",
"valueUri": "http://example.com/$mdm-query-links?_offset=8_count=2"
}, {
"name": "self",
"valueUri": "http://example.com/$mdm-query-links?_offset=10_count=2"
}, {
"name": "next",
"valueUri": "http://example.com/$mdm-query-links?_offset=12_count=2"
}, {
"name": "matchResult",
"valueString": "POSSIBLE_MATCH"
} ]
@ -79,6 +151,15 @@ This operation returns a `Parameters` resource that looks like the following:
{
"resourceType": "Parameters",
"parameter": [ {
"name": "prev",
"valueUri": "http://example.com$mdm-query-links?_offset=8_count=2"
}, {
"name": "self",
"valueUri": "http://example.com$mdm-query-links?_offset=10_count=10"
}, {
"name": "next",
"valueUri": "http://example.com$mdm-query-links?_offset=20_count=10"
}, {
"name": "link",
"part": [ {
"name": "goldenResourceId",
@ -119,6 +200,37 @@ Use an HTTP GET to the following URL to invoke this operation:
http://example.com/$mdm-duplicate-golden-resources
```
The following is a table of the request parameters supported by this GET operation.
<table class="table table-striped table-condensed">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th>Cardinality</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>_offset</td>
<td>int</td>
<td>0..1</td>
<td>
the offset to begin returning records at.
</td>
</tr>
<tr>
<td>_count</td>
<td>int</td>
<td>0..1</td>
<td>
The number of links to be returned in a page.
</td>
</tr>
</tbody>
</table>
This operation returns `Parameters` similar to `$mdm-query-links`:

View File

@ -11,7 +11,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -428,7 +428,6 @@ public class SearchQueryBuilder {
startOfQueryParameterIndex = bindOffsetParameter(bindVariables, offset, limitHandler, startOfQueryParameterIndex, bindLimitParametersFirst);
bindCountParameter(bindVariables, maxResultsToFetch, limitHandler, startOfQueryParameterIndex, bindLimitParametersFirst);
}
}
}

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -7,7 +7,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -27,12 +27,14 @@ import ca.uhn.fhir.jpa.entity.MdmLink;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchOutcome;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.mdm.api.paging.MdmPageRequest;
import ca.uhn.fhir.mdm.log.Logs;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@ -286,8 +288,8 @@ public class MdmLinkDaoSvc {
* @param theExampleLink The MDM link containing the data we would like to search for.
* @return a list of {@link MdmLink} entities which match the example.
*/
public List<MdmLink> findMdmLinkByExample(Example<MdmLink> theExampleLink) {
return myMdmLinkDao.findAll(theExampleLink);
public Page<MdmLink> findMdmLinkByExample(Example<MdmLink> theExampleLink, MdmPageRequest thePageRequest) {
return myMdmLinkDao.findAll(theExampleLink, thePageRequest.toPageRequest());
}
/**

View File

@ -27,6 +27,7 @@ import ca.uhn.fhir.mdm.api.IMdmLinkUpdaterSvc;
import ca.uhn.fhir.mdm.api.MdmLinkJson;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.mdm.api.paging.MdmPageRequest;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.mdm.provider.MdmControllerHelper;
import ca.uhn.fhir.mdm.provider.MdmControllerUtil;
@ -34,10 +35,10 @@ import ca.uhn.fhir.rest.server.provider.ProviderConstants;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import javax.annotation.Nullable;
import java.util.stream.Stream;
/**
* This class acts as a layer between MdmProviders and MDM services to support a REST API that's not a FHIR Operation API.
@ -66,18 +67,17 @@ public class MdmControllerSvcImpl implements IMdmControllerSvc {
}
@Override
public Stream<MdmLinkJson> queryLinks(@Nullable String theGoldenResourceId, @Nullable String theSourceResourceId, @Nullable String theMatchResult, @Nullable String theLinkSource, MdmTransactionContext theMdmTransactionContext) {
public Page<MdmLinkJson> queryLinks(@Nullable String theGoldenResourceId, @Nullable String theSourceResourceId, @Nullable String theMatchResult, @Nullable String theLinkSource, MdmTransactionContext theMdmTransactionContext, MdmPageRequest thePageRequest) {
IIdType goldenResourceId = MdmControllerUtil.extractGoldenResourceIdDtOrNull(ProviderConstants.MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID, theGoldenResourceId);
IIdType sourceId = MdmControllerUtil.extractSourceIdDtOrNull(ProviderConstants.MDM_QUERY_LINKS_RESOURCE_ID, theSourceResourceId);
MdmMatchResultEnum matchResult = MdmControllerUtil.extractMatchResultOrNull(theMatchResult);
MdmLinkSourceEnum linkSource = MdmControllerUtil.extractLinkSourceOrNull(theLinkSource);
return myMdmLinkQuerySvc.queryLinks(goldenResourceId, sourceId, matchResult, linkSource, theMdmTransactionContext);
return myMdmLinkQuerySvc.queryLinks(goldenResourceId, sourceId, matchResult, linkSource, theMdmTransactionContext, thePageRequest);
}
@Override
public Stream<MdmLinkJson> getDuplicateGoldenResources(MdmTransactionContext theMdmTransactionContext) {
return myMdmLinkQuerySvc.getDuplicateGoldenResources(theMdmTransactionContext);
public Page<MdmLinkJson> getDuplicateGoldenResources(MdmTransactionContext theMdmTransactionContext, MdmPageRequest thePageRequest) {
return myMdmLinkQuerySvc.getDuplicateGoldenResources(theMdmTransactionContext, thePageRequest);
}
@Override

View File

@ -24,6 +24,7 @@ import ca.uhn.fhir.mdm.api.MdmLinkJson;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.mdm.api.IMdmLinkQuerySvc;
import ca.uhn.fhir.mdm.api.paging.MdmPageRequest;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
@ -33,8 +34,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import java.util.stream.Stream;
import org.springframework.data.domain.Page;
public class MdmLinkQuerySvcImpl implements IMdmLinkQuerySvc {
@ -46,17 +46,19 @@ public class MdmLinkQuerySvcImpl implements IMdmLinkQuerySvc {
MdmLinkDaoSvc myMdmLinkDaoSvc;
@Override
public Stream<MdmLinkJson> queryLinks(IIdType theGoldenResourceId, IIdType theSourceResourceId, MdmMatchResultEnum theMatchResult, MdmLinkSourceEnum theLinkSource, MdmTransactionContext theMdmContext) {
public Page<MdmLinkJson> queryLinks(IIdType theGoldenResourceId, IIdType theSourceResourceId, MdmMatchResultEnum theMatchResult, MdmLinkSourceEnum theLinkSource, MdmTransactionContext theMdmContext, MdmPageRequest thePageRequest) {
Example<MdmLink> exampleLink = exampleLinkFromParameters(theGoldenResourceId, theSourceResourceId, theMatchResult, theLinkSource);
return myMdmLinkDaoSvc.findMdmLinkByExample(exampleLink).stream()
.filter(mdmLink -> mdmLink.getMatchResult() != MdmMatchResultEnum.POSSIBLE_DUPLICATE)
.map(this::toJson);
Page<MdmLink> mdmLinkByExample = myMdmLinkDaoSvc.findMdmLinkByExample(exampleLink, thePageRequest);
Page<MdmLinkJson> map = mdmLinkByExample.map(this::toJson);
return map;
}
@Override
public Stream<MdmLinkJson> getDuplicateGoldenResources(MdmTransactionContext theMdmContext) {
public Page<MdmLinkJson> getDuplicateGoldenResources(MdmTransactionContext theMdmContext, MdmPageRequest thePageRequest) {
Example<MdmLink> exampleLink = exampleLinkFromParameters(null, null, MdmMatchResultEnum.POSSIBLE_DUPLICATE, null);
return myMdmLinkDaoSvc.findMdmLinkByExample(exampleLink).stream().map(this::toJson);
Page<MdmLink> mdmLinkPage = myMdmLinkDaoSvc.findMdmLinkByExample(exampleLink, thePageRequest);
Page<MdmLinkJson> map = mdmLinkPage.map(this::toJson);
return map;
}
private MdmLinkJson toJson(MdmLink theLink) {

View File

@ -1,7 +1,9 @@
package ca.uhn.fhir.jpa.mdm;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.api.AddProfileTagEnum;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.interceptor.api.IInterceptorService;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome;
@ -32,9 +34,19 @@ import ca.uhn.fhir.mdm.rules.svc.MdmResourceMatcherSvc;
import ca.uhn.fhir.mdm.util.EIDHelper;
import ca.uhn.fhir.mdm.util.MdmResourceUtil;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.BasePagingProvider;
import ca.uhn.fhir.rest.server.ETagSupportEnum;
import ca.uhn.fhir.rest.server.ElementsSupportEnum;
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
import ca.uhn.fhir.rest.server.IPagingProvider;
import ca.uhn.fhir.rest.server.IRestfulServerDefaults;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import org.apache.commons.lang3.StringUtils;
import org.hamcrest.Matcher;
@ -49,11 +61,15 @@ import org.hl7.fhir.r4.model.Organization;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Practitioner;
import org.hl7.fhir.r4.model.Reference;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.ExtendWith;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@ -71,6 +87,8 @@ import static org.slf4j.LoggerFactory.getLogger;
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = {MdmSubmitterConfig.class, MdmConsumerConfig.class, TestMdmConfigR4.class, SubscriptionProcessorConfig.class})
abstract public class BaseMdmR4Test extends BaseJpaR4Test {
private static final Logger ourLog = getLogger(BaseMdmR4Test.class);
public static final String NAME_GIVEN_JANE = "Jane";

View File

@ -4,6 +4,8 @@ import ca.uhn.fhir.mdm.api.IMdmSettings;
import ca.uhn.fhir.mdm.rules.config.MdmRuleValidator;
import ca.uhn.fhir.mdm.rules.config.MdmSettings;
import ca.uhn.fhir.jpa.mdm.helper.MdmLinkHelper;
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
import ca.uhn.fhir.rest.server.IPagingProvider;
import com.google.common.base.Charsets;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Value;

View File

@ -7,6 +7,8 @@ import ca.uhn.fhir.mdm.api.IMdmMatchFinderSvc;
import ca.uhn.fhir.mdm.api.IMdmSubmitSvc;
import ca.uhn.fhir.mdm.provider.MdmProviderDstu3Plus;
import ca.uhn.fhir.mdm.rules.config.MdmSettings;
import ca.uhn.fhir.rest.server.IPagingProvider;
import ca.uhn.fhir.rest.server.IRestfulServerDefaults;
import com.google.common.base.Charsets;
import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.AfterEach;

View File

@ -1,27 +1,38 @@
package ca.uhn.fhir.jpa.mdm.provider;
import ca.uhn.fhir.jpa.entity.MdmLink;
import ca.uhn.fhir.jpa.util.CircularQueueCaptureQueriesListener;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.util.StopWatch;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.dstu3.model.UnsignedIntType;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.BooleanType;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Parameters;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.StringType;
import org.hl7.fhir.r4.model.Type;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
import java.util.stream.Collectors;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.lessThanOrEqualTo;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
@ -31,6 +42,8 @@ public class MdmProviderQueryLinkR4Test extends BaseLinkR4Test {
private StringType myLinkSource;
private StringType myGoldenResource1Id;
private StringType myGoldenResource2Id;
@Autowired
protected CircularQueueCaptureQueriesListener myCaptureQueriesListener;
@Override
@BeforeEach
@ -55,35 +68,130 @@ public class MdmProviderQueryLinkR4Test extends BaseLinkR4Test {
@Test
public void testQueryLinkOneMatch() {
Parameters result = (Parameters) myMdmProvider.queryLinks(mySourcePatientId, myPatientId, null, null, myRequestDetails);
Parameters result = (Parameters) myMdmProvider.queryLinks(mySourcePatientId, myPatientId, null, null, new UnsignedIntType(0), new UnsignedIntType(10), myRequestDetails);
ourLog.info(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(result));
List<Parameters.ParametersParameterComponent> list = result.getParameter();
List<Parameters.ParametersParameterComponent> list = getParametersByName(result, "link");
assertThat(list, hasSize(1));
List<Parameters.ParametersParameterComponent> part = list.get(0).getPart();
assertMdmLink(7, part, mySourcePatientId.getValue(), myPatientId.getValue(), MdmMatchResultEnum.POSSIBLE_MATCH, "false", "true", null);
}
@Test
public void testQueryLinkThreeMatches() {
public void testQueryLinkPages() {
for (int i = 0; i < 10; i++) {
createPatientAndUpdateLinks(buildJanePatient());
}
int offset = 0;
int count = 2;
StopWatch sw = new StopWatch();
while (true) {
Parameters result = (Parameters) myMdmProvider.queryLinks(null, null, null, myLinkSource, new UnsignedIntType(offset), new UnsignedIntType(count), myRequestDetails);
List<Parameters.ParametersParameterComponent> parameter = result.getParameter();
ourLog.info(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(result));
List<Parameters.ParametersParameterComponent> previousUrl = getParametersByName(result, "prev");
if (offset == 0) {
assertThat(previousUrl, hasSize(0));
} else {
assertThat(previousUrl, hasSize(1));
}
String sourceResourceIds = parameter.stream().flatMap(p -> p.getPart().stream()).filter(part -> part.getName().equals("sourceResourceId")).map(part -> part.getValue().toString()).collect(Collectors.joining(","));
ourLog.warn("Search at offset {} took {} ms",offset, sw.getMillisAndRestart());
ourLog.warn("Found source resource IDs: {}", sourceResourceIds);
List<Parameters.ParametersParameterComponent> mdmLink = getParametersByName(result, "link");
assertThat(mdmLink.size(), is(lessThanOrEqualTo(2)));
List<Parameters.ParametersParameterComponent> selfUrl = getParametersByName(result, "self");
assertThat(selfUrl.size(), is(equalTo(1)));
//We have stopped finding patients, make sure theres no next page
if (StringUtils.isEmpty(sourceResourceIds)) {
List<Parameters.ParametersParameterComponent> nextUrl= getParametersByName(result, "next");
assertThat(nextUrl.size(), is(equalTo(0)));
break;
}
offset += count;
}
}
private List<Parameters.ParametersParameterComponent> getParametersByName(Parameters theParams, String theName) {
return theParams.getParameter().stream().filter(p -> p.getName().equals(theName)).collect(Collectors.toList());
}
@Test
public void testQueryWithIllegalPagingValuesFails() {
//Given
int count = 0;
int offset = 0;
try {
//When
myMdmProvider.queryLinks(
null, null,
null, myLinkSource,
new UnsignedIntType(offset),
new UnsignedIntType(count),
myRequestDetails);
} catch (InvalidRequestException e) {
//Then
assertThat(e.getMessage(), is(equalTo("_count must be greater than 0.")));
}
//Given
count = 1;
offset= -1;
try {
//When
myMdmProvider.queryLinks(
null, null,
null, myLinkSource,
new UnsignedIntType(offset),
new UnsignedIntType(count),
myRequestDetails);
} catch (InvalidRequestException e) {
//Then
assertThat(e.getMessage(), is(equalTo("_offset must be greater than or equal to 0. ")));
}
//Given
count = 0;
offset= -1;
try {
//When
myMdmProvider.queryLinks(
null, null,
null, myLinkSource,
new UnsignedIntType(offset),
new UnsignedIntType(count),
myRequestDetails);
} catch (InvalidRequestException e) {
//Then
assertThat(e.getMessage(), is(equalTo("_offset must be greater than or equal to 0. _count must be greater than 0.")));
}
}
@Test
public void testQueryLnkThreeMatches() {
// Add a third patient
Patient patient = createPatientAndUpdateLinks(buildJanePatient());
IdType patientId = patient.getIdElement().toVersionless();
IAnyResource goldenResource = getGoldenResourceFromTargetResource(patient);
IIdType goldenResourceId = goldenResource.getIdElement().toVersionless();
Parameters result = (Parameters) myMdmProvider.queryLinks(null, null, null, myLinkSource, myRequestDetails);
Parameters result = (Parameters) myMdmProvider.queryLinks(null, null, null, myLinkSource, new UnsignedIntType(0), new UnsignedIntType(10), myRequestDetails);
ourLog.info(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(result));
List<Parameters.ParametersParameterComponent> list = result.getParameter();
assertThat(list, hasSize(3));
List<Parameters.ParametersParameterComponent> part = list.get(2).getPart();
List<Parameters.ParametersParameterComponent> list = getParametersByName(result, "link");
assertThat(list, hasSize(4));
List<Parameters.ParametersParameterComponent> part = list.get(3).getPart();
assertMdmLink(7, part, goldenResourceId.getValue(), patientId.getValue(), MdmMatchResultEnum.MATCH, "false", "false", "2");
}
@Test
public void testQueryPossibleDuplicates() {
Parameters result = (Parameters) myMdmProvider.getDuplicateGoldenResources(myRequestDetails);
Parameters result = (Parameters) myMdmProvider.getDuplicateGoldenResources(new UnsignedIntType(0), new UnsignedIntType(10),myRequestDetails);
ourLog.info(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(result));
List<Parameters.ParametersParameterComponent> list = result.getParameter();
List<Parameters.ParametersParameterComponent> list = getParametersByName(result, "link");
assertThat(list, hasSize(1));
List<Parameters.ParametersParameterComponent> part = list.get(0).getPart();
assertMdmLink(2, part, myGoldenResource1Id.getValue(), myGoldenResource2Id.getValue(), MdmMatchResultEnum.POSSIBLE_DUPLICATE, "false", "false", null);
@ -92,8 +200,9 @@ public class MdmProviderQueryLinkR4Test extends BaseLinkR4Test {
@Test
public void testNotDuplicate() {
{
Parameters result = (Parameters) myMdmProvider.getDuplicateGoldenResources(myRequestDetails);
List<Parameters.ParametersParameterComponent> list = result.getParameter();
Parameters result = (Parameters) myMdmProvider.getDuplicateGoldenResources(new UnsignedIntType(0), new UnsignedIntType(10),myRequestDetails);
List<Parameters.ParametersParameterComponent> list = getParametersByName(result, "link");
assertThat(list, hasSize(1));
}
{
@ -103,8 +212,8 @@ public class MdmProviderQueryLinkR4Test extends BaseLinkR4Test {
assertTrue(((BooleanType) (result.getParameterFirstRep().getValue())).booleanValue());
}
Parameters result = (Parameters) myMdmProvider.getDuplicateGoldenResources(myRequestDetails);
List<Parameters.ParametersParameterComponent> list = result.getParameter();
Parameters result = (Parameters) myMdmProvider.getDuplicateGoldenResources(new UnsignedIntType(0), new UnsignedIntType(10),myRequestDetails);
List<Parameters.ParametersParameterComponent> list = getParametersByName(result, "link");
assertThat(list, hasSize(0));
}

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -7,7 +7,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>
@ -27,6 +27,11 @@
<artifactId>hapi-fhir-server</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
<version>${spring_data_version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu3</artifactId>

View File

@ -20,17 +20,18 @@ package ca.uhn.fhir.mdm.api;
* #L%
*/
import ca.uhn.fhir.mdm.api.paging.MdmPageRequest;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.springframework.data.domain.Page;
import javax.annotation.Nullable;
import java.util.stream.Stream;
public interface IMdmControllerSvc {
Stream<MdmLinkJson> queryLinks(@Nullable String theGoldenResourceId, @Nullable String theSourceResourceId, @Nullable String theMatchResult, @Nullable String theLinkSource, MdmTransactionContext theMdmTransactionContext);
Page<MdmLinkJson> queryLinks(@Nullable String theGoldenResourceId, @Nullable String theSourceResourceId, @Nullable String theMatchResult, @Nullable String theLinkSource, MdmTransactionContext theMdmTransactionContext, MdmPageRequest thePageRequest);
Stream<MdmLinkJson> getDuplicateGoldenResources(MdmTransactionContext theMdmTransactionContext);
Page<MdmLinkJson> getDuplicateGoldenResources(MdmTransactionContext theMdmTransactionContext, MdmPageRequest thePageRequest);
void notDuplicateGoldenResource(String theGoldenResourceId, String theTargetGoldenResourceId, MdmTransactionContext theMdmTransactionContext);

View File

@ -20,15 +20,15 @@ package ca.uhn.fhir.mdm.api;
* #L%
*/
import ca.uhn.fhir.mdm.api.paging.MdmPageRequest;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import org.hl7.fhir.instance.model.api.IIdType;
import java.util.stream.Stream;
import org.springframework.data.domain.Page;
/**
* This service supports the MDM operation providers for those services that return multiple MDM links.
*/
public interface IMdmLinkQuerySvc {
Stream<MdmLinkJson> queryLinks(IIdType theGoldenResourceId, IIdType theSourceResourceId, MdmMatchResultEnum theMatchResult, MdmLinkSourceEnum theLinkSource, MdmTransactionContext theMdmContext);
Stream<MdmLinkJson> getDuplicateGoldenResources(MdmTransactionContext theMdmContext);
Page<MdmLinkJson> queryLinks(IIdType theGoldenResourceId, IIdType theSourceResourceId, MdmMatchResultEnum theMatchResult, MdmLinkSourceEnum theLinkSource, MdmTransactionContext theMdmContext, MdmPageRequest thePageRequest);
Page<MdmLinkJson> getDuplicateGoldenResources(MdmTransactionContext theMdmContext, MdmPageRequest thePageRequest);
}

View File

@ -0,0 +1,78 @@
package ca.uhn.fhir.mdm.api.paging;
/*-
* #%L
* HAPI FHIR - Master Data Management
* %%
* Copyright (C) 2014 - 2021 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%
*/
import ca.uhn.fhir.mdm.api.MdmLinkJson;
import ca.uhn.fhir.rest.server.RestfulServerUtils;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import org.springframework.data.domain.Page;
import java.util.Arrays;
import static ca.uhn.fhir.rest.api.Constants.PARAM_COUNT;
import static ca.uhn.fhir.rest.api.Constants.PARAM_OFFSET;
/**
* Builder to generate {@link MdmPageLinkTuple} objects, based on a given page of data and the incoming page request.
*/
public final class MdmPageLinkBuilder {
/**
* Generates an {@link MdmPageLinkTuple} which contains previous/self/next links for pagination purposes.
*
* @param theServletRequestDetails the incoming request details. Used to determine server base.
* @param theCurrentPage the page of MDM link data. Used for determining if there are next/previous pages available.
* @param thePageRequest the incoming Page request, containing requested offset and count. Used for building offset for outgoing URLs.
*
* @return the {@link MdmPageLinkTuple}
*/
public static MdmPageLinkTuple buildMdmPageLinks(ServletRequestDetails theServletRequestDetails, Page<MdmLinkJson> theCurrentPage, MdmPageRequest thePageRequest) {
String urlWithoutPaging = RestfulServerUtils.createLinkSelfWithoutGivenParameters(theServletRequestDetails.getFhirServerBase(), theServletRequestDetails, Arrays.asList(PARAM_OFFSET, PARAM_COUNT));
return buildMdmPageLinks(urlWithoutPaging, theCurrentPage, thePageRequest);
}
public static MdmPageLinkTuple buildMdmPageLinks(String theUrlWithoutPaging, Page<MdmLinkJson> theCurrentPage, MdmPageRequest thePageRequest) {
MdmPageLinkTuple tuple = new MdmPageLinkTuple();
tuple.setSelfLink(buildLinkWithOffsetAndCount(theUrlWithoutPaging, thePageRequest.getCount(), thePageRequest.getOffset()));
if (theCurrentPage.hasNext()) {
tuple.setNextLink(buildLinkWithOffsetAndCount(theUrlWithoutPaging,thePageRequest.getCount(), thePageRequest.getNextOffset()));
}
if (theCurrentPage.hasPrevious()) {
tuple.setPreviousLink(buildLinkWithOffsetAndCount(theUrlWithoutPaging,thePageRequest.getCount(), thePageRequest.getPreviousOffset()));
}
return tuple;
}
public static String buildLinkWithOffsetAndCount(String theBaseUrl, int theCount, int theOffset) {
StringBuilder builder = new StringBuilder();
builder.append(theBaseUrl);
if (!theBaseUrl.contains("?")) {
builder.append("?");
} else {
builder.append("&");
}
builder.append(PARAM_OFFSET).append("=").append(theOffset);
builder.append("&");
builder.append(PARAM_COUNT).append("=").append(theCount);
return builder.toString();
}
}

View File

@ -0,0 +1,58 @@
package ca.uhn.fhir.mdm.api.paging;
/*-
* #%L
* HAPI FHIR - Master Data Management
* %%
* Copyright (C) 2014 - 2021 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%
*/
import java.util.Optional;
/**
* Data clump class to keep the relevant paging URLs together for MDM.
*/
public class MdmPageLinkTuple {
private String myPreviousLink = null;
private String mySelfLink = null;
private String myNextLink = null;
MdmPageLinkTuple() {}
public Optional<String> getPreviousLink() {
return Optional.ofNullable(myPreviousLink);
}
public void setPreviousLink(String thePreviousLink) {
this.myPreviousLink = thePreviousLink;
}
public String getSelfLink() {
return mySelfLink;
}
public void setSelfLink(String theSelfLink) {
this.mySelfLink = theSelfLink;
}
public Optional<String> getNextLink() {
return Optional.ofNullable(myNextLink);
}
public void setNextLink(String theNextLink) {
this.myNextLink = theNextLink;
}
}

View File

@ -0,0 +1,101 @@
package ca.uhn.fhir.mdm.api.paging;
/*-
* #%L
* HAPI FHIR - Master Data Management
* %%
* Copyright (C) 2014 - 2021 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%
*/
import ca.uhn.fhir.rest.server.IPagingProvider;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.dstu3.model.UnsignedIntType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.slf4j.Logger;
import org.springframework.data.domain.PageRequest;
import javax.annotation.Nullable;
import static ca.uhn.fhir.rest.api.Constants.PARAM_COUNT;
import static ca.uhn.fhir.rest.api.Constants.PARAM_OFFSET;
import static org.slf4j.LoggerFactory.getLogger;
/**
* This class is essentially just a data clump of offset + count, as well as the ability to convert itself into a standard
* {@link PageRequest} for spring data to use. The reason we don't use PageRequest natively is because it is concerned with `pages` and `counts`,
* but we are using `offset` and `count` which requires some minor translation.
*/
public class MdmPageRequest {
private final int myPage;
private final int myOffset;
private final int myCount;
public MdmPageRequest(@Nullable IPrimitiveType<Integer> theOffset, @Nullable IPrimitiveType<Integer> theCount, int theDefaultPageSize, int theMaximumPageSize) {
myOffset = theOffset == null ? 0 : theOffset.getValue();
myCount = theCount == null ? theDefaultPageSize : Math.min(theCount.getValue(), theMaximumPageSize);
validatePagingParameters(myOffset, myCount);
this.myPage = myOffset / myCount;
}
public MdmPageRequest(@Nullable Integer theOffset, @Nullable Integer theCount, int theDefaultPageSize, int theMaximumPageSize) {
myOffset = theOffset == null ? 0 : theOffset;
myCount = theCount == null ? theDefaultPageSize : Math.min(theCount, theMaximumPageSize);
validatePagingParameters(myOffset, myCount);
this.myPage = myOffset / myCount;
}
private void validatePagingParameters(int theOffset, int theCount) {
String errorMessage = "";
if (theOffset < 0) {
errorMessage += PARAM_OFFSET + " must be greater than or equal to 0. ";
}
if (theCount <= 0 ) {
errorMessage += PARAM_COUNT + " must be greater than 0.";
}
if (StringUtils.isNotEmpty(errorMessage)) {
throw new InvalidRequestException(errorMessage);
}
}
public int getOffset() {
return myOffset;
}
public int getPage() {
return myPage;
}
public int getCount() {
return myCount;
}
public int getNextOffset() {
return myOffset + myCount;
}
public int getPreviousOffset() {
return myOffset - myCount;
}
public PageRequest toPageRequest() {
return PageRequest.of(this.myPage, this.myCount);
}
}

View File

@ -23,17 +23,21 @@ package ca.uhn.fhir.mdm.provider;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.mdm.api.MdmLinkJson;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.mdm.api.paging.MdmPageLinkBuilder;
import ca.uhn.fhir.mdm.api.paging.MdmPageLinkTuple;
import ca.uhn.fhir.mdm.api.paging.MdmPageRequest;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.TransactionLogMessages;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.ParametersUtil;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.springframework.data.domain.Page;
import java.util.stream.Stream;
public abstract class BaseMdmProvider {
@ -92,9 +96,9 @@ public abstract class BaseMdmProvider {
return theString.getValue();
}
protected IBaseParameters parametersFromMdmLinks(Stream<MdmLinkJson> theMdmLinkStream, boolean includeResultAndSource) {
protected IBaseParameters parametersFromMdmLinks(Page<MdmLinkJson> theMdmLinkStream, boolean includeResultAndSource, ServletRequestDetails theServletRequestDetails, MdmPageRequest thePageRequest) {
IBaseParameters retval = ParametersUtil.newInstance(myFhirContext);
addPagingParameters(retval, theMdmLinkStream, theServletRequestDetails, thePageRequest);
theMdmLinkStream.forEach(mdmLink -> {
IBase resultPart = ParametersUtil.addParameterToParameters(myFhirContext, retval, "link");
ParametersUtil.addPartString(myFhirContext, resultPart, "goldenResourceId", mdmLink.getGoldenResourceId());
@ -111,4 +115,17 @@ public abstract class BaseMdmProvider {
return retval;
}
protected void addPagingParameters(IBaseParameters theParameters, Page<MdmLinkJson> theCurrentPage, ServletRequestDetails theServletRequestDetails, MdmPageRequest thePageRequest) {
MdmPageLinkTuple mdmPageLinkTuple = MdmPageLinkBuilder.buildMdmPageLinks(theServletRequestDetails, theCurrentPage, thePageRequest);
if (mdmPageLinkTuple.getPreviousLink().isPresent()) {
ParametersUtil.addParameterToParametersUri(myFhirContext, theParameters, "prev", mdmPageLinkTuple.getPreviousLink().get());
}
ParametersUtil.addParameterToParametersUri(myFhirContext, theParameters, "self", mdmPageLinkTuple.getSelfLink());
if (mdmPageLinkTuple.getNextLink().isPresent()) {
ParametersUtil.addParameterToParametersUri(myFhirContext, theParameters, "next", mdmPageLinkTuple.getNextLink().get());
}
}
}

View File

@ -28,10 +28,14 @@ import ca.uhn.fhir.mdm.api.IMdmSubmitSvc;
import ca.uhn.fhir.mdm.api.MatchedTarget;
import ca.uhn.fhir.mdm.api.MdmConstants;
import ca.uhn.fhir.mdm.api.MdmLinkJson;
import ca.uhn.fhir.mdm.api.paging.MdmPageRequest;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.primitive.IntegerDt;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
@ -49,6 +53,8 @@ import org.hl7.fhir.instance.model.api.IBaseParameters;
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.slf4j.Logger;
import org.springframework.data.domain.Page;
import javax.annotation.Nonnull;
import java.math.BigDecimal;
@ -56,15 +62,22 @@ import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.stream.Stream;
import static ca.uhn.fhir.rest.api.Constants.PARAM_OFFSET;
import static org.slf4j.LoggerFactory.getLogger;
public class MdmProviderDstu3Plus extends BaseMdmProvider {
private static final Logger ourLog = getLogger(MdmProviderDstu3Plus.class);
private final IMdmControllerSvc myMdmControllerSvc;
private final IMdmMatchFinderSvc myMdmMatchFinderSvc;
private final IMdmExpungeSvc myMdmExpungeSvc;
private final IMdmSubmitSvc myMdmSubmitSvc;
public static final int DEFAULT_PAGE_SIZE = 20;
public static final int MAX_PAGE_SIZE = 100;
/**
* Constructor
* <p>
@ -184,28 +197,45 @@ public class MdmProviderDstu3Plus extends BaseMdmProvider {
return retval;
}
@Operation(name = ProviderConstants.MDM_QUERY_LINKS, idempotent = true)
public IBaseParameters queryLinks(@OperationParam(name = ProviderConstants.MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID, min = 0, max = 1, typeName = "string") IPrimitiveType<String> theGoldenResourceId,
@OperationParam(name = ProviderConstants.MDM_QUERY_LINKS_RESOURCE_ID, min = 0, max = 1, typeName = "string") IPrimitiveType<String> theResourceId,
@OperationParam(name = ProviderConstants.MDM_QUERY_LINKS_MATCH_RESULT, min = 0, max = 1, typeName = "string") IPrimitiveType<String> theMatchResult,
@OperationParam(name = ProviderConstants.MDM_QUERY_LINKS_LINK_SOURCE, min = 0, max = 1, typeName = "string") IPrimitiveType<String> theLinkSource,
ServletRequestDetails theRequestDetails) {
@OperationParam(name = ProviderConstants.MDM_QUERY_LINKS_LINK_SOURCE, min = 0, max = 1, typeName = "string")
IPrimitiveType<String> theLinkSource,
Stream<MdmLinkJson> mdmLinkJson = myMdmControllerSvc.queryLinks(extractStringOrNull(theGoldenResourceId),
@Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.")
@OperationParam(name = PARAM_OFFSET, min = 0, max = 1, typeName = "integer")
IPrimitiveType<Integer> theOffset,
@Description(formalDefinition = "Results from this method are returned across multiple pages. This parameter controls the size of those pages.")
@OperationParam(name = Constants.PARAM_COUNT, min = 0, max = 1, typeName = "integer")
IPrimitiveType<Integer> theCount,
ServletRequestDetails theRequestDetails) {
MdmPageRequest mdmPageRequest = new MdmPageRequest(theOffset, theCount, DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE);
Page<MdmLinkJson> mdmLinkJson = myMdmControllerSvc.queryLinks(extractStringOrNull(theGoldenResourceId),
extractStringOrNull(theResourceId), extractStringOrNull(theMatchResult), extractStringOrNull(theLinkSource),
createMdmContext(theRequestDetails, MdmTransactionContext.OperationType.QUERY_LINKS,
getResourceType(ProviderConstants.MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID, theGoldenResourceId))
);
return parametersFromMdmLinks(mdmLinkJson, true);
getResourceType(ProviderConstants.MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID, theGoldenResourceId)), mdmPageRequest);
return parametersFromMdmLinks(mdmLinkJson, true, theRequestDetails, mdmPageRequest);
}
@Operation(name = ProviderConstants.MDM_DUPLICATE_GOLDEN_RESOURCES, idempotent = true)
public IBaseParameters getDuplicateGoldenResources(ServletRequestDetails theRequestDetails) {
Stream<MdmLinkJson> possibleDuplicates = myMdmControllerSvc.getDuplicateGoldenResources(
createMdmContext(theRequestDetails, MdmTransactionContext.OperationType.DUPLICATE_GOLDEN_RESOURCES, (String) null)
);
return parametersFromMdmLinks(possibleDuplicates, false);
public IBaseParameters getDuplicateGoldenResources(
@Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.")
@OperationParam(name = PARAM_OFFSET, min = 0, max = 1, typeName = "integer")
IPrimitiveType<Integer> theOffset,
@Description(formalDefinition = "Results from this method are returned across multiple pages. This parameter controls the size of those pages.")
@OperationParam(name = Constants.PARAM_COUNT, min = 0, max = 1, typeName = "integer")
IPrimitiveType<Integer> theCount,
ServletRequestDetails theRequestDetails) {
MdmPageRequest mdmPageRequest = new MdmPageRequest(theOffset, theCount, DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE);
Page<MdmLinkJson> possibleDuplicates = myMdmControllerSvc.getDuplicateGoldenResources(createMdmContext(theRequestDetails, MdmTransactionContext.OperationType.DUPLICATE_GOLDEN_RESOURCES, (String) null), mdmPageRequest);
return parametersFromMdmLinks(possibleDuplicates, false, theRequestDetails, mdmPageRequest);
}
@Operation(name = ProviderConstants.MDM_NOT_DUPLICATE)
@ -233,7 +263,6 @@ public class MdmProviderDstu3Plus extends BaseMdmProvider {
ServletRequestDetails theRequestDetails) {
String criteria = convertStringTypeToString(theCriteria);
String resourceType = convertStringTypeToString(theResourceType);
long submittedCount;
if (resourceType != null) {
submittedCount = myMdmSubmitSvc.submitSourceResourceTypeToMdm(resourceType, criteria);

View File

@ -26,6 +26,9 @@ import ca.uhn.fhir.mdm.api.IMdmControllerSvc;
import ca.uhn.fhir.mdm.api.IMdmExpungeSvc;
import ca.uhn.fhir.mdm.api.IMdmMatchFinderSvc;
import ca.uhn.fhir.mdm.api.IMdmSubmitSvc;
import ca.uhn.fhir.rest.server.IPagingProvider;
import ca.uhn.fhir.rest.server.IRestfulServerDefaults;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.provider.ResourceProviderFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

View File

@ -0,0 +1,37 @@
package ca.uhn.fhir.mdm.api.paging;
import org.junit.jupiter.api.Test;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.jupiter.api.Assertions.*;
class MdmPageLinkBuilderTest {
@Test
void buildLinkWithExistingParameters() {
//Given
String expected = "http://localhost:8000/$mdm-query-links?sourceResourceId=Patient/123&_offset=1&_count=1";
String baseUrl = "http://localhost:8000/$mdm-query-links?sourceResourceId=Patient/123";
//When
String builtUrl = MdmPageLinkBuilder.buildLinkWithOffsetAndCount(baseUrl, 1, 1);
//Then
assertThat(builtUrl, is(equalTo(expected)));
}
@Test
void buildLinkWithoutExistingParameters() {
//Given
String expected = "http://localhost:8000/$mdm-query-links?_offset=1&_count=1";
String baseUrl = "http://localhost:8000/$mdm-query-links";
//When
String builtUrl = MdmPageLinkBuilder.buildLinkWithOffsetAndCount(baseUrl, 1, 1);
//Then
assertThat(builtUrl, is(equalTo(expected)));
}
}

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -249,6 +249,13 @@ public class RestfulServerUtils {
public static String createLinkSelf(String theServerBase, RequestDetails theRequest) {
return createLinkSelfWithoutGivenParameters(theServerBase, theRequest, null);
}
/**
* This function will create a self link but omit any parameters passed in via the excludedParameterNames list.
*/
public static String createLinkSelfWithoutGivenParameters(String theServerBase, RequestDetails theRequest, List<String> excludedParameterNames) {
StringBuilder b = new StringBuilder();
b.append(theServerBase);
@ -265,21 +272,24 @@ public class RestfulServerUtils {
boolean first = true;
Map<String, String[]> parameters = theRequest.getParameters();
for (String nextParamName : new TreeSet<>(parameters.keySet())) {
for (String nextParamValue : parameters.get(nextParamName)) {
if (first) {
b.append('?');
first = false;
} else {
b.append('&');
if (excludedParameterNames == null || !excludedParameterNames.contains(nextParamName)) {
for (String nextParamValue : parameters.get(nextParamName)) {
if (first) {
b.append('?');
first = false;
} else {
b.append('&');
}
b.append(UrlUtil.escapeUrlParam(nextParamName));
b.append('=');
b.append(UrlUtil.escapeUrlParam(nextParamValue));
}
b.append(UrlUtil.escapeUrlParam(nextParamName));
b.append('=');
b.append(UrlUtil.escapeUrlParam(nextParamValue));
}
}
}
return b.toString();
}
public static String createOffsetPagingLink(BundleLinks theBundleLinks, String requestPath, String tenantId, Integer theOffset, Integer theCount, Map<String, String[]> theRequestParameters) {

View File

@ -1,5 +1,25 @@
package ca.uhn.fhir.rest.server.interceptor.auth;
/*-
* #%L
* HAPI FHIR - Server Framework
* %%
* Copyright (C) 2014 - 2021 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%
*/
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.hl7.fhir.instance.model.api.IBaseResource;

View File

@ -3,8 +3,19 @@ package ca.uhn.fhir.rest.server;
import ca.uhn.fhir.rest.api.PreferHandlingEnum;
import ca.uhn.fhir.rest.api.PreferHeader;
import ca.uhn.fhir.rest.api.PreferReturnEnum;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static ca.uhn.fhir.rest.api.RequestTypeEnum.GET;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.jupiter.api.Assertions.*;
public class RestfulServerUtilsTest{
@ -53,4 +64,37 @@ public class RestfulServerUtilsTest{
assertFalse(header.getRespondAsync());
assertEquals(PreferHandlingEnum.LENIENT, header.getHanding());
}
@Test
public void testCreateSelfLinks() {
//Given
String baseUrl = "http://localhost:8000";
Map<String, String[]> parameters = new HashMap<>();
parameters.put("_format", new String[]{"json"});
parameters.put("_count", new String[]{"10"});
parameters.put("_offset", new String[]{"100"});
List<String> paramsToRemove = Arrays.asList("_count", "_offset");
ServletRequestDetails servletRequestDetails = new ServletRequestDetails();
servletRequestDetails.setFhirServerBase("http://localhost:8000");
servletRequestDetails.setRequestPath("$my-operation");
servletRequestDetails.setRequestType(GET);
servletRequestDetails.setParameters(parameters);
//When
String linkSelf = RestfulServerUtils.createLinkSelf(baseUrl, servletRequestDetails);
//Then
assertThat(linkSelf, is(containsString("http://localhost:8000/$my-operation?")));
assertThat(linkSelf, is(containsString("_format=json")));
assertThat(linkSelf, is(containsString("_count=10")));
assertThat(linkSelf, is(containsString("_offset=100")));
//When
String linkSelfWithoutGivenParameters = RestfulServerUtils.createLinkSelfWithoutGivenParameters(baseUrl, servletRequestDetails, paramsToRemove);
//Then
assertThat(linkSelfWithoutGivenParameters, is(containsString("http://localhost:8000/$my-operation?")));
assertThat(linkSelfWithoutGivenParameters, is(containsString("_format=json")));
}
}

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
</parent>
<artifactId>hapi-fhir-spring-boot-sample-client-apache</artifactId>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
</parent>
<artifactId>hapi-fhir-spring-boot-sample-client-okhttp</artifactId>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
</parent>
<artifactId>hapi-fhir-spring-boot-sample-server-jersey</artifactId>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
</parent>
<artifactId>hapi-fhir-spring-boot-samples</artifactId>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
@ -58,37 +58,37 @@
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu3</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-hl7org-dstu2</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-r4</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-r5</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-dstu2</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-dstu3</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-r4</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<packaging>pom</packaging>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<name>HAPI-FHIR</name>
<description>An open-source implementation of the FHIR specification in Java.</description>
<url>https://hapifhir.io</url>

View File

@ -8,7 +8,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>5.5.0-PRE5-SNAPSHOT</version>
<version>5.5.0-PRE6-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>