added IPagingProvider.canSearchByOffset() (#2281)

* init rev

* added IPagingProvider.canSearchByOffset()
consolidated parameters in bundle link methods

* fix refactoring error

* add @Nonnull to BundleLinks parameter

* null check

* fix test

* cleanup

* fix refactoring

* final cleanup

* review feedback

* review feedback

* review feedback
This commit is contained in:
Ken Stevens 2021-01-11 20:42:41 -05:00 committed by GitHub
parent ee7b19fe3c
commit 520400d6ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 370 additions and 185 deletions

View File

@ -116,6 +116,12 @@
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<!-- @Nonnull annotation -->
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.awaitility</groupId> <groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId> <artifactId>awaitility</artifactId>

View File

@ -0,0 +1,62 @@
package ca.uhn.fhir.rest.api;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
public class BundleLinks {
public final String serverBase;
public final boolean prettyPrint;
public final BundleTypeEnum bundleType;
private final List<Include> includes;
private String self;
private String next;
private String prev;
public BundleLinks(String theServerBase, Set<Include> theIncludes, boolean thePrettyPrint, BundleTypeEnum theBundleType) {
serverBase = theServerBase;
includes = theIncludes == null ? null : new ArrayList<>(theIncludes);
prettyPrint = thePrettyPrint;
bundleType = theBundleType;
}
public String getSelf() {
return self;
}
public BundleLinks setSelf(String theSelf) {
self = theSelf;
return this;
}
public String getNext() {
return next;
}
public BundleLinks setNext(String theNext) {
next = theNext;
return this;
}
public String getPrev() {
return prev;
}
public BundleLinks setPrev(String thePrev) {
prev = thePrev;
return this;
}
public Collection<Include> getIncludes() {
if (includes == null) {
return null;
}
return Collections.unmodifiableList(includes);
}
}

View File

@ -26,6 +26,7 @@ import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.instance.model.api.IPrimitiveType;
import javax.annotation.Nonnull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
@ -38,17 +39,17 @@ public interface IVersionSpecificBundleFactory {
void addResourcesToBundle(List<IBaseResource> theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set<Include> theIncludes); void addResourcesToBundle(List<IBaseResource> theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set<Include> theIncludes);
void addRootPropertiesToBundle(String theId, String theServerBase, String theLinkSelf, String theLinkPrev, String theLinkNext, Integer theTotalResults, BundleTypeEnum theBundleType, IPrimitiveType<Date> theLastUpdated); void addRootPropertiesToBundle(String theId, @Nonnull BundleLinks theBundleLinks, Integer theTotalResults, IPrimitiveType<Date> theLastUpdated);
IBaseResource getResourceBundle(); IBaseResource getResourceBundle();
/** /**
* @deprecated This was deprecated in HAPI FHIR 4.1.0 as it provides duplicate functionality to the {@link #addRootPropertiesToBundle(String, String, String, String, String, Integer, BundleTypeEnum, IPrimitiveType)} * @deprecated This was deprecated in HAPI FHIR 4.1.0 as it provides duplicate functionality to the {@link #addRootPropertiesToBundle(String, BundleLinks, Integer, IPrimitiveType<Date>)}
* and {@link #addResourcesToBundle(List, BundleTypeEnum, String, BundleInclusionRule, Set)} methods * and {@link #addResourcesToBundle(List, BundleTypeEnum, String, BundleInclusionRule, Set)} methods
*/ */
@Deprecated @Deprecated
default void initializeBundleFromResourceList(String theAuthor, List<? extends IBaseResource> theResult, String theServerBase, String theCompleteUrl, int theTotalResults, BundleTypeEnum theBundleType) { default void initializeBundleFromResourceList(String theAuthor, List<? extends IBaseResource> theResult, String theServerBase, String theCompleteUrl, int theTotalResults, BundleTypeEnum theBundleType) {
addRootPropertiesToBundle(null, null, null, null, null, theResult.size(), theBundleType, null); addTotalResultsToBundle(theResult.size(), theBundleType);
addResourcesToBundle(new ArrayList<>(theResult), theBundleType, null, null, null); addResourcesToBundle(new ArrayList<>(theResult), theBundleType, null, null, null);
} }
@ -56,4 +57,5 @@ public interface IVersionSpecificBundleFactory {
List<IBaseResource> toListOfResources(); List<IBaseResource> toListOfResources();
void addTotalResultsToBundle(Integer theTotalResults, BundleTypeEnum theBundleType);
} }

View File

@ -23,6 +23,7 @@ package ca.uhn.fhir.cli;
import ca.uhn.fhir.context.BaseRuntimeChildDefinition; import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition; import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu2.resource.Bundle; import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry; import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
@ -44,7 +45,6 @@ import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options; import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException; import org.apache.commons.cli.ParseException;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator; import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator;
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent; import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.dstu3.model.Bundle.BundleType; import org.hl7.fhir.dstu3.model.Bundle.BundleType;
@ -720,7 +720,7 @@ public class ExampleDataUploader extends BaseCommand {
ourLog.info("About to upload {} examples in a transaction, {} remaining", subResourceList.size(), resources.size()); ourLog.info("About to upload {} examples in a transaction, {} remaining", subResourceList.size(), resources.size());
IVersionSpecificBundleFactory bundleFactory = ctx.newBundleFactory(); IVersionSpecificBundleFactory bundleFactory = ctx.newBundleFactory();
bundleFactory.addRootPropertiesToBundle(null, null, null, null, null, subResourceList.size(), BundleTypeEnum.TRANSACTION, null); bundleFactory.addTotalResultsToBundle(subResourceList.size(), BundleTypeEnum.TRANSACTION);
bundleFactory.addResourcesToBundle(new ArrayList<>(subResourceList), BundleTypeEnum.TRANSACTION, null, null, null); bundleFactory.addResourcesToBundle(new ArrayList<>(subResourceList), BundleTypeEnum.TRANSACTION, null, null, null);
IBaseResource subBundle = bundleFactory.getResourceBundle(); IBaseResource subBundle = bundleFactory.getResourceBundle();

View File

@ -1,44 +1,27 @@
package ca.uhn.fhir.rest.client.method; package ca.uhn.fhir.rest.client.method;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
/*
* #%L
* HAPI FHIR - Client 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 java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.*;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.model.valueset.BundleTypeEnum; import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.*; import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.api.IHttpClient; import ca.uhn.fhir.rest.client.api.IHttpClient;
import ca.uhn.fhir.rest.client.api.IHttpRequest; import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.impl.BaseHttpClientInvocation; import ca.uhn.fhir.rest.client.impl.BaseHttpClientInvocation;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IBaseBinary;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
/** /**
* @author James Agnew * @author James Agnew
@ -173,7 +156,7 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
parser.setOmitResourceId(myOmitResourceId); parser.setOmitResourceId(myOmitResourceId);
if (myResources != null) { if (myResources != null) {
IVersionSpecificBundleFactory bundleFactory = getContext().newBundleFactory(); IVersionSpecificBundleFactory bundleFactory = getContext().newBundleFactory();
bundleFactory.addRootPropertiesToBundle(null, null, null, null, null, myResources.size(), myBundleType, null); bundleFactory.addTotalResultsToBundle(myResources.size(), myBundleType);
bundleFactory.addResourcesToBundle(myResources, myBundleType, null, null, null); bundleFactory.addResourcesToBundle(myResources, myBundleType, null, null, null);
IBaseResource bundleRes = bundleFactory.getResourceBundle(); IBaseResource bundleRes = bundleFactory.getResourceBundle();
return parser.encodeResourceToString(bundleRes); return parser.encodeResourceToString(bundleRes);

View File

@ -44,7 +44,7 @@ public class IncludesExamples {
FhirContext ctx = FhirContext.forDstu2(); FhirContext ctx = FhirContext.forDstu2();
R4BundleFactory bf = new R4BundleFactory(ctx); R4BundleFactory bf = new R4BundleFactory(ctx);
bf.addRootPropertiesToBundle(null, null, null, null, null, resources.size(), BundleTypeEnum.SEARCHSET, null); bf.addTotalResultsToBundle(resources.size(), BundleTypeEnum.SEARCHSET);
bf.addResourcesToBundle(new ArrayList<>(resources), BundleTypeEnum.SEARCHSET, null, null, null); bf.addResourcesToBundle(new ArrayList<>(resources), BundleTypeEnum.SEARCHSET, null, null, null);
IBaseResource b = bf.getResourceBundle(); IBaseResource b = bf.getResourceBundle();

View File

@ -25,13 +25,12 @@ import ca.uhn.fhir.jpa.dao.SearchBuilderFactory;
import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.BasePagingProvider; import ca.uhn.fhir.rest.server.BasePagingProvider;
import ca.uhn.fhir.rest.server.IPagingProvider;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
// Note: this class is not annotated with @Service because we want to // Note: this class is not annotated with @Service because we want to
// explicitly define it in BaseConfig.java. This is done so that // explicitly define it in BaseConfig.java. This is done so that
// implementors can override if they want to. // implementors can override if they want to.
public class DatabaseBackedPagingProvider extends BasePagingProvider implements IPagingProvider { public class DatabaseBackedPagingProvider extends BasePagingProvider {
@Autowired @Autowired
private DaoRegistry myDaoRegistry; private DaoRegistry myDaoRegistry;

View File

@ -33,4 +33,7 @@ public interface IRestfulServer<T extends RequestDetails> extends IRestfulServer
PreferReturnEnum getDefaultPreferReturn(); PreferReturnEnum getDefaultPreferReturn();
default boolean canStoreSearchResults() {
return getPagingProvider() != null && getPagingProvider().canStoreSearchResults();
}
} }

View File

@ -27,10 +27,10 @@ import org.apache.commons.lang3.Validate;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.UUID; import java.util.UUID;
public class FifoMemoryPagingProvider extends BasePagingProvider implements IPagingProvider { public class FifoMemoryPagingProvider extends BasePagingProvider {
private LinkedHashMap<String, IBundleProvider> myBundleProviders; private final LinkedHashMap<String, IBundleProvider> myBundleProviders;
private int mySize; private final int mySize;
public FifoMemoryPagingProvider(int theSize) { public FifoMemoryPagingProvider(int theSize) {
Validate.isTrue(theSize > 0, "theSize must be greater than 0"); Validate.isTrue(theSize > 0, "theSize must be greater than 0");

View File

@ -28,10 +28,23 @@ import javax.annotation.Nullable;
public interface IPagingProvider { public interface IPagingProvider {
/**
* if no _count parameter is provided, use this for the page size
*/
int getDefaultPageSize(); int getDefaultPageSize();
/**
* if the _count parameter is larger than this value, reduce it to this value
*/
int getMaximumPageSize(); int getMaximumPageSize();
/**
* @return true if the paging provider is able to store search results.
*/
default boolean canStoreSearchResults() {
return true;
}
/** /**
* Retrieve a result list by Search ID * Retrieve a result list by Search ID
* *

View File

@ -28,8 +28,8 @@ import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.BundleLinks;
import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.DeleteCascadeModeEnum; import ca.uhn.fhir.rest.api.DeleteCascadeModeEnum;
import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.EncodingEnum;
@ -64,7 +64,19 @@ import javax.annotation.Nullable;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import java.io.IOException; import java.io.IOException;
import java.io.Writer; import java.io.Writer;
import java.util.*; import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.EnumSet;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -269,9 +281,9 @@ public class RestfulServerUtils {
return b.toString(); return b.toString();
} }
public static String createOffsetPagingLink(String theServerBase, String requestPath, String tenantId, Integer theOffset, Integer theCount, Map<String, String[]> theRequestParameters) { public static String createOffsetPagingLink(BundleLinks theBundleLinks, String requestPath, String tenantId, Integer theOffset, Integer theCount, Map<String, String[]> theRequestParameters) {
StringBuilder b = new StringBuilder(); StringBuilder b = new StringBuilder();
b.append(theServerBase); b.append(theBundleLinks.serverBase);
if (isNotBlank(requestPath)) { if (isNotBlank(requestPath)) {
b.append('/'); b.append('/');
@ -304,20 +316,17 @@ public class RestfulServerUtils {
return b.toString(); return b.toString();
} }
public static String createPagingLink(Set<Include> theIncludes, RequestDetails theRequestDetails, String theSearchId, int theOffset, int theCount, Map<String, String[]> theRequestParameters, boolean thePrettyPrint, public static String createPagingLink(BundleLinks theBundleLinks, RequestDetails theRequestDetails, String theSearchId, int theOffset, int theCount, Map<String, String[]> theRequestParameters) {
BundleTypeEnum theBundleType) { return createPagingLink(theBundleLinks, theRequestDetails, theSearchId, theOffset, theCount, theRequestParameters, null);
return createPagingLink(theIncludes, theRequestDetails, theSearchId, theOffset, theCount, theRequestParameters, thePrettyPrint,
theBundleType, null);
} }
public static String createPagingLink(Set<Include> theIncludes, RequestDetails theRequestDetails, String theSearchId, String thePageId, Map<String, String[]> theRequestParameters, boolean thePrettyPrint, public static String createPagingLink(BundleLinks theBundleLinks, RequestDetails theRequestDetails, String theSearchId, String thePageId, Map<String, String[]> theRequestParameters) {
BundleTypeEnum theBundleType) { return createPagingLink(theBundleLinks, theRequestDetails, theSearchId, null, null, theRequestParameters,
return createPagingLink(theIncludes, theRequestDetails, theSearchId, null, null, theRequestParameters, thePrettyPrint, thePageId);
theBundleType, thePageId);
} }
private static String createPagingLink(Set<Include> theIncludes, RequestDetails theRequestDetails, String theSearchId, Integer theOffset, Integer theCount, Map<String, String[]> theRequestParameters, boolean thePrettyPrint, private static String createPagingLink(BundleLinks theBundleLinks, RequestDetails theRequestDetails, String theSearchId, Integer theOffset, Integer theCount, Map<String, String[]> theRequestParameters,
BundleTypeEnum theBundleType, String thePageId) { String thePageId) {
String serverBase = theRequestDetails.getFhirServerBase(); String serverBase = theRequestDetails.getFhirServerBase();
@ -355,15 +364,15 @@ public class RestfulServerUtils {
format = replace(format, " ", "+"); format = replace(format, " ", "+");
b.append(UrlUtil.escapeUrlParam(format)); b.append(UrlUtil.escapeUrlParam(format));
} }
if (thePrettyPrint) { if (theBundleLinks.prettyPrint) {
b.append('&'); b.append('&');
b.append(Constants.PARAM_PRETTY); b.append(Constants.PARAM_PRETTY);
b.append('='); b.append('=');
b.append(Constants.PARAM_PRETTY_VALUE_TRUE); b.append(Constants.PARAM_PRETTY_VALUE_TRUE);
} }
if (theIncludes != null) { if (theBundleLinks.getIncludes() != null) {
for (Include nextInclude : theIncludes) { for (Include nextInclude : theBundleLinks.getIncludes()) {
if (isNotBlank(nextInclude.getValue())) { if (isNotBlank(nextInclude.getValue())) {
b.append('&'); b.append('&');
b.append(Constants.PARAM_INCLUDE); b.append(Constants.PARAM_INCLUDE);
@ -373,11 +382,11 @@ public class RestfulServerUtils {
} }
} }
if (theBundleType != null) { if (theBundleLinks.bundleType != null) {
b.append('&'); b.append('&');
b.append(Constants.PARAM_BUNDLETYPE); b.append(Constants.PARAM_BUNDLETYPE);
b.append('='); b.append('=');
b.append(theBundleType.getCode()); b.append(theBundleLinks.bundleType.getCode());
} }
// _elements // _elements

View File

@ -7,9 +7,13 @@ import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import ca.uhn.fhir.model.valueset.BundleTypeEnum; import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.rest.api.*; import ca.uhn.fhir.rest.api.BundleLinks;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.SummaryEnum;
import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.api.server.IRestfulServer; import ca.uhn.fhir.rest.api.server.IRestfulServer;
import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.RequestDetails;
@ -23,7 +27,6 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.ReflectionUtil; import ca.uhn.fhir.util.ReflectionUtil;
import ca.uhn.fhir.util.UrlUtil;
import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
@ -34,7 +37,13 @@ import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.*; import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank;
@ -124,7 +133,7 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
List<IBaseResource> resourceList; List<IBaseResource> resourceList;
Integer numTotalResults = theResult.size(); Integer numTotalResults = theResult.size();
if (requestOffset != null || theServer.getPagingProvider() == null) { if (requestOffset != null || !theServer.canStoreSearchResults()) {
if (theLimit != null) { if (theLimit != null) {
numToReturn = theLimit; numToReturn = theLimit;
} else { } else {
@ -204,31 +213,33 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
} }
} }
String serverBase = theRequest.getFhirServerBase(); BundleLinks links = new BundleLinks(theRequest.getFhirServerBase(), theIncludes, RestfulServerUtils.prettyPrintResponse(theServer, theRequest), theBundleType);
boolean prettyPrint = RestfulServerUtils.prettyPrintResponse(theServer, theRequest); links.setSelf(theLinkSelf);
String linkPrev = null; if (requestOffset != null || !theServer.canStoreSearchResults()) {
String linkNext = null; int offset = requestOffset != null ? requestOffset : 0;
if (theServer.getPagingProvider() == null || requestOffset != null) {
int myOffset = requestOffset != null ? requestOffset : 0;
// Paging without caching // Paging without caching
// We're doing requestOffset pages // We're doing requestOffset pages
if (numTotalResults == null || myOffset + numToReturn < numTotalResults) { int requestedToReturn = numToReturn;
linkNext = (RestfulServerUtils.createOffsetPagingLink(serverBase, theRequest.getRequestPath(), theRequest.getTenantId(), myOffset + numToReturn, numToReturn, theRequest.getParameters())); if (theServer.getPagingProvider() == null) {
// There is no paging provider at all, so assume we're querying up to all the results we need every time
requestedToReturn += offset;
} }
if (myOffset > 0) { if (numTotalResults == null || requestedToReturn < numTotalResults) {
int start = Math.max(0, myOffset - numToReturn); links.setNext(RestfulServerUtils.createOffsetPagingLink(links, theRequest.getRequestPath(), theRequest.getTenantId(), offset + numToReturn, numToReturn, theRequest.getParameters()));
linkPrev = RestfulServerUtils.createOffsetPagingLink(serverBase, theRequest.getRequestPath(), theRequest.getTenantId(), start, numToReturn, theRequest.getParameters()); }
if (offset > 0) {
int start = Math.max(0, offset - numToReturn);
links.setPrev(RestfulServerUtils.createOffsetPagingLink(links, theRequest.getRequestPath(), theRequest.getTenantId(), start, numToReturn, theRequest.getParameters()));
} }
} else if (isNotBlank(theResult.getCurrentPageId())) { } else if (isNotBlank(theResult.getCurrentPageId())) {
// We're doing named pages // We're doing named pages
searchId = theResult.getUuid(); searchId = theResult.getUuid();
if (isNotBlank(theResult.getNextPageId())) { if (isNotBlank(theResult.getNextPageId())) {
linkNext = RestfulServerUtils.createPagingLink(theIncludes, theRequest, searchId, theResult.getNextPageId(), theRequest.getParameters(), prettyPrint, theBundleType); links.setNext(RestfulServerUtils.createPagingLink(links, theRequest, searchId, theResult.getNextPageId(), theRequest.getParameters()));
} }
if (isNotBlank(theResult.getPreviousPageId())) { if (isNotBlank(theResult.getPreviousPageId())) {
linkPrev = RestfulServerUtils.createPagingLink(theIncludes, theRequest, searchId, theResult.getPreviousPageId(), theRequest.getParameters(), prettyPrint, theBundleType); links.setPrev(RestfulServerUtils.createPagingLink(links, theRequest, searchId, theResult.getPreviousPageId(), theRequest.getParameters()));
} }
} else if (searchId != null) { } else if (searchId != null) {
/* /*
@ -239,24 +250,17 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
*/ */
if (resourceList.size() > 0) { if (resourceList.size() > 0) {
if (numTotalResults == null || theOffset + numToReturn < numTotalResults) { if (numTotalResults == null || theOffset + numToReturn < numTotalResults) {
linkNext = (RestfulServerUtils.createPagingLink(theIncludes, theRequest, searchId, theOffset + numToReturn, numToReturn, theRequest.getParameters(), prettyPrint, theBundleType)); links.setNext((RestfulServerUtils.createPagingLink(links, theRequest, searchId, theOffset + numToReturn, numToReturn, theRequest.getParameters())));
} }
if (theOffset > 0) { if (theOffset > 0) {
int start = Math.max(0, theOffset - numToReturn); int start = Math.max(0, theOffset - numToReturn);
linkPrev = RestfulServerUtils.createPagingLink(theIncludes, theRequest, searchId, start, numToReturn, theRequest.getParameters(), prettyPrint, theBundleType); links.setPrev(RestfulServerUtils.createPagingLink(links, theRequest, searchId, start, numToReturn, theRequest.getParameters()));
} }
} }
} }
bundleFactory.addRootPropertiesToBundle(theResult.getUuid(), serverBase, theLinkSelf, linkPrev, linkNext, theResult.size(), theBundleType, theResult.getPublished()); bundleFactory.addRootPropertiesToBundle(theResult.getUuid(), links, theResult.size(), theResult.getPublished());
bundleFactory.addResourcesToBundle(new ArrayList<>(resourceList), theBundleType, serverBase, theServer.getBundleInclusionRule(), theIncludes); bundleFactory.addResourcesToBundle(new ArrayList<>(resourceList), theBundleType, links.serverBase, theServer.getBundleInclusionRule(), theIncludes);
if (theServer.getPagingProvider() != null) {
int limit;
limit = theLimit != null ? theLimit : theServer.getPagingProvider().getDefaultPageSize();
limit = Math.min(limit, theServer.getPagingProvider().getMaximumPageSize());
}
return bundleFactory.getResourceBundle(); return bundleFactory.getResourceBundle();
@ -280,8 +284,9 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
/* /*
* Figure out the self-link for this request * Figure out the self-link for this request
*/ */
String serverBase = theRequest.getServerBaseForRequest();
String linkSelf = RestfulServerUtils.createLinkSelf(theRequest.getFhirServerBase(), theRequest); BundleLinks bundleLinks = new BundleLinks(theRequest.getServerBaseForRequest(), null, RestfulServerUtils.prettyPrintResponse(theServer, theRequest), getResponseBundleType());
bundleLinks.setSelf(RestfulServerUtils.createLinkSelf(theRequest.getFhirServerBase(), theRequest));
if (getMethodReturnType() == MethodReturnTypeEnum.BUNDLE_RESOURCE) { if (getMethodReturnType() == MethodReturnTypeEnum.BUNDLE_RESOURCE) {
IBaseResource resource; IBaseResource resource;
@ -300,7 +305,7 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
*/ */
IVersionSpecificBundleFactory bundleFactory = theServer.getFhirContext().newBundleFactory(); IVersionSpecificBundleFactory bundleFactory = theServer.getFhirContext().newBundleFactory();
bundleFactory.initializeWithBundleResource(resource); bundleFactory.initializeWithBundleResource(resource);
bundleFactory.addRootPropertiesToBundle(null, theRequest.getFhirServerBase(), linkSelf, null, null, count, getResponseBundleType(), lastUpdated); bundleFactory.addRootPropertiesToBundle(null, bundleLinks, count, lastUpdated);
responseObject = resource; responseObject = resource;
} else { } else {
@ -327,7 +332,7 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
ResponseEncoding responseEncoding = RestfulServerUtils.determineResponseEncodingNoDefault(theRequest, theServer.getDefaultResponseEncoding()); ResponseEncoding responseEncoding = RestfulServerUtils.determineResponseEncodingNoDefault(theRequest, theServer.getDefaultResponseEncoding());
EncodingEnum linkEncoding = theRequest.getParameters().containsKey(Constants.PARAM_FORMAT) && responseEncoding != null ? responseEncoding.getEncoding() : null; EncodingEnum linkEncoding = theRequest.getParameters().containsKey(Constants.PARAM_FORMAT) && responseEncoding != null ? responseEncoding.getEncoding() : null;
responseObject = createBundleFromBundleProvider(theServer, theRequest, count, linkSelf, includes, result, start, getResponseBundleType(), linkEncoding, null); responseObject = createBundleFromBundleProvider(theServer, theRequest, count, RestfulServerUtils.createLinkSelf(theRequest.getFhirServerBase(), theRequest), includes, result, start, getResponseBundleType(), linkEncoding, null);
} }
break; break;
} }

