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