001package ca.uhn.fhir.rest.api.server;
002
003import ca.uhn.fhir.i18n.Msg;
004import ca.uhn.fhir.context.ConfigurationException;
005import org.apache.commons.lang3.Validate;
006import org.hl7.fhir.instance.model.api.IBaseResource;
007import org.hl7.fhir.instance.model.api.IPrimitiveType;
008
009import javax.annotation.Nonnull;
010import javax.annotation.Nullable;
011import java.util.ArrayList;
012import java.util.Date;
013import java.util.List;
014import java.util.stream.Collectors;
015
016/*
017 * #%L
018 * HAPI FHIR - Server Framework
019 * %%
020 * Copyright (C) 2014 - 2022 Smile CDR, Inc.
021 * %%
022 * Licensed under the Apache License, Version 2.0 (the "License");
023 * you may not use this file except in compliance with the License.
024 * You may obtain a copy of the License at
025 *
026 * http://www.apache.org/licenses/LICENSE-2.0
027 *
028 * Unless required by applicable law or agreed to in writing, software
029 * distributed under the License is distributed on an "AS IS" BASIS,
030 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
031 * See the License for the specific language governing permissions and
032 * limitations under the License.
033 * #L%
034 */
035
036
037public interface IBundleProvider {
038
039        /**
040         * If this method is implemented, provides an ID for the current
041         * page of results. This ID should be unique (at least within
042         * the current search as identified by {@link #getUuid()})
043         * so that it can be used to look up a specific page of results.
044         * <p>
045         * This can be used in order to allow the
046         * server paging mechanism to work using completely
047         * opaque links (links that do not encode any index/offset
048         * information), which can be useful on some servers.
049         * </p>
050         *
051         * @since 3.5.0
052         */
053        default String getCurrentPageId() {
054                return null;
055        }
056
057        /**
058         * If this method is implemented, provides an ID for the next
059         * page of results. This ID should be unique (at least within
060         * the current search as identified by {@link #getUuid()})
061         * so that it can be used to look up a specific page of results.
062         * <p>
063         * This can be used in order to allow the
064         * server paging mechanism to work using completely
065         * opaque links (links that do not encode any index/offset
066         * information), which can be useful on some servers.
067         * </p>
068         *
069         * @since 3.5.0
070         */
071        default String getNextPageId() {
072                return null;
073        }
074
075        /**
076         * If this method is implemented, provides an ID for the previous
077         * page of results. This ID should be unique (at least within
078         * the current search as identified by {@link #getUuid()})
079         * so that it can be used to look up a specific page of results.
080         * <p>
081         * This can be used in order to allow the
082         * server paging mechanism to work using completely
083         * opaque links (links that do not encode any index/offset
084         * information), which can be useful on some servers.
085         * </p>
086         *
087         * @since 3.5.0
088         */
089        default String getPreviousPageId() {
090                return null;
091        }
092
093        /**
094         * If the results in this bundle were produced using an offset query (as opposed to a query using
095         * continuation pointers, page IDs, etc.) the page offset can be returned here. The server
096         * should then attempt to form paging links that use <code>_offset</code> instead of
097         * opaque page IDs.
098         */
099        default Integer getCurrentPageOffset() {
100                return null;
101        }
102
103        /**
104         * If {@link #getCurrentPageOffset()} returns a non-null value, this method must also return
105         * the actual page size used
106         */
107        default Integer getCurrentPageSize() {
108                return null;
109        }
110
111
112        /**
113         * Returns the instant as of which this result was created. The
114         * result of this value is used to populate the <code>lastUpdated</code>
115         * value on search result/history result bundles.
116         */
117        IPrimitiveType<Date> getPublished();
118
119        /**
120         * Load the given collection of resources by index, plus any additional resources per the
121         * server's processing rules (e.g. _include'd resources, OperationOutcome, etc.). For example,
122         * if the method is invoked with index 0,10 the method might return 10 search results, plus an
123         * additional 20 resources which matched a client's _include specification.
124         * <p>
125         * Note that if this bundle provider was loaded using a
126         * page ID (i.e. via {@link ca.uhn.fhir.rest.server.IPagingProvider#retrieveResultList(RequestDetails, String, String)}
127         * because {@link #getNextPageId()} provided a value on the
128         * previous page, then the indexes should be ignored and the
129         * whole page returned.
130         * </p>
131         *
132         * @param theFromIndex The low index (inclusive) to return
133         * @param theToIndex   The high index (exclusive) to return
134         * @return A list of resources. The size of this list must be at least <code>theToIndex - theFromIndex</code>.
135         */
136        @Nonnull
137        List<IBaseResource> getResources(int theFromIndex, int theToIndex);
138
139        /**
140         * Get all resources
141         *
142         * @return <code>getResources(0, this.size())</code>.  Return an empty list if <code>this.size()</code> is zero.
143         * @throws ConfigurationException if size() is null
144         */
145        @Nonnull
146        default List<IBaseResource> getAllResources() {
147                List<IBaseResource> retval = new ArrayList<>();
148
149                Integer size = size();
150                if (size == null) {
151                        throw new ConfigurationException(Msg.code(464) + "Attempt to request all resources from an asynchronous search result.  The SearchParameterMap for this search probably should have been synchronous.");
152                }
153                if (size > 0) {
154                        retval.addAll(getResources(0, size));
155                }
156                return retval;
157        }
158
159        /**
160         * Returns the UUID associated with this search. Note that this
161         * does not need to return a non-null value unless it a
162         * IPagingProvider is being used that requires UUIDs
163         * being returned.
164         * <p>
165         * In other words, if you are using the default FifoMemoryPagingProvider in
166         * your server, it is fine for this method to simply return {@code null} since FifoMemoryPagingProvider
167         * does not use the value anyhow. On the other hand, if you are creating a custom
168         * IPagingProvider implementation you might use this method to communicate
169         * the search ID back to the provider.
170         * </p>
171         * <p>
172         * Note that the UUID returned by this method corresponds to
173         * the search, and not to the individual page.
174         * </p>
175         */
176        @Nullable
177        String getUuid();
178
179        /**
180         * Optionally may be used to signal a preferred page size to the server, e.g. because
181         * the implementing code recognizes that the resources which will be returned by this
182         * implementation are expensive to load so a smaller page size should be used. The value
183         * returned by this method will only be used if the client has not explicitly requested
184         * a page size.
185         *
186         * @return Returns the preferred page size or <code>null</code>
187         */
188        Integer preferredPageSize();
189
190        /**
191         * Returns the total number of results which match the given query (exclusive of any
192         * _include's or OperationOutcome). May return {@literal null} if the total size is not
193         * known or would be too expensive to calculate.
194         */
195        @Nullable
196        Integer size();
197
198        /**
199         * This method returns <code>true</code> if the bundle provider knows that at least
200         * one result exists.
201         */
202        default boolean isEmpty() {
203                Integer size = size();
204                if (size != null) {
205                        return size == 0;
206                }
207                return getResources(0, 1).isEmpty();
208        }
209
210        /**
211         * @return the value of {@link #size()} and throws a {@link NullPointerException} of it is null
212         */
213        default int sizeOrThrowNpe() {
214                Integer retVal = size();
215                Validate.notNull(retVal, "size() returned null");
216                return retVal;
217        }
218
219        /**
220         * @return the list of ids of all resources in the bundle
221         */
222        default List<String> getAllResourceIds() {
223                return getAllResources().stream().map(resource -> resource.getIdElement().getIdPart()).collect(Collectors.toList());
224        }
225}