001/* 002 * #%L 003 * HAPI FHIR Storage api 004 * %% 005 * Copyright (C) 2014 - 2024 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 ExpungeOutcome forceExpungeInExistingTransaction( 180 IIdType theId, ExpungeOptions theExpungeOptions, RequestDetails theRequest); 181 182 @Nonnull 183 Class<T> getResourceType(); 184 185 IBundleProvider history(Date theSince, Date theUntil, Integer theOffset, RequestDetails theRequestDetails); 186 187 /** 188 * @deprecated Use {@link #history(IIdType, HistorySearchDateRangeParam, RequestDetails)} instead 189 */ 190 @Deprecated(since = "6.2") 191 IBundleProvider history( 192 IIdType theId, Date theSince, Date theUntil, Integer theOffset, RequestDetails theRequestDetails); 193 194 IBundleProvider history( 195 IIdType theId, 196 HistorySearchDateRangeParam theHistorySearchDateRangeParam, 197 RequestDetails theRequestDetails); 198 199 /** 200 * Not supported in DSTU1! 201 * 202 * @param theRequestDetails The request details including permissions and partitioning information 203 */ 204 <MT extends IBaseMetaType> MT metaAddOperation(IIdType theId1, MT theMetaAdd, RequestDetails theRequestDetails); 205 206 /** 207 * Not supported in DSTU1! 208 * 209 * @param theRequestDetails The request details including permissions and partitioning information 210 */ 211 <MT extends IBaseMetaType> MT metaDeleteOperation(IIdType theId1, MT theMetaDel, RequestDetails theRequestDetails); 212 213 /** 214 * Not supported in DSTU1! 215 * 216 * @param theRequestDetails The request details including permissions and partitioning information 217 */ 218 <MT extends IBaseMetaType> MT metaGetOperation(Class<MT> theType, IIdType theId, RequestDetails theRequestDetails); 219 220 /** 221 * Not supported in DSTU1! 222 * 223 * @param theRequestDetails The request details including permissions and partitioning information 224 */ 225 <MT extends IBaseMetaType> MT metaGetOperation(Class<MT> theType, RequestDetails theRequestDetails); 226 227 /** 228 * Opens a new transaction and performs a patch operation 229 */ 230 DaoMethodOutcome patch( 231 IIdType theId, 232 String theConditionalUrl, 233 PatchTypeEnum thePatchType, 234 String thePatchBody, 235 IBaseParameters theFhirPatchBody, 236 RequestDetails theRequestDetails); 237 238 /** 239 * Execute a patch operation within the existing database transaction 240 */ 241 DaoMethodOutcome patchInTransaction( 242 IIdType theId, 243 String theConditionalUrl, 244 boolean thePerformIndexing, 245 PatchTypeEnum thePatchType, 246 String thePatchBody, 247 IBaseParameters theFhirPatchBody, 248 RequestDetails theRequestDetails, 249 TransactionDetails theTransactionDetails); 250 251 /** 252 * Read a resource - Note that this variant of the method does not take in a {@link RequestDetails} and 253 * therefore can not fire any interceptors. 254 * 255 * @deprecated Use {@link #read(IIdType, RequestDetails)} instead 256 */ 257 T read(IIdType theId); 258 259 /** 260 * Read a resource by its internal PID 261 * 262 * @throws ResourceNotFoundException If the ID is not known to the server 263 * @throws ResourceGoneException If the resource has been deleted 264 */ 265 T readByPid(IResourcePersistentId thePid); 266 267 /** 268 * Read a resource by its internal PID 269 * 270 * @throws ResourceNotFoundException If the ID is not known to the server 271 * @throws ResourceGoneException If the resource has been deleted and theDeletedOk is true 272 */ 273 default T readByPid(IResourcePersistentId thePid, boolean theDeletedOk) { 274 throw new UnsupportedOperationException(Msg.code(571)); 275 } 276 277 /** 278 * @param theRequestDetails The request details including permissions and partitioning information 279 * @throws ResourceNotFoundException If the ID is not known to the server 280 * @throws ResourceGoneException If the resource has been deleted 281 */ 282 T read(IIdType theId, RequestDetails theRequestDetails); 283 284 /** 285 * Should deleted resources be returned successfully. This should be false for 286 * a normal FHIR read. 287 * 288 * @throws ResourceNotFoundException If the ID is not known to the server 289 * @throws ResourceGoneException If the resource has been deleted and theDeletedOk is true 290 */ 291 T read(IIdType theId, RequestDetails theRequestDetails, boolean theDeletedOk); 292 293 /** 294 * Read an entity from the database, and return it. Note that here we're talking about whatever the 295 * native database representation is, not the parsed {@link IBaseResource} instance. 296 * 297 * @param theId The resource ID to fetch 298 * @param theRequest The request details object associated with the request 299 */ 300 IBasePersistedResource readEntity(IIdType theId, RequestDetails theRequest); 301 302 /** 303 * Updates index tables associated with the given resource. Does not create a new 304 * version or update the resource's update time. 305 * 306 * @param theResource The FHIR resource object corresponding to the entity to reindex 307 * @param theEntity The storage entity to reindex 308 * @deprecated Use {@link #reindex(IResourcePersistentId, ReindexParameters, RequestDetails, TransactionDetails)} 309 */ 310 @Deprecated 311 void reindex(T theResource, IBasePersistedResource theEntity); 312 313 /** 314 * Reindex the given resource 315 * 316 * @param theResourcePersistentId The ID 317 * @return 318 */ 319 @SuppressWarnings("rawtypes") 320 ReindexOutcome reindex( 321 IResourcePersistentId theResourcePersistentId, 322 ReindexParameters theReindexParameters, 323 RequestDetails theRequest, 324 TransactionDetails theTransactionDetails); 325 326 /** 327 * Returns ReindexJobStatus information object that tells the caller 328 * if a reindex job is still in progress or not. 329 * 330 * If the implementing DAO requires additional work during reindexing, 331 * this is the method to override. 332 */ 333 default ReindexJobStatus getReindexJobStatus() { 334 return ReindexJobStatus.NO_WORK_NEEDED; 335 } 336 337 void removeTag( 338 IIdType theId, TagTypeEnum theTagType, String theSystem, String theCode, RequestDetails theRequestDetails); 339 340 void removeTag(IIdType theId, TagTypeEnum theTagType, String theSystem, String theCode); 341 342 /** 343 * @deprecated Use {@link #search(SearchParameterMap, RequestDetails)} instead 344 * @throws InvalidRequestException If a SearchParameter is not known to the server 345 */ 346 IBundleProvider search(SearchParameterMap theParams) throws InvalidRequestException; 347 348 /** 349 * * 350 * @throws InvalidRequestException If a SearchParameter is not known to the server 351 */ 352 IBundleProvider search(SearchParameterMap theParams, RequestDetails theRequestDetails) 353 throws InvalidRequestException; 354 355 /** 356 * * 357 * @throws InvalidRequestException If a SearchParameter is not known to the server 358 */ 359 IBundleProvider search( 360 SearchParameterMap theParams, RequestDetails theRequestDetails, HttpServletResponse theServletResponse) 361 throws InvalidRequestException; 362 363 /** 364 * Search for IDs for processing a match URLs, etc. 365 */ 366 default <PT extends IResourcePersistentId> List<PT> searchForIds( 367 SearchParameterMap theParams, RequestDetails theRequest) { 368 return searchForIds(theParams, theRequest, null); 369 } 370 371 /** 372 * Search for IDs for processing a match URLs, etc. 373 * 374 * @param theConditionalOperationTargetOrNull If we're searching for IDs in order to satisfy a conditional 375 * create/update, this is the resource being searched for 376 * @since 5.5.0 377 */ 378 default <PT extends IResourcePersistentId> List<PT> searchForIds( 379 SearchParameterMap theParams, 380 RequestDetails theRequest, 381 @Nullable IBaseResource theConditionalOperationTargetOrNull) { 382 return searchForIds(theParams, theRequest); 383 } 384 385 /** 386 * Search results matching theParams. 387 * This call does not currently invoke any interceptors, so should only be used for infrastructure that 388 * will not need to participate in the consent services, or caching. 389 * The Stream MUST be closed to avoid leaking resources. 390 * If called within a transaction, the Stream will fail if passed outside the tx boundary. 391 * @param theParams the search 392 * @param theRequest for partition target info 393 * @return a Stream that MUST only be used within the calling transaction. 394 */ 395 default <PID extends IResourcePersistentId<?>> Stream<PID> searchForIdStream( 396 SearchParameterMap theParams, 397 RequestDetails theRequest, 398 @Nullable IBaseResource theConditionalOperationTargetOrNull) { 399 List<PID> iResourcePersistentIds = searchForIds(theParams, theRequest); 400 return iResourcePersistentIds.stream(); 401 } 402 403 /** 404 * Return all search results matching theParams. 405 * Will load all resources into ram, so not appropriate for large data sets. 406 * This call invokes both preaccess and preshow interceptors. 407 * @param theParams the search 408 * @param theRequest for partition target info 409 */ 410 default List<T> searchForResources(SearchParameterMap theParams, RequestDetails theRequest) { 411 IBundleProvider provider = search(theParams, theRequest); 412 //noinspection unchecked 413 return (List<T>) provider.getAllResources(); 414 } 415 416 /** 417 * Return the FHIR Ids matching theParams. 418 * This call does not currently invoke any interceptors, so should only be used for infrastructure that 419 * will not need to participate in the consent services, or caching. 420 * @param theParams the search 421 * @param theRequest for partition target info 422 */ 423 default List<IIdType> searchForResourceIds(SearchParameterMap theParams, RequestDetails theRequest) { 424 return searchForResources(theParams, theRequest).stream() 425 .map(IBaseResource::getIdElement) 426 .collect(Collectors.toList()); 427 } 428 429 /** 430 * Takes a map of incoming raw search parameters and translates/parses them into 431 * appropriate {@link IQueryParameterType} instances of the appropriate type 432 * for the given param 433 * 434 * @throws InvalidRequestException If any of the parameters are not known 435 */ 436 void translateRawParameters(Map<String, List<String>> theSource, SearchParameterMap theTarget); 437 438 /** 439 * Update a resource - Note that this variant of the method does not take in a {@link RequestDetails} and 440 * therefore can not fire any interceptors. 441 * 442 * @deprecated Use {@link #update(T, RequestDetails)} instead 443 */ 444 DaoMethodOutcome update(T theResource); 445 446 DaoMethodOutcome update(T theResource, RequestDetails theRequestDetails); 447 448 /** 449 * Update a resource - Note that this variant of the method does not take in a {@link RequestDetails} and 450 * therefore can not fire any interceptors. 451 * 452 * @deprecated Use {@link #update(T, String, RequestDetails)} instead 453 */ 454 DaoMethodOutcome update(T theResource, String theMatchUrl); 455 456 /** 457 * @param thePerformIndexing Use with caution! If you set this to false, you need to manually perform indexing or your resources 458 * won't be indexed and searches won't work. 459 * @param theRequestDetails The request details including permissions and partitioning information 460 */ 461 DaoMethodOutcome update( 462 T theResource, String theMatchUrl, boolean thePerformIndexing, RequestDetails theRequestDetails); 463 464 DaoMethodOutcome update(T theResource, String theMatchUrl, RequestDetails theRequestDetails); 465 466 /** 467 * @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 468 * resources mapping to external content such as external code systems) 469 */ 470 DaoMethodOutcome update( 471 T theResource, 472 String theMatchUrl, 473 boolean thePerformIndexing, 474 boolean theForceUpdateVersion, 475 RequestDetails theRequestDetails, 476 @Nonnull TransactionDetails theTransactionDetails); 477 478 /** 479 * Not supported in DSTU1! 480 * 481 * @param theRequestDetails The request details including permissions and partitioning information 482 * @return MethodOutcome even if the resource fails validation it should still successfully return with a response status of 200 483 */ 484 MethodOutcome validate( 485 T theResource, 486 IIdType theId, 487 String theRawResource, 488 EncodingEnum theEncoding, 489 ValidationModeEnum theMode, 490 String theProfile, 491 RequestDetails theRequestDetails); 492 493 RuntimeResourceDefinition validateCriteriaAndReturnResourceDefinition(String criteria); 494 495 /** 496 * @deprecated use #read(IIdType, RequestDetails) instead 497 */ 498 default String getCurrentVersionId(IIdType theReferenceElement) { 499 return read(theReferenceElement.toVersionless()).getIdElement().getVersionIdPart(); 500 } 501}