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}