
001/* 002 * #%L 003 * HAPI FHIR Storage api 004 * %% 005 * Copyright (C) 2014 - 2025 Smile CDR, Inc. 006 * %% 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 * #L% 019 */ 020package ca.uhn.fhir.jpa.api.dao; 021 022import ca.uhn.fhir.context.RuntimeResourceDefinition; 023import ca.uhn.fhir.i18n.Msg; 024import ca.uhn.fhir.interceptor.model.RequestPartitionId; 025import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome; 026import ca.uhn.fhir.jpa.api.model.DeleteConflictList; 027import ca.uhn.fhir.jpa.api.model.DeleteMethodOutcome; 028import ca.uhn.fhir.jpa.api.model.ExpungeOptions; 029import ca.uhn.fhir.jpa.api.model.ExpungeOutcome; 030import ca.uhn.fhir.jpa.api.model.ReindexJobStatus; 031import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource; 032import ca.uhn.fhir.jpa.model.entity.TagTypeEnum; 033import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; 034import ca.uhn.fhir.model.api.IQueryParameterType; 035import ca.uhn.fhir.rest.api.EncodingEnum; 036import ca.uhn.fhir.rest.api.MethodOutcome; 037import ca.uhn.fhir.rest.api.PatchTypeEnum; 038import ca.uhn.fhir.rest.api.ValidationModeEnum; 039import ca.uhn.fhir.rest.api.server.IBundleProvider; 040import ca.uhn.fhir.rest.api.server.RequestDetails; 041import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; 042import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; 043import ca.uhn.fhir.rest.param.HistorySearchDateRangeParam; 044import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; 045import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; 046import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; 047import jakarta.annotation.Nonnull; 048import jakarta.annotation.Nullable; 049import jakarta.servlet.http.HttpServletResponse; 050import org.hl7.fhir.instance.model.api.IBaseMetaType; 051import org.hl7.fhir.instance.model.api.IBaseParameters; 052import org.hl7.fhir.instance.model.api.IBaseResource; 053import org.hl7.fhir.instance.model.api.IIdType; 054 055import java.util.Collection; 056import java.util.Date; 057import java.util.List; 058import java.util.Map; 059import java.util.stream.Collectors; 060import java.util.stream.Stream; 061 062/** 063 * Note that this interface is not considered a stable interface. While it is possible to build applications 064 * that use it directly, please be aware that we may modify methods, add methods, or even remove methods from 065 * time to time, even within minor point releases. 066 */ 067public interface IFhirResourceDao<T extends IBaseResource> extends IDao { 068 069 /** 070 * Create a resource - Note that this variant of the method does not take in a {@link RequestDetails} and 071 * therefore can not fire any interceptors. 072 * 073 * @deprecated Use {@link #create(IBaseResource, RequestDetails)} instead 074 */ 075 DaoMethodOutcome create(T theResource); 076 077 DaoMethodOutcome create(T theResource, RequestDetails theRequestDetails); 078 079 /** 080 * Create a resource - Note that this variant of the method does not take in a {@link RequestDetails} and 081 * therefore can not fire any interceptors. 082 * 083 * @deprecated Use {@link #create(IBaseResource, String, RequestDetails)} instead 084 */ 085 DaoMethodOutcome create(T theResource, String theIfNoneExist); 086 087 /** 088 * @param thePerformIndexing Use with caution! If you set this to false, you need to manually perform indexing or your resources 089 * won't be indexed and searches won't work. 090 * @param theRequestDetails The request details including permissions and partitioning information 091 */ 092 DaoMethodOutcome create( 093 T theResource, 094 String theIfNoneExist, 095 boolean thePerformIndexing, 096 RequestDetails theRequestDetails, 097 @Nonnull TransactionDetails theTransactionDetails); 098 099 DaoMethodOutcome create(T theResource, String theIfNoneExist, RequestDetails theRequestDetails); 100 101 /** 102 * Delete a resource - Note that this variant of the method does not take in a {@link RequestDetails} and 103 * therefore can not fire any interceptors. 104 * 105 * @deprecated Use {@link #delete(IIdType, RequestDetails)} instead 106 */ 107 DaoMethodOutcome delete(IIdType theResource); 108 109 /** 110 * This method does not throw an exception if there are delete conflicts, but populates them 111 * in the provided list 112 */ 113 DaoMethodOutcome delete( 114 IIdType theResource, 115 DeleteConflictList theDeleteConflictsListToPopulate, 116 RequestDetails theRequestDetails, 117 @Nonnull TransactionDetails theTransactionDetails); 118 119 /** 120 * This method throws an exception if there are delete conflicts 121 */ 122 DaoMethodOutcome delete(IIdType theResource, RequestDetails theRequestDetails); 123 124 /** 125 * This method does not throw an exception if there are delete conflicts, but populates them 126 * in the provided list 127 * 128 * @since 6.8.0 129 */ 130 DeleteMethodOutcome deleteByUrl( 131 String theUrl, 132 DeleteConflictList theDeleteConflictsListToPopulate, 133 RequestDetails theRequestDetails, 134 @Nonnull TransactionDetails theTransactionDetails); 135 136 /** 137 * This method throws an exception if there are delete conflicts 138 */ 139 DeleteMethodOutcome deleteByUrl(String theString, RequestDetails theRequestDetails); 140 141 /** 142 * @deprecated Deprecated in 6.8.0 - Use and implement {@link #deletePidList(String, Collection, DeleteConflictList, RequestDetails, TransactionDetails)} 143 */ 144 default <P extends IResourcePersistentId> DeleteMethodOutcome deletePidList( 145 String theUrl, 146 Collection<P> theResourceIds, 147 DeleteConflictList theDeleteConflicts, 148 RequestDetails theRequest) { 149 return deletePidList(theUrl, theResourceIds, theDeleteConflicts, theRequest, new TransactionDetails()); 150 } 151 152 /** 153 * Delete a list of resource Pids 154 * <p> 155 * CAUTION: This list does not throw an exception if there are delete conflicts. It should always be followed by 156 * a call to DeleteConflictUtil.validateDeleteConflictsEmptyOrThrowException(fhirContext, conflicts); 157 * to actually throw the exception. The reason this method doesn't do that itself is that it is expected to be 158 * called repeatedly where an earlier conflict can be removed in a subsequent pass. 159 * 160 * @param theUrl the original URL that triggered the deletion 161 * @param theResourceIds the ids of the resources to be deleted 162 * @param theDeleteConflicts out parameter of conflicts preventing deletion 163 * @param theRequestDetails the request that initiated the request 164 * @return response back to the client 165 * @since 6.8.0 166 */ 167 <P extends IResourcePersistentId> DeleteMethodOutcome deletePidList( 168 String theUrl, 169 Collection<P> theResourceIds, 170 DeleteConflictList theDeleteConflicts, 171 RequestDetails theRequestDetails, 172 TransactionDetails theTransactionDetails); 173 174 ExpungeOutcome expunge(ExpungeOptions theExpungeOptions, RequestDetails theRequestDetails); 175 176 ExpungeOutcome expunge(IIdType theIIdType, ExpungeOptions theExpungeOptions, RequestDetails theRequest); 177 178 <P extends IResourcePersistentId> void expunge(Collection<P> theResourceIds, RequestDetails theRequest); 179 180 /** 181 * Given a collection of resource IDs, return a stream of resource IDs with the versions populated. For example, if the 182 * input contains two different resource IDs and each of those IDs corresponds to a resource 183 * with 2 versions, then the response will include 4 objects, 2 for each input ID. 184 * 185 */ 186 Stream<IResourcePersistentId> fetchAllVersionsOfResources( 187 RequestDetails theRequestDetails, Collection<IResourcePersistentId> theIds); 188 189 ExpungeOutcome forceExpungeInExistingTransaction( 190 IIdType theId, ExpungeOptions theExpungeOptions, RequestDetails theRequest); 191 192 @Nonnull 193 Class<T> getResourceType(); 194 195 IBundleProvider history(Date theSince, Date theUntil, Integer theOffset, RequestDetails theRequestDetails); 196 197 /** 198 * @deprecated Use {@link #history(IIdType, HistorySearchDateRangeParam, RequestDetails)} instead 199 */ 200 @Deprecated(since = "6.2") 201 IBundleProvider history( 202 IIdType theId, Date theSince, Date theUntil, Integer theOffset, RequestDetails theRequestDetails); 203 204 IBundleProvider history( 205 IIdType theId, 206 HistorySearchDateRangeParam theHistorySearchDateRangeParam, 207 RequestDetails theRequestDetails); 208 209 /** 210 * Not supported in DSTU1! 211 * 212 * @param theRequestDetails The request details including permissions and partitioning information 213 */ 214 <MT extends IBaseMetaType> MT metaAddOperation(IIdType theId1, MT theMetaAdd, RequestDetails theRequestDetails); 215 216 /** 217 * Not supported in DSTU1! 218 * 219 * @param theRequestDetails The request details including permissions and partitioning information 220 */ 221 <MT extends IBaseMetaType> MT metaDeleteOperation(IIdType theId1, MT theMetaDel, RequestDetails theRequestDetails); 222 223 /** 224 * Not supported in DSTU1! 225 * 226 * @param theRequestDetails The request details including permissions and partitioning information 227 */ 228 <MT extends IBaseMetaType> MT metaGetOperation(Class<MT> theType, IIdType theId, RequestDetails theRequestDetails); 229 230 /** 231 * Not supported in DSTU1! 232 * 233 * @param theRequestDetails The request details including permissions and partitioning information 234 */ 235 <MT extends IBaseMetaType> MT metaGetOperation(Class<MT> theType, RequestDetails theRequestDetails); 236 237 /** 238 * Opens a new transaction and performs a patch operation 239 */ 240 DaoMethodOutcome patch( 241 IIdType theId, 242 String theConditionalUrl, 243 PatchTypeEnum thePatchType, 244 String thePatchBody, 245 IBaseParameters theFhirPatchBody, 246 RequestDetails theRequestDetails); 247 248 /** 249 * Execute a patch operation within the existing database transaction 250 */ 251 DaoMethodOutcome patchInTransaction( 252 IIdType theId, 253 String theConditionalUrl, 254 boolean thePerformIndexing, 255 PatchTypeEnum thePatchType, 256 String thePatchBody, 257 IBaseParameters theFhirPatchBody, 258 RequestDetails theRequestDetails, 259 TransactionDetails theTransactionDetails, 260 RequestPartitionId theRequestPartitionId); 261 262 /** 263 * Read a resource - Note that this variant of the method does not take in a {@link RequestDetails} and 264 * therefore can not fire any interceptors. 265 * 266 * @deprecated Use {@link #read(IIdType, RequestDetails)} instead 267 */ 268 T read(IIdType theId); 269 270 /** 271 * Read a resource by its internal PID 272 * 273 * @throws ResourceNotFoundException If the ID is not known to the server 274 * @throws ResourceGoneException If the resource has been deleted 275 */ 276 T readByPid(IResourcePersistentId thePid); 277 278 /** 279 * Read a resource by its internal PID 280 * 281 * @throws ResourceNotFoundException If the ID is not known to the server 282 * @throws ResourceGoneException If the resource has been deleted and theDeletedOk is true 283 */ 284 default T readByPid(IResourcePersistentId thePid, boolean theDeletedOk) { 285 throw new UnsupportedOperationException(Msg.code(571)); 286 } 287 288 /** 289 * @param theRequestDetails The request details including permissions and partitioning information 290 * @throws ResourceNotFoundException If the ID is not known to the server 291 * @throws ResourceGoneException If the resource has been deleted 292 */ 293 T read(IIdType theId, RequestDetails theRequestDetails); 294 295 /** 296 * Should deleted resources be returned successfully. This should be false for 297 * a normal FHIR read. 298 * 299 * @throws ResourceNotFoundException If the ID is not known to the server 300 * @throws ResourceGoneException If the resource has been deleted and theDeletedOk is true 301 */ 302 T read(IIdType theId, RequestDetails theRequestDetails, boolean theDeletedOk); 303 304 /** 305 * Read an entity from the database, and return it. Note that here we're talking about whatever the 306 * native database representation is, not the parsed {@link IBaseResource} instance. 307 * 308 * @param theId The resource ID to fetch 309 * @param theRequest The request details object associated with the request 310 */ 311 IBasePersistedResource readEntity(IIdType theId, RequestDetails theRequest); 312 313 /** 314 * Updates index tables associated with the given resource. Does not create a new 315 * version or update the resource's update time. 316 * 317 * @param theResource The FHIR resource object corresponding to the entity to reindex 318 * @param theEntity The storage entity to reindex 319 * @deprecated Use {@link #reindex(IResourcePersistentId, ReindexParameters, RequestDetails, TransactionDetails)} 320 */ 321 @Deprecated 322 void reindex(T theResource, IBasePersistedResource theEntity); 323 324 /** 325 * Reindex the given resource 326 * 327 * @param theResourcePersistentId The ID 328 * @return 329 */ 330 @SuppressWarnings("rawtypes") 331 ReindexOutcome reindex( 332 IResourcePersistentId theResourcePersistentId, 333 ReindexParameters theReindexParameters, 334 RequestDetails theRequest, 335 TransactionDetails theTransactionDetails); 336 337 /** 338 * Returns ReindexJobStatus information object that tells the caller 339 * if a reindex job is still in progress or not. 340 * 341 * If the implementing DAO requires additional work during reindexing, 342 * this is the method to override. 343 */ 344 default ReindexJobStatus getReindexJobStatus() { 345 return ReindexJobStatus.NO_WORK_NEEDED; 346 } 347 348 void removeTag( 349 IIdType theId, TagTypeEnum theTagType, String theSystem, String theCode, RequestDetails theRequestDetails); 350 351 void removeTag(IIdType theId, TagTypeEnum theTagType, String theSystem, String theCode); 352 353 /** 354 * @deprecated Use {@link #search(SearchParameterMap, RequestDetails)} instead 355 * @throws InvalidRequestException If a SearchParameter is not known to the server 356 */ 357 IBundleProvider search(SearchParameterMap theParams) throws InvalidRequestException; 358 359 /** 360 * * 361 * @throws InvalidRequestException If a SearchParameter is not known to the server 362 */ 363 IBundleProvider search(SearchParameterMap theParams, RequestDetails theRequestDetails) 364 throws InvalidRequestException; 365 366 /** 367 * * 368 * @throws InvalidRequestException If a SearchParameter is not known to the server 369 */ 370 IBundleProvider search( 371 SearchParameterMap theParams, RequestDetails theRequestDetails, HttpServletResponse theServletResponse) 372 throws InvalidRequestException; 373 374 /** 375 * Search for IDs for processing a match URLs, etc. 376 */ 377 default <PT extends IResourcePersistentId> List<PT> searchForIds( 378 SearchParameterMap theParams, RequestDetails theRequest) { 379 return searchForIds(theParams, theRequest, null); 380 } 381 382 /** 383 * Search for IDs for processing a match URLs, etc. 384 * 385 * @param theConditionalOperationTargetOrNull If we're searching for IDs in order to satisfy a conditional 386 * create/update, this is the resource being searched for 387 * @since 5.5.0 388 */ 389 default <PT extends IResourcePersistentId> List<PT> searchForIds( 390 SearchParameterMap theParams, 391 RequestDetails theRequest, 392 @Nullable IBaseResource theConditionalOperationTargetOrNull) { 393 return searchForIds(theParams, theRequest); 394 } 395 396 /** 397 * Search results matching theParams. 398 * This call does not currently invoke any interceptors, so should only be used for infrastructure that 399 * will not need to participate in the consent services, or caching. 400 * The Stream MUST be closed to avoid leaking resources. 401 * If called within a transaction, the Stream will fail if passed outside the tx boundary. 402 * @param theParams the search 403 * @param theRequest for partition target info 404 * @return a Stream that MUST only be used within the calling transaction. 405 */ 406 default <PID extends IResourcePersistentId<?>> Stream<PID> searchForIdStream( 407 SearchParameterMap theParams, 408 RequestDetails theRequest, 409 @Nullable IBaseResource theConditionalOperationTargetOrNull) { 410 List<PID> iResourcePersistentIds = searchForIds(theParams, theRequest); 411 return iResourcePersistentIds.stream(); 412 } 413 414 /** 415 * Return all search results matching theParams. 416 * Will load all resources into ram, so not appropriate for large data sets. 417 * This call invokes both preaccess and preshow interceptors. 418 * @param theParams the search 419 * @param theRequest for partition target info 420 */ 421 default List<T> searchForResources(SearchParameterMap theParams, RequestDetails theRequest) { 422 IBundleProvider provider = search(theParams, theRequest); 423 //noinspection unchecked 424 return (List<T>) provider.getAllResources(); 425 } 426 427 /** 428 * Return the FHIR Ids matching theParams. 429 * This call does not currently invoke any interceptors, so should only be used for infrastructure that 430 * will not need to participate in the consent services, or caching. 431 * @param theParams the search 432 * @param theRequest for partition target info 433 */ 434 default List<IIdType> searchForResourceIds(SearchParameterMap theParams, RequestDetails theRequest) { 435 return searchForResources(theParams, theRequest).stream() 436 .map(IBaseResource::getIdElement) 437 .collect(Collectors.toList()); 438 } 439 440 /** 441 * Takes a map of incoming raw search parameters and translates/parses them into 442 * appropriate {@link IQueryParameterType} instances of the appropriate type 443 * for the given param 444 * 445 * @throws InvalidRequestException If any of the parameters are not known 446 */ 447 void translateRawParameters(Map<String, List<String>> theSource, SearchParameterMap theTarget); 448 449 /** 450 * Update a resource - Note that this variant of the method does not take in a {@link RequestDetails} and 451 * therefore can not fire any interceptors. 452 * 453 * @deprecated Use {@link #update(T, RequestDetails)} instead 454 */ 455 DaoMethodOutcome update(T theResource); 456 457 DaoMethodOutcome update(T theResource, RequestDetails theRequestDetails); 458 459 /** 460 * Update a resource - Note that this variant of the method does not take in a {@link RequestDetails} and 461 * therefore can not fire any interceptors. 462 * 463 * @deprecated Use {@link #update(T, String, RequestDetails)} instead 464 */ 465 DaoMethodOutcome update(T theResource, String theMatchUrl); 466 467 /** 468 * @param thePerformIndexing Use with caution! If you set this to false, you need to manually perform indexing or your resources 469 * won't be indexed and searches won't work. 470 * @param theRequestDetails The request details including permissions and partitioning information 471 */ 472 DaoMethodOutcome update( 473 T theResource, String theMatchUrl, boolean thePerformIndexing, RequestDetails theRequestDetails); 474 475 DaoMethodOutcome update(T theResource, String theMatchUrl, RequestDetails theRequestDetails); 476 477 /** 478 * @param theForceUpdateVersion Create a new version with the same contents as the current version even if the content hasn't changed (this is mostly useful for 479 * resources mapping to external content such as external code systems) 480 */ 481 DaoMethodOutcome update( 482 T theResource, 483 String theMatchUrl, 484 boolean thePerformIndexing, 485 boolean theForceUpdateVersion, 486 RequestDetails theRequestDetails, 487 @Nonnull TransactionDetails theTransactionDetails); 488 489 /** 490 * Not supported in DSTU1! 491 * 492 * @param theRequestDetails The request details including permissions and partitioning information 493 * @return MethodOutcome even if the resource fails validation it should still successfully return with a response status of 200 494 */ 495 MethodOutcome validate( 496 T theResource, 497 IIdType theId, 498 String theRawResource, 499 EncodingEnum theEncoding, 500 ValidationModeEnum theMode, 501 String theProfile, 502 RequestDetails theRequestDetails); 503 504 RuntimeResourceDefinition validateCriteriaAndReturnResourceDefinition(String criteria); 505 506 /** 507 * @deprecated use #read(IIdType, RequestDetails) instead 508 */ 509 default String getCurrentVersionId(IIdType theReferenceElement) { 510 return read(theReferenceElement.toVersionless()).getIdElement().getVersionIdPart(); 511 } 512}