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