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}