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}