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.provider;
021
022import ca.uhn.fhir.i18n.Msg;
023import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
024import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome;
025import ca.uhn.fhir.jpa.api.model.ExpungeOptions;
026import ca.uhn.fhir.jpa.api.model.ExpungeOutcome;
027import ca.uhn.fhir.jpa.model.util.JpaConstants;
028import ca.uhn.fhir.model.api.annotation.Description;
029import ca.uhn.fhir.rest.annotation.At;
030import ca.uhn.fhir.rest.annotation.ConditionalUrlParam;
031import ca.uhn.fhir.rest.annotation.Create;
032import ca.uhn.fhir.rest.annotation.Delete;
033import ca.uhn.fhir.rest.annotation.History;
034import ca.uhn.fhir.rest.annotation.IdParam;
035import ca.uhn.fhir.rest.annotation.Offset;
036import ca.uhn.fhir.rest.annotation.Operation;
037import ca.uhn.fhir.rest.annotation.OperationParam;
038import ca.uhn.fhir.rest.annotation.Patch;
039import ca.uhn.fhir.rest.annotation.Read;
040import ca.uhn.fhir.rest.annotation.ResourceParam;
041import ca.uhn.fhir.rest.annotation.Since;
042import ca.uhn.fhir.rest.annotation.Update;
043import ca.uhn.fhir.rest.annotation.Validate;
044import ca.uhn.fhir.rest.api.EncodingEnum;
045import ca.uhn.fhir.rest.api.MethodOutcome;
046import ca.uhn.fhir.rest.api.PatchTypeEnum;
047import ca.uhn.fhir.rest.api.ValidationModeEnum;
048import ca.uhn.fhir.rest.api.server.IBundleProvider;
049import ca.uhn.fhir.rest.api.server.RequestDetails;
050import ca.uhn.fhir.rest.param.DateRangeParam;
051import ca.uhn.fhir.rest.param.HistorySearchDateRangeParam;
052import ca.uhn.fhir.rest.server.IResourceProvider;
053import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
054import ca.uhn.fhir.rest.server.provider.ProviderConstants;
055import ca.uhn.fhir.util.CoverageIgnore;
056import ca.uhn.fhir.util.ParametersUtil;
057import jakarta.servlet.http.HttpServletRequest;
058import org.hl7.fhir.instance.model.api.IBaseMetaType;
059import org.hl7.fhir.instance.model.api.IBaseParameters;
060import org.hl7.fhir.instance.model.api.IBaseResource;
061import org.hl7.fhir.instance.model.api.IIdType;
062import org.hl7.fhir.instance.model.api.IPrimitiveType;
063
064import java.util.Date;
065
066import static ca.uhn.fhir.jpa.model.util.JpaConstants.OPERATION_META_ADD;
067import static ca.uhn.fhir.jpa.model.util.JpaConstants.OPERATION_META_DELETE;
068import static ca.uhn.fhir.rest.server.provider.ProviderConstants.OPERATION_META;
069
070public abstract class BaseJpaResourceProvider<T extends IBaseResource> extends BaseJpaProvider
071                implements IResourceProvider {
072
073        private IFhirResourceDao<T> myDao;
074
075        public BaseJpaResourceProvider() {
076                // nothing
077        }
078
079        @CoverageIgnore
080        public BaseJpaResourceProvider(IFhirResourceDao<T> theDao) {
081                myDao = theDao;
082        }
083
084        protected IBaseParameters doExpunge(
085                        IIdType theIdParam,
086                        IPrimitiveType<? extends Integer> theLimit,
087                        IPrimitiveType<? extends Boolean> theExpungeDeletedResources,
088                        IPrimitiveType<? extends Boolean> theExpungeOldVersions,
089                        IPrimitiveType<? extends Boolean> theExpungeEverything,
090                        RequestDetails theRequest) {
091
092                ExpungeOptions options =
093                                createExpungeOptions(theLimit, theExpungeDeletedResources, theExpungeOldVersions, theExpungeEverything);
094
095                ExpungeOutcome outcome;
096                if (theIdParam != null) {
097                        outcome = getDao().expunge(theIdParam, options, theRequest);
098                } else {
099                        outcome = getDao().expunge(options, theRequest);
100                }
101
102                return createExpungeResponse(outcome);
103        }
104
105        public IFhirResourceDao<T> getDao() {
106                return myDao;
107        }
108
109        public void setDao(IFhirResourceDao<T> theDao) {
110                myDao = theDao;
111        }
112
113        @History
114        public IBundleProvider getHistoryForResourceInstance(
115                        HttpServletRequest theRequest,
116                        @Offset Integer theOffset,
117                        @IdParam IIdType theId,
118                        @Since Date theSince,
119                        @At DateRangeParam theAt,
120                        RequestDetails theRequestDetails) {
121
122                startRequest(theRequest);
123                try {
124                        DateRangeParam sinceOrAt = processSinceOrAt(theSince, theAt);
125                        return myDao.history(
126                                        theId,
127                                        new HistorySearchDateRangeParam(theRequestDetails.getParameters(), sinceOrAt, theOffset),
128                                        theRequestDetails);
129                } finally {
130                        endRequest(theRequest);
131                }
132        }
133
134        @History
135        public IBundleProvider getHistoryForResourceType(
136                        HttpServletRequest theRequest,
137                        @Offset Integer theOffset,
138                        @Since Date theSince,
139                        @At DateRangeParam theAt,
140                        RequestDetails theRequestDetails) {
141                startRequest(theRequest);
142                try {
143                        DateRangeParam sinceOrAt = processSinceOrAt(theSince, theAt);
144                        return myDao.history(
145                                        sinceOrAt.getLowerBoundAsInstant(),
146                                        sinceOrAt.getUpperBoundAsInstant(),
147                                        theOffset,
148                                        theRequestDetails);
149                } finally {
150                        endRequest(theRequest);
151                }
152        }
153
154        @Override
155        public Class<? extends IBaseResource> getResourceType() {
156                return myDao.getResourceType();
157        }
158
159        @Patch
160        public DaoMethodOutcome patch(
161                        HttpServletRequest theRequest,
162                        @IdParam IIdType theId,
163                        @ConditionalUrlParam String theConditionalUrl,
164                        RequestDetails theRequestDetails,
165                        @ResourceParam String theBody,
166                        PatchTypeEnum thePatchType,
167                        @ResourceParam IBaseParameters theRequestBody) {
168                startRequest(theRequest);
169                try {
170                        return myDao.patch(theId, theConditionalUrl, thePatchType, theBody, theRequestBody, theRequestDetails);
171                } finally {
172                        endRequest(theRequest);
173                }
174        }
175
176        @Read(version = true)
177        public T read(HttpServletRequest theRequest, @IdParam IIdType theId, RequestDetails theRequestDetails) {
178                startRequest(theRequest);
179                try {
180                        return myDao.read(theId, theRequestDetails);
181                } finally {
182                        endRequest(theRequest);
183                }
184        }
185
186        @Create
187        public MethodOutcome create(
188                        HttpServletRequest theRequest,
189                        @ResourceParam T theResource,
190                        @ConditionalUrlParam String theConditional,
191                        RequestDetails theRequestDetails) {
192                startRequest(theRequest);
193                try {
194                        if (theConditional != null) {
195                                return getDao().create(theResource, theConditional, theRequestDetails);
196                        } else {
197                                return getDao().create(theResource, theRequestDetails);
198                        }
199                } finally {
200                        endRequest(theRequest);
201                }
202        }
203
204        @Delete()
205        public MethodOutcome delete(
206                        HttpServletRequest theRequest,
207                        @IdParam IIdType theResource,
208                        @ConditionalUrlParam(supportsMultiple = true) String theConditional,
209                        RequestDetails theRequestDetails) {
210                startRequest(theRequest);
211                try {
212                        if (theConditional != null) {
213                                return getDao().deleteByUrl(theConditional, theRequestDetails);
214                        } else {
215                                return getDao().delete(theResource, theRequestDetails);
216                        }
217                } finally {
218                        endRequest(theRequest);
219                }
220        }
221
222        @Operation(
223                        name = ProviderConstants.OPERATION_EXPUNGE,
224                        idempotent = false,
225                        returnParameters = {
226                                @OperationParam(name = JpaConstants.OPERATION_EXPUNGE_OUT_PARAM_EXPUNGE_COUNT, typeName = "integer")
227                        })
228        public IBaseParameters expunge(
229                        @IdParam IIdType theIdParam,
230                        @OperationParam(name = ProviderConstants.OPERATION_EXPUNGE_PARAM_LIMIT, typeName = "integer")
231                                        IPrimitiveType<Integer> theLimit,
232                        @OperationParam(
233                                                        name = ProviderConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_DELETED_RESOURCES,
234                                                        typeName = "boolean")
235                                        IPrimitiveType<Boolean> theExpungeDeletedResources,
236                        @OperationParam(
237                                                        name = ProviderConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_PREVIOUS_VERSIONS,
238                                                        typeName = "boolean")
239                                        IPrimitiveType<Boolean> theExpungeOldVersions,
240                        RequestDetails theRequest) {
241                return doExpunge(theIdParam, theLimit, theExpungeDeletedResources, theExpungeOldVersions, null, theRequest);
242        }
243
244        @Operation(
245                        name = ProviderConstants.OPERATION_EXPUNGE,
246                        idempotent = false,
247                        returnParameters = {
248                                @OperationParam(name = JpaConstants.OPERATION_EXPUNGE_OUT_PARAM_EXPUNGE_COUNT, typeName = "integer")
249                        })
250        public IBaseParameters expunge(
251                        @OperationParam(name = ProviderConstants.OPERATION_EXPUNGE_PARAM_LIMIT, typeName = "integer")
252                                        IPrimitiveType<Integer> theLimit,
253                        @OperationParam(
254                                                        name = ProviderConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_DELETED_RESOURCES,
255                                                        typeName = "boolean")
256                                        IPrimitiveType<Boolean> theExpungeDeletedResources,
257                        @OperationParam(
258                                                        name = ProviderConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_PREVIOUS_VERSIONS,
259                                                        typeName = "boolean")
260                                        IPrimitiveType<Boolean> theExpungeOldVersions,
261                        RequestDetails theRequest) {
262                return doExpunge(null, theLimit, theExpungeDeletedResources, theExpungeOldVersions, null, theRequest);
263        }
264
265        @Description("Request a global list of tags, profiles, and security labels")
266        @Operation(
267                        name = OPERATION_META,
268                        idempotent = true,
269                        returnParameters = {@OperationParam(name = "return", typeName = "Meta")})
270        public IBaseParameters meta(RequestDetails theRequestDetails) {
271                Class metaType = getContext().getElementDefinition("Meta").getImplementingClass();
272                IBaseMetaType metaGetOperation = getDao().metaGetOperation(metaType, theRequestDetails);
273                IBaseParameters parameters = ParametersUtil.newInstance(getContext());
274                ParametersUtil.addParameterToParameters(getContext(), parameters, "return", metaGetOperation);
275                return parameters;
276        }
277
278        @Description("Request a list of tags, profiles, and security labels for a specfic resource instance")
279        @Operation(
280                        name = OPERATION_META,
281                        idempotent = true,
282                        returnParameters = {@OperationParam(name = "return", typeName = "Meta")})
283        public IBaseParameters meta(@IdParam IIdType theId, RequestDetails theRequestDetails) {
284                Class metaType = getContext().getElementDefinition("Meta").getImplementingClass();
285                IBaseMetaType metaGetOperation = getDao().metaGetOperation(metaType, theId, theRequestDetails);
286
287                IBaseParameters parameters = ParametersUtil.newInstance(getContext());
288                ParametersUtil.addParameterToParameters(getContext(), parameters, "return", metaGetOperation);
289                return parameters;
290        }
291
292        @Description("Add tags, profiles, and/or security labels to a resource")
293        @Operation(
294                        name = OPERATION_META_ADD,
295                        idempotent = false,
296                        returnParameters = {@OperationParam(name = "return", typeName = "Meta")})
297        public IBaseParameters metaAdd(
298                        @IdParam IIdType theId,
299                        @OperationParam(name = "meta", typeName = "Meta") IBaseMetaType theMeta,
300                        RequestDetails theRequestDetails) {
301                if (theMeta == null) {
302                        throw new InvalidRequestException(Msg.code(554) + "Input contains no parameter with name 'meta'");
303                }
304                IBaseMetaType metaAddOperation = getDao().metaAddOperation(theId, theMeta, theRequestDetails);
305                IBaseParameters parameters = ParametersUtil.newInstance(getContext());
306                ParametersUtil.addParameterToParameters(getContext(), parameters, "return", metaAddOperation);
307                return parameters;
308        }
309
310        @Description("Delete tags, profiles, and/or security labels from a resource")
311        @Operation(
312                        name = OPERATION_META_DELETE,
313                        idempotent = false,
314                        returnParameters = {@OperationParam(name = "return", typeName = "Meta")})
315        public IBaseParameters metaDelete(
316                        @IdParam IIdType theId,
317                        @OperationParam(name = "meta", typeName = "Meta") IBaseMetaType theMeta,
318                        RequestDetails theRequestDetails) {
319                if (theMeta == null) {
320                        throw new InvalidRequestException(Msg.code(555) + "Input contains no parameter with name 'meta'");
321                }
322                IBaseMetaType metaDelete = getDao().metaDeleteOperation(theId, theMeta, theRequestDetails);
323                IBaseParameters parameters = ParametersUtil.newInstance(getContext());
324                ParametersUtil.addParameterToParameters(getContext(), parameters, "return", metaDelete);
325                return parameters;
326        }
327
328        @Update
329        public MethodOutcome update(
330                        HttpServletRequest theRequest,
331                        @ResourceParam T theResource,
332                        @IdParam IIdType theId,
333                        @ConditionalUrlParam String theConditional,
334                        RequestDetails theRequestDetails) {
335                startRequest(theRequest);
336                try {
337                        if (theConditional != null) {
338                                return getDao().update(theResource, theConditional, theRequestDetails);
339                        } else {
340                                return getDao().update(theResource, theRequestDetails);
341                        }
342                } finally {
343                        endRequest(theRequest);
344                }
345        }
346
347        @Validate
348        public MethodOutcome validate(
349                        @ResourceParam T theResource,
350                        @ResourceParam String theRawResource,
351                        @ResourceParam EncodingEnum theEncoding,
352                        @Validate.Mode ValidationModeEnum theMode,
353                        @Validate.Profile String theProfile,
354                        RequestDetails theRequestDetails) {
355                return validate(theResource, null, theRawResource, theEncoding, theMode, theProfile, theRequestDetails);
356        }
357
358        @Validate
359        public MethodOutcome validate(
360                        @ResourceParam T theResource,
361                        @IdParam IIdType theId,
362                        @ResourceParam String theRawResource,
363                        @ResourceParam EncodingEnum theEncoding,
364                        @Validate.Mode ValidationModeEnum theMode,
365                        @Validate.Profile String theProfile,
366                        RequestDetails theRequestDetails) {
367                return getDao().validate(
368                                                theResource, theId, theRawResource, theEncoding, theMode, theProfile, theRequestDetails);
369        }
370}