Refactor docs

This commit is contained in:
Tadgh 2021-06-30 14:48:13 -04:00
parent 61260dd1b1
commit 8f3cbb0a7f
10 changed files with 193 additions and 29 deletions

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

@ -4,6 +4,51 @@ 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
```
You could use request body
```json
{
"resourceType": "Parameters",
"parameter": [ {
"name": "_offset",
"valueInt": 0
}, {
"name": "_count",
"valueInt": 2
} ]
}
```
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=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"
},...(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 +95,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 +119,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 +128,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 +149,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 +198,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`:
@ -583,3 +693,7 @@ This operation can also be done at the Instance level. When this is the case, th
http://example.com/Patient/123/$mdm-submit
http://example.com/Practitioner/456/$mdm-submit
```

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,7 @@ 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;
@ -31,7 +32,7 @@ public abstract class BaseProviderR4Test extends BaseMdmR4Test {
@Autowired
private MdmSettings myMdmSettings;
@Autowired
private IRestfulServerDefaults myRestfulServerDefaults;
private IPagingProvider myPagingProvider;
private String defaultScript;
@ -45,7 +46,7 @@ public abstract class BaseProviderR4Test extends BaseMdmR4Test {
@BeforeEach
public void before() {
myMdmProvider = new MdmProviderDstu3Plus(myFhirContext, myMdmControllerSvc, myMdmMatchFinderSvc, myMdmExpungeSvc, myMdmSubmitSvc, myRestfulServerDefaults);
myMdmProvider = new MdmProviderDstu3Plus(myFhirContext, myMdmControllerSvc, myMdmMatchFinderSvc, myMdmExpungeSvc, myMdmSubmitSvc, myPagingProvider);
defaultScript = myMdmSettings.getScriptText();
}
@AfterEach

View File

@ -11,12 +11,14 @@ 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;
@ -86,19 +88,38 @@ public class MdmProviderQueryLinkR4Test extends BaseLinkR4Test {
while (true) {
Parameters result = (Parameters) myMdmProvider.queryLinks(null, null, null, myLinkSource, new UnsignedIntType(offset), new UnsignedIntType(count), myRequestDetails);
List<Parameters.ParametersParameterComponent> parameter = result.getParameter();
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);
offset += count;
assertThat(parameter.size(), is(lessThanOrEqualTo(3)));
//We have stopped finding patients.
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
@ -168,7 +189,7 @@ public class MdmProviderQueryLinkR4Test extends BaseLinkR4Test {
@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();
assertThat(list, hasSize(1));
@ -179,7 +200,7 @@ public class MdmProviderQueryLinkR4Test extends BaseLinkR4Test {
@Test
public void testNotDuplicate() {
{
Parameters result = (Parameters) myMdmProvider.getDuplicateGoldenResources(myRequestDetails);
Parameters result = (Parameters) myMdmProvider.getDuplicateGoldenResources(new UnsignedIntType(0), new UnsignedIntType(10),myRequestDetails);
List<Parameters.ParametersParameterComponent> list = result.getParameter();
assertThat(list, hasSize(1));
}
@ -190,7 +211,7 @@ public class MdmProviderQueryLinkR4Test extends BaseLinkR4Test {
assertTrue(((BooleanType) (result.getParameterFirstRep().getValue())).booleanValue());
}
Parameters result = (Parameters) myMdmProvider.getDuplicateGoldenResources(myRequestDetails);
Parameters result = (Parameters) myMdmProvider.getDuplicateGoldenResources(new UnsignedIntType(0), new UnsignedIntType(10),myRequestDetails);
List<Parameters.ParametersParameterComponent> list = result.getParameter();
assertThat(list, hasSize(0));
}

View File

@ -1,5 +1,6 @@
package ca.uhn.fhir.mdm.api.paging;
import ca.uhn.fhir.rest.server.IPagingProvider;
import ca.uhn.fhir.rest.server.IRestfulServerDefaults;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import org.apache.commons.lang3.StringUtils;
@ -17,11 +18,17 @@ public class MdmPageRequest {
private int myPage;
private int myOffset;
private int myCount;
private IRestfulServerDefaults myRestfulServerDefaults;
private IPagingProvider myPagingProvider;
public MdmPageRequest(UnsignedIntType theOffset, UnsignedIntType theCount, IRestfulServerDefaults theDefaults) {
public MdmPageRequest(UnsignedIntType theOffset, UnsignedIntType theCount, IPagingProvider thePagingProvider) {
myPagingProvider = thePagingProvider;
myOffset = theOffset == null ? 0 : theOffset.getValue();
myCount = theCount == null ? theDefaults.getDefaultPageSize() : theCount.getValue();
myCount = theCount == null ? myPagingProvider.getDefaultPageSize() : theCount.getValue();
if (myCount > myPagingProvider.getMaximumPageSize()) {
ourLog.debug("Reducing count {} to paging provider's maximum of {}", theCount, myPagingProvider.getMaximumPageSize());
myCount = myPagingProvider.getMaximumPageSize();
}
validatePagingParameters(myOffset, myCount);
this.myPage = myOffset / myCount;
@ -40,9 +47,6 @@ public class MdmPageRequest {
if (theCount <= 0 ) {
errorMessage += PARAM_COUNT + " must be greater than 0.";
}
if (myRestfulServerDefaults.getMaximumPageSize() != null && theCount > myRestfulServerDefaults.getMaximumPageSize() ) {
ourLog.debug("Shrinking page size down to {}, as this is the maximum allowed.", myRestfulServerDefaults.getMaximumPageSize());
}
if (StringUtils.isNotEmpty(errorMessage)) {
throw new InvalidRequestException(errorMessage);
}

View File

@ -122,13 +122,15 @@ public abstract class BaseMdmProvider {
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());
}
if (mdmPageLinkTuple.getPreviousLink().isPresent()) {
ParametersUtil.addParameterToParametersUri(myFhirContext, theParameters, "prev", mdmPageLinkTuple.getPreviousLink().get());
}
}
}

View File

@ -36,6 +36,7 @@ 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.IPagingProvider;
import ca.uhn.fhir.rest.server.IRestfulServerDefaults;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
@ -75,7 +76,7 @@ public class MdmProviderDstu3Plus extends BaseMdmProvider {
private final IMdmMatchFinderSvc myMdmMatchFinderSvc;
private final IMdmExpungeSvc myMdmExpungeSvc;
private final IMdmSubmitSvc myMdmSubmitSvc;
private final IRestfulServerDefaults myRestfulServerDefaults;
private final IPagingProvider myPagingProvider;
/**
* Constructor
@ -83,13 +84,13 @@ public class MdmProviderDstu3Plus extends BaseMdmProvider {
* Note that this is not a spring bean. Any necessary injections should
* happen in the constructor
*/
public MdmProviderDstu3Plus(FhirContext theFhirContext, IMdmControllerSvc theMdmControllerSvc, IMdmMatchFinderSvc theMdmMatchFinderSvc, IMdmExpungeSvc theMdmExpungeSvc, IMdmSubmitSvc theMdmSubmitSvc, IRestfulServerDefaults theRestfulServerDefaults) {
public MdmProviderDstu3Plus(FhirContext theFhirContext, IMdmControllerSvc theMdmControllerSvc, IMdmMatchFinderSvc theMdmMatchFinderSvc, IMdmExpungeSvc theMdmExpungeSvc, IMdmSubmitSvc theMdmSubmitSvc, IPagingProvider thePagingProvider) {
super(theFhirContext);
myMdmControllerSvc = theMdmControllerSvc;
myMdmMatchFinderSvc = theMdmMatchFinderSvc;
myMdmExpungeSvc = theMdmExpungeSvc;
myMdmSubmitSvc = theMdmSubmitSvc;
myRestfulServerDefaults = theRestfulServerDefaults;
myPagingProvider = thePagingProvider;
}
@Operation(name = ProviderConstants.EMPI_MATCH, typeName = "Patient")
@ -213,7 +214,7 @@ public class MdmProviderDstu3Plus extends BaseMdmProvider {
UnsignedIntType theCount,
ServletRequestDetails theRequestDetails) {
MdmPageRequest mdmPageRequest = new MdmPageRequest(theOffset, theCount, myRestfulServerDefaults);
MdmPageRequest mdmPageRequest = new MdmPageRequest(theOffset, theCount, myPagingProvider);
Page<MdmLinkJson> mdmLinkJson = myMdmControllerSvc.queryLinks(extractStringOrNull(theGoldenResourceId),
extractStringOrNull(theResourceId), extractStringOrNull(theMatchResult), extractStringOrNull(theLinkSource),
createMdmContext(theRequestDetails, MdmTransactionContext.OperationType.QUERY_LINKS,
@ -233,7 +234,7 @@ public class MdmProviderDstu3Plus extends BaseMdmProvider {
UnsignedIntType theCount,
ServletRequestDetails theRequestDetails) {
MdmPageRequest mdmPageRequest = new MdmPageRequest(theOffset, theCount, myRestfulServerDefaults);
MdmPageRequest mdmPageRequest = new MdmPageRequest(theOffset, theCount, myPagingProvider);
Page<MdmLinkJson> possibleDuplicates = myMdmControllerSvc.getDuplicateGoldenResources(createMdmContext(theRequestDetails, MdmTransactionContext.OperationType.DUPLICATE_GOLDEN_RESOURCES, (String) null), mdmPageRequest);

View File

@ -26,7 +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;
@ -48,7 +50,7 @@ public class MdmProviderLoader {
@Autowired
private IMdmSubmitSvc myMdmSubmitSvc;
@Autowired
private IRestfulServerDefaults myRestfulServerDefaults;
private IPagingProvider myPagingProvider;
private BaseMdmProvider myMdmProvider;
@ -57,7 +59,7 @@ public class MdmProviderLoader {
case DSTU3:
case R4:
myResourceProviderFactory.addSupplier(() -> {
myMdmProvider = new MdmProviderDstu3Plus(myFhirContext, myMdmControllerSvc, myMdmMatchFinderSvc, myMdmExpungeSvc, myMdmSubmitSvc, myRestfulServerDefaults);
myMdmProvider = new MdmProviderDstu3Plus(myFhirContext, myMdmControllerSvc, myMdmMatchFinderSvc, myMdmExpungeSvc, myMdmSubmitSvc, myPagingProvider);
return myMdmProvider;
});
break;