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