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}