View File

@ -86,7 +86,7 @@ public class PageMethodBinding extends BaseResourceReturningMethodBinding {
Integer offsetI; Integer offsetI;
int start = 0; int start = 0;
IBundleProvider resultList; IBundleProvider bundleProvider;
String pageId = null; String pageId = null;
String[] pageIdParams = theRequest.getParameters().get(Constants.PARAM_PAGEID); String[] pageIdParams = theRequest.getParameters().get(Constants.PARAM_PAGEID);
@ -101,21 +101,21 @@ public class PageMethodBinding extends BaseResourceReturningMethodBinding {
if (pageId != null) { if (pageId != null) {
// This is a page request by Search ID and Page ID // This is a page request by Search ID and Page ID
resultList = pagingProvider.retrieveResultList(theRequest, thePagingAction, pageId); bundleProvider = pagingProvider.retrieveResultList(theRequest, thePagingAction, pageId);
validateHaveBundleProvider(thePagingAction, resultList); validateHaveBundleProvider(thePagingAction, bundleProvider);
} else { } else {
// This is a page request by Search ID and Offset // This is a page request by Search ID and Offset
resultList = pagingProvider.retrieveResultList(theRequest, thePagingAction); bundleProvider = pagingProvider.retrieveResultList(theRequest, thePagingAction);
validateHaveBundleProvider(thePagingAction, resultList); validateHaveBundleProvider(thePagingAction, bundleProvider);
offsetI = RestfulServerUtils.tryToExtractNamedParameter(theRequest, Constants.PARAM_PAGINGOFFSET); offsetI = RestfulServerUtils.tryToExtractNamedParameter(theRequest, Constants.PARAM_PAGINGOFFSET);
if (offsetI == null || offsetI < 0) { if (offsetI == null || offsetI < 0) {
offsetI = 0; offsetI = 0;
} }
Integer totalNum = resultList.size(); Integer totalNum = bundleProvider.size();
start = offsetI; start = offsetI;
if (totalNum != null) { if (totalNum != null) {
start = Math.min(start, totalNum); start = Math.min(start, totalNum);
@ -155,7 +155,7 @@ public class PageMethodBinding extends BaseResourceReturningMethodBinding {
count = pagingProvider.getMaximumPageSize(); count = pagingProvider.getMaximumPageSize();
} }
return createBundleFromBundleProvider(theServer, theRequest, count, linkSelf, includes, resultList, start, bundleType, encodingEnum, thePagingAction); return createBundleFromBundleProvider(theServer, theRequest, count, linkSelf, includes, bundleProvider, start, bundleType, encodingEnum, thePagingAction);
} }
private void validateHaveBundleProvider(String thePagingAction, IBundleProvider theBundleProvider) { private void validateHaveBundleProvider(String thePagingAction, IBundleProvider theBundleProvider) {

View File

@ -25,6 +25,7 @@ import ca.uhn.fhir.context.api.BundleInclusionRule;
import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.valueset.BundleTypeEnum; import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.rest.api.BundleLinks;
import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory; import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory;
import ca.uhn.fhir.util.ResourceReferenceInfo; import ca.uhn.fhir.util.ResourceReferenceInfo;
@ -35,9 +36,18 @@ import org.hl7.fhir.dstu2016may.model.Bundle.SearchEntryMode;
import org.hl7.fhir.dstu2016may.model.DomainResource; import org.hl7.fhir.dstu2016may.model.DomainResource;
import org.hl7.fhir.dstu2016may.model.IdType; import org.hl7.fhir.dstu2016may.model.IdType;
import org.hl7.fhir.dstu2016may.model.Resource; import org.hl7.fhir.dstu2016may.model.Resource;
import org.hl7.fhir.instance.model.api.*; import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import java.util.*; import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank;
@ -147,31 +157,39 @@ public class Dstu2_1BundleFactory implements IVersionSpecificBundleFactory {
} }
@Override @Override
public void addRootPropertiesToBundle(String theId, String theServerBase, String theLinkSelf, String theLinkPrev, String theLinkNext, Integer theTotalResults, BundleTypeEnum theBundleType, public void addRootPropertiesToBundle(String theId, @Nonnull BundleLinks theBundleLinks, Integer theTotalResults,
IPrimitiveType<Date> theLastUpdated) { IPrimitiveType<Date> theLastUpdated) {
ensureBundle(); ensureBundle();
myBase = theServerBase; myBase = theBundleLinks.serverBase;
if (myBundle.getIdElement().isEmpty()) { if (myBundle.getIdElement().isEmpty()) {
myBundle.setId(theId); myBundle.setId(theId);
} }
if (myBundle.getIdElement().isEmpty()) {
myBundle.setId(UUID.randomUUID().toString());
}
if (myBundle.getMeta().getLastUpdated() == null && theLastUpdated != null) { if (myBundle.getMeta().getLastUpdated() == null && theLastUpdated != null) {
myBundle.getMeta().getLastUpdatedElement().setValueAsString(theLastUpdated.getValueAsString()); myBundle.getMeta().getLastUpdatedElement().setValueAsString(theLastUpdated.getValueAsString());
} }
if (!hasLink(Constants.LINK_SELF, myBundle) && isNotBlank(theLinkSelf)) { if (!hasLink(Constants.LINK_SELF, myBundle) && isNotBlank(theBundleLinks.getSelf())) {
myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theLinkSelf); myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theBundleLinks.getSelf());
} }
if (!hasLink(Constants.LINK_NEXT, myBundle) && isNotBlank(theLinkNext)) { if (!hasLink(Constants.LINK_NEXT, myBundle) && isNotBlank(theBundleLinks.getNext())) {
myBundle.addLink().setRelation(Constants.LINK_NEXT).setUrl(theLinkNext); myBundle.addLink().setRelation(Constants.LINK_NEXT).setUrl(theBundleLinks.getNext());
} }
if (!hasLink(Constants.LINK_PREVIOUS, myBundle) && isNotBlank(theLinkPrev)) { if (!hasLink(Constants.LINK_PREVIOUS, myBundle) && isNotBlank(theBundleLinks.getPrev())) {
myBundle.addLink().setRelation(Constants.LINK_PREVIOUS).setUrl(theLinkPrev); myBundle.addLink().setRelation(Constants.LINK_PREVIOUS).setUrl(theBundleLinks.getPrev());
}
addTotalResultsToBundle(theTotalResults, theBundleLinks.bundleType);
}
@Override
public void addTotalResultsToBundle(Integer theTotalResults, BundleTypeEnum theBundleType) {
ensureBundle();
if (myBundle.getIdElement().isEmpty()) {
myBundle.setId(UUID.randomUUID().toString());
} }
if (myBundle.getTypeElement().isEmpty() && theBundleType != null) { if (myBundle.getTypeElement().isEmpty() && theBundleType != null) {

View File

@ -19,25 +19,37 @@ package ca.uhn.fhir.rest.server.provider.dstu2;
* limitations under the License. * limitations under the License.
* #L% * #L%
*/ */
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.util.*;
import org.hl7.fhir.instance.model.api.*;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.api.BundleInclusionRule; import ca.uhn.fhir.context.api.BundleInclusionRule;
import ca.uhn.fhir.model.api.*; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.dstu2.resource.Bundle; import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry; import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
import ca.uhn.fhir.model.dstu2.resource.Bundle.Link; import ca.uhn.fhir.model.dstu2.resource.Bundle.Link;
import ca.uhn.fhir.model.dstu2.valueset.SearchEntryModeEnum; import ca.uhn.fhir.model.dstu2.valueset.SearchEntryModeEnum;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.valueset.*; import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.rest.api.BundleLinks;
import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory; import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory;
import ca.uhn.fhir.util.ResourceReferenceInfo; import ca.uhn.fhir.util.ResourceReferenceInfo;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class Dstu2BundleFactory implements IVersionSpecificBundleFactory { public class Dstu2BundleFactory implements IVersionSpecificBundleFactory {
private String myBase; private String myBase;
@ -138,31 +150,39 @@ public class Dstu2BundleFactory implements IVersionSpecificBundleFactory {
} }
@Override @Override
public void addRootPropertiesToBundle(String theId, String theServerBase, String theLinkSelf, String theLinkPrev, String theLinkNext, Integer theTotalResults, BundleTypeEnum theBundleType, public void addRootPropertiesToBundle(String theId, @Nonnull BundleLinks theBundleLinks, Integer theTotalResults,
IPrimitiveType<Date> theLastUpdated) { IPrimitiveType<Date> theLastUpdated) {
ensureBundle(); ensureBundle();
myBase = theServerBase; myBase = theBundleLinks.serverBase;
if (myBundle.getIdElement().isEmpty()) { if (myBundle.getIdElement().isEmpty()) {
myBundle.setId(theId); myBundle.setId(theId);
} }
if (myBundle.getId().isEmpty()) {
myBundle.setId(UUID.randomUUID().toString());
}
if (ResourceMetadataKeyEnum.UPDATED.get(myBundle) == null) { if (ResourceMetadataKeyEnum.UPDATED.get(myBundle) == null) {
ResourceMetadataKeyEnum.UPDATED.put(myBundle, (InstantDt) theLastUpdated); ResourceMetadataKeyEnum.UPDATED.put(myBundle, (InstantDt) theLastUpdated);
} }
if (!hasLink(Constants.LINK_SELF, myBundle) && isNotBlank(theLinkSelf)) { if (!hasLink(Constants.LINK_SELF, myBundle) && isNotBlank(theBundleLinks.getSelf())) {
myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theLinkSelf); myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theBundleLinks.getSelf());
} }
if (!hasLink(Constants.LINK_NEXT, myBundle) && isNotBlank(theLinkNext)) { if (!hasLink(Constants.LINK_NEXT, myBundle) && isNotBlank(theBundleLinks.getNext())) {
myBundle.addLink().setRelation(Constants.LINK_NEXT).setUrl(theLinkNext); myBundle.addLink().setRelation(Constants.LINK_NEXT).setUrl(theBundleLinks.getNext());
} }
if (!hasLink(Constants.LINK_PREVIOUS, myBundle) && isNotBlank(theLinkPrev)) { if (!hasLink(Constants.LINK_PREVIOUS, myBundle) && isNotBlank(theBundleLinks.getPrev())) {
myBundle.addLink().setRelation(Constants.LINK_PREVIOUS).setUrl(theLinkPrev); myBundle.addLink().setRelation(Constants.LINK_PREVIOUS).setUrl(theBundleLinks.getPrev());
}
addTotalResultsToBundle(theTotalResults, theBundleLinks.bundleType);
}
@Override
public void addTotalResultsToBundle(Integer theTotalResults, BundleTypeEnum theBundleType) {
ensureBundle();
if (myBundle.getId().isEmpty()) {
myBundle.setId(UUID.randomUUID().toString());
} }
if (myBundle.getTypeElement().isEmpty() && theBundleType != null) { if (myBundle.getTypeElement().isEmpty() && theBundleType != null) {

View File

@ -25,6 +25,7 @@ import ca.uhn.fhir.context.api.BundleInclusionRule;
import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.valueset.BundleTypeEnum; import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.rest.api.BundleLinks;
import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory; import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory;
import ca.uhn.fhir.rest.server.RestfulServerUtils; import ca.uhn.fhir.rest.server.RestfulServerUtils;
@ -36,9 +37,18 @@ import org.hl7.fhir.dstu3.model.Bundle.SearchEntryMode;
import org.hl7.fhir.dstu3.model.DomainResource; import org.hl7.fhir.dstu3.model.DomainResource;
import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.Resource; import org.hl7.fhir.dstu3.model.Resource;
import org.hl7.fhir.instance.model.api.*; import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import java.util.*; import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank;
@ -170,31 +180,39 @@ public class Dstu3BundleFactory implements IVersionSpecificBundleFactory {
} }
@Override @Override
public void addRootPropertiesToBundle(String theId, String theServerBase, String theLinkSelf, String theLinkPrev, String theLinkNext, Integer theTotalResults, BundleTypeEnum theBundleType, public void addRootPropertiesToBundle(String theId, @Nonnull BundleLinks theBundleLinks, Integer theTotalResults,
IPrimitiveType<Date> theLastUpdated) { IPrimitiveType<Date> theLastUpdated) {
ensureBundle(); ensureBundle();
myBase = theServerBase; myBase = theBundleLinks.serverBase;
if (myBundle.getIdElement().isEmpty()) { if (myBundle.getIdElement().isEmpty()) {
myBundle.setId(theId); myBundle.setId(theId);
} }
if (myBundle.getIdElement().isEmpty()) {
myBundle.setId(UUID.randomUUID().toString());
}
if (myBundle.getMeta().getLastUpdated() == null && theLastUpdated != null) { if (myBundle.getMeta().getLastUpdated() == null && theLastUpdated != null) {
myBundle.getMeta().getLastUpdatedElement().setValueAsString(theLastUpdated.getValueAsString()); myBundle.getMeta().getLastUpdatedElement().setValueAsString(theLastUpdated.getValueAsString());
} }
if (!hasLink(Constants.LINK_SELF, myBundle) && isNotBlank(theLinkSelf)) { if (!hasLink(Constants.LINK_SELF, myBundle) && isNotBlank(theBundleLinks.getSelf())) {
myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theLinkSelf); myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theBundleLinks.getSelf());
} }
if (!hasLink(Constants.LINK_NEXT, myBundle) && isNotBlank(theLinkNext)) { if (!hasLink(Constants.LINK_NEXT, myBundle) && isNotBlank(theBundleLinks.getNext())) {
myBundle.addLink().setRelation(Constants.LINK_NEXT).setUrl(theLinkNext); myBundle.addLink().setRelation(Constants.LINK_NEXT).setUrl(theBundleLinks.getNext());
} }
if (!hasLink(Constants.LINK_PREVIOUS, myBundle) && isNotBlank(theLinkPrev)) { if (!hasLink(Constants.LINK_PREVIOUS, myBundle) && isNotBlank(theBundleLinks.getPrev())) {
myBundle.addLink().setRelation(Constants.LINK_PREVIOUS).setUrl(theLinkPrev); myBundle.addLink().setRelation(Constants.LINK_PREVIOUS).setUrl(theBundleLinks.getPrev());
}
addTotalResultsToBundle(theTotalResults, theBundleLinks.bundleType);
}
@Override
public void addTotalResultsToBundle(Integer theTotalResults, BundleTypeEnum theBundleType) {
ensureBundle();
if (myBundle.getIdElement().isEmpty()) {
myBundle.setId(UUID.randomUUID().toString());
} }
if (myBundle.getTypeElement().isEmpty() && theBundleType != null) { if (myBundle.getTypeElement().isEmpty() && theBundleType != null) {

View File

@ -25,6 +25,7 @@ import ca.uhn.fhir.context.api.BundleInclusionRule;
import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.valueset.BundleTypeEnum; import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.rest.api.BundleLinks;
import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory; import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory;
import ca.uhn.fhir.rest.server.RestfulServerUtils; import ca.uhn.fhir.rest.server.RestfulServerUtils;
@ -42,6 +43,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.instance.model.api.IPrimitiveType;
import javax.annotation.Nonnull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
@ -183,16 +185,13 @@ public class Dstu2Hl7OrgBundleFactory implements IVersionSpecificBundleFactory {
} }
@Override @Override
public void addRootPropertiesToBundle(String theId, String theServerBase, String theLinkSelf, String theLinkPrev, String theLinkNext, public void addRootPropertiesToBundle(String theId, @Nonnull BundleLinks theBundleLinks,
Integer theTotalResults, BundleTypeEnum theBundleType, IPrimitiveType<Date> theLastUpdated) { Integer theTotalResults, IPrimitiveType<Date> theLastUpdated) {
ensureBundle(); ensureBundle();
if (myBundle.getIdElement().isEmpty()) { if (myBundle.getIdElement().isEmpty()) {
myBundle.setId(theId); myBundle.setId(theId);
} }
if (isBlank(myBundle.getId())) {
myBundle.setId(UUID.randomUUID().toString());
}
if (myBundle.getMeta().getLastUpdated() == null && theLastUpdated != null) { if (myBundle.getMeta().getLastUpdated() == null && theLastUpdated != null) {
InstantType instantType = new InstantType(); InstantType instantType = new InstantType();
@ -200,17 +199,28 @@ public class Dstu2Hl7OrgBundleFactory implements IVersionSpecificBundleFactory {
myBundle.getMeta().setLastUpdatedElement(instantType); myBundle.getMeta().setLastUpdatedElement(instantType);
} }
if (!hasLink(Constants.LINK_SELF, myBundle) && isNotBlank(theLinkSelf)) { if (!hasLink(Constants.LINK_SELF, myBundle) && isNotBlank(theBundleLinks.getSelf())) {
myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theLinkSelf); myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theBundleLinks.getSelf());
} }
if (!hasLink(Constants.LINK_NEXT, myBundle) && isNotBlank(theLinkNext)) { if (!hasLink(Constants.LINK_NEXT, myBundle) && isNotBlank(theBundleLinks.getNext())) {
myBundle.addLink().setRelation(Constants.LINK_NEXT).setUrl(theLinkNext); myBundle.addLink().setRelation(Constants.LINK_NEXT).setUrl(theBundleLinks.getNext());
} }
if (!hasLink(Constants.LINK_SELF, myBundle) && isNotBlank(theLinkPrev)) { if (!hasLink(Constants.LINK_SELF, myBundle) && isNotBlank(theBundleLinks.getPrev())) {
myBundle.addLink().setRelation(Constants.LINK_PREVIOUS).setUrl(theLinkPrev); myBundle.addLink().setRelation(Constants.LINK_PREVIOUS).setUrl(theBundleLinks.getPrev());
} }
myBase = theServerBase; myBase = theBundleLinks.serverBase;
addTotalResultsToBundle(theTotalResults, theBundleLinks.bundleType);
}
@Override
public void addTotalResultsToBundle(Integer theTotalResults, BundleTypeEnum theBundleType) {
ensureBundle();
if (isBlank(myBundle.getId())) {
myBundle.setId(UUID.randomUUID().toString());
}
if (myBundle.getTypeElement().isEmpty() && theBundleType != null) { if (myBundle.getTypeElement().isEmpty() && theBundleType != null) {
myBundle.getTypeElement().setValueAsString(theBundleType.getCode()); myBundle.getTypeElement().setValueAsString(theBundleType.getCode());

View File

@ -25,11 +25,15 @@ import ca.uhn.fhir.context.api.BundleInclusionRule;
import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.valueset.BundleTypeEnum; import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.rest.api.BundleLinks;
import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory; import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory;
import ca.uhn.fhir.rest.server.RestfulServerUtils; import ca.uhn.fhir.rest.server.RestfulServerUtils;
import ca.uhn.fhir.util.ResourceReferenceInfo; import ca.uhn.fhir.util.ResourceReferenceInfo;
import org.hl7.fhir.instance.model.api.*; import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent; import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.r4.model.Bundle.BundleLinkComponent; import org.hl7.fhir.r4.model.Bundle.BundleLinkComponent;
@ -38,7 +42,13 @@ import org.hl7.fhir.r4.model.DomainResource;
import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Resource; import org.hl7.fhir.r4.model.Resource;
import java.util.*; import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank;
@ -172,31 +182,39 @@ public class R4BundleFactory implements IVersionSpecificBundleFactory {
} }
@Override @Override
public void addRootPropertiesToBundle(String theId, String theServerBase, String theLinkSelf, String theLinkPrev, String theLinkNext, Integer theTotalResults, BundleTypeEnum theBundleType, public void addRootPropertiesToBundle(String theId, @Nonnull BundleLinks theBundleLinks, Integer theTotalResults,
IPrimitiveType<Date> theLastUpdated) { IPrimitiveType<Date> theLastUpdated) {
ensureBundle(); ensureBundle();
myBase = theServerBase; myBase = theBundleLinks.serverBase;
if (myBundle.getIdElement().isEmpty()) { if (myBundle.getIdElement().isEmpty()) {
myBundle.setId(theId); myBundle.setId(theId);
} }
if (myBundle.getIdElement().isEmpty()) {
myBundle.setId(UUID.randomUUID().toString());
}
if (myBundle.getMeta().getLastUpdated() == null && theLastUpdated != null) { if (myBundle.getMeta().getLastUpdated() == null && theLastUpdated != null) {
myBundle.getMeta().getLastUpdatedElement().setValueAsString(theLastUpdated.getValueAsString()); myBundle.getMeta().getLastUpdatedElement().setValueAsString(theLastUpdated.getValueAsString());
} }
if (!hasLink(Constants.LINK_SELF, myBundle) && isNotBlank(theLinkSelf)) { if (!hasLink(Constants.LINK_SELF, myBundle) && isNotBlank(theBundleLinks.getSelf())) {
myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theLinkSelf); myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theBundleLinks.getSelf());
} }
if (!hasLink(Constants.LINK_NEXT, myBundle) && isNotBlank(theLinkNext)) { if (!hasLink(Constants.LINK_NEXT, myBundle) && isNotBlank(theBundleLinks.getNext())) {
myBundle.addLink().setRelation(Constants.LINK_NEXT).setUrl(theLinkNext); myBundle.addLink().setRelation(Constants.LINK_NEXT).setUrl(theBundleLinks.getNext());
} }
if (!hasLink(Constants.LINK_PREVIOUS, myBundle) && isNotBlank(theLinkPrev)) { if (!hasLink(Constants.LINK_PREVIOUS, myBundle) && isNotBlank(theBundleLinks.getPrev())) {
myBundle.addLink().setRelation(Constants.LINK_PREVIOUS).setUrl(theLinkPrev); myBundle.addLink().setRelation(Constants.LINK_PREVIOUS).setUrl(theBundleLinks.getPrev());
}
addTotalResultsToBundle(theTotalResults, theBundleLinks.bundleType);
}
@Override
public void addTotalResultsToBundle(Integer theTotalResults, BundleTypeEnum theBundleType) {
ensureBundle();
if (myBundle.getIdElement().isEmpty()) {
myBundle.setId(UUID.randomUUID().toString());
} }
if (myBundle.getTypeElement().isEmpty() && theBundleType != null) { if (myBundle.getTypeElement().isEmpty() && theBundleType != null) {

View File

@ -59,6 +59,7 @@ public class PagingUsingNamedPagesR4Test {
@BeforeEach @BeforeEach
public void before() { public void before() {
myPagingProvider = mock(IPagingProvider.class); myPagingProvider = mock(IPagingProvider.class);
when(myPagingProvider.canStoreSearchResults()).thenReturn(true);
servlet.setPagingProvider(myPagingProvider); servlet.setPagingProvider(myPagingProvider);
ourNextBundleProvider = null; ourNextBundleProvider = null;
} }

View File

@ -25,11 +25,15 @@ import ca.uhn.fhir.context.api.BundleInclusionRule;
import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.valueset.BundleTypeEnum; import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.rest.api.BundleLinks;
import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory; import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory;
import ca.uhn.fhir.rest.server.RestfulServerUtils; import ca.uhn.fhir.rest.server.RestfulServerUtils;
import ca.uhn.fhir.util.ResourceReferenceInfo; import ca.uhn.fhir.util.ResourceReferenceInfo;
import org.hl7.fhir.instance.model.api.*; import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r5.model.Bundle; import org.hl7.fhir.r5.model.Bundle;
import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent; import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.r5.model.Bundle.BundleLinkComponent; import org.hl7.fhir.r5.model.Bundle.BundleLinkComponent;
@ -38,7 +42,13 @@ import org.hl7.fhir.r5.model.DomainResource;
import org.hl7.fhir.r5.model.IdType; import org.hl7.fhir.r5.model.IdType;
import org.hl7.fhir.r5.model.Resource; import org.hl7.fhir.r5.model.Resource;
import java.util.*; import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank;
@ -172,31 +182,39 @@ public class R5BundleFactory implements IVersionSpecificBundleFactory {
} }
@Override @Override
public void addRootPropertiesToBundle(String theId, String theServerBase, String theLinkSelf, String theLinkPrev, String theLinkNext, Integer theTotalResults, BundleTypeEnum theBundleType, public void addRootPropertiesToBundle(String theId, @Nonnull BundleLinks theBundleLinks, Integer theTotalResults,
IPrimitiveType<Date> theLastUpdated) { IPrimitiveType<Date> theLastUpdated) {
ensureBundle(); ensureBundle();
myBase = theServerBase; myBase = theBundleLinks.serverBase;
if (myBundle.getIdElement().isEmpty()) { if (myBundle.getIdElement().isEmpty()) {
myBundle.setId(theId); myBundle.setId(theId);
} }
if (myBundle.getIdElement().isEmpty()) {
myBundle.setId(UUID.randomUUID().toString());
}
if (myBundle.getMeta().getLastUpdated() == null && theLastUpdated != null) { if (myBundle.getMeta().getLastUpdated() == null && theLastUpdated != null) {
myBundle.getMeta().getLastUpdatedElement().setValueAsString(theLastUpdated.getValueAsString()); myBundle.getMeta().getLastUpdatedElement().setValueAsString(theLastUpdated.getValueAsString());
} }
if (!hasLink(Constants.LINK_SELF, myBundle) && isNotBlank(theLinkSelf)) { if (!hasLink(Constants.LINK_SELF, myBundle) && isNotBlank(theBundleLinks.getSelf())) {
myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theLinkSelf); myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theBundleLinks.getSelf());
} }
if (!hasLink(Constants.LINK_NEXT, myBundle) && isNotBlank(theLinkNext)) { if (!hasLink(Constants.LINK_NEXT, myBundle) && isNotBlank(theBundleLinks.getNext())) {
myBundle.addLink().setRelation(Constants.LINK_NEXT).setUrl(theLinkNext); myBundle.addLink().setRelation(Constants.LINK_NEXT).setUrl(theBundleLinks.getNext());
} }
if (!hasLink(Constants.LINK_PREVIOUS, myBundle) && isNotBlank(theLinkPrev)) { if (!hasLink(Constants.LINK_PREVIOUS, myBundle) && isNotBlank(theBundleLinks.getPrev())) {
myBundle.addLink().setRelation(Constants.LINK_PREVIOUS).setUrl(theLinkPrev); myBundle.addLink().setRelation(Constants.LINK_PREVIOUS).setUrl(theBundleLinks.getPrev());
}
addTotalResultsToBundle(theTotalResults, theBundleLinks.bundleType);
}
@Override
public void addTotalResultsToBundle(Integer theTotalResults, BundleTypeEnum theBundleType) {
ensureBundle();
if (myBundle.getIdElement().isEmpty()) {
myBundle.setId(UUID.randomUUID().toString());
} }
if (myBundle.getTypeElement().isEmpty() && theBundleType != null) { if (myBundle.getTypeElement().isEmpty() && theBundleType != null) {