001/*
002 * #%L
003 * HAPI FHIR Storage api
004 * %%
005 * Copyright (C) 2014 - 2023 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 org.hl7.fhir.instance.model.api.IBaseMetaType;
058import org.hl7.fhir.instance.model.api.IBaseParameters;
059import org.hl7.fhir.instance.model.api.IBaseResource;
060import org.hl7.fhir.instance.model.api.IIdType;
061import org.hl7.fhir.instance.model.api.IPrimitiveType;
062import org.springframework.beans.factory.annotation.Required;
063
064import javax.servlet.http.HttpServletRequest;
065import java.util.Date;
066
067import static ca.uhn.fhir.jpa.model.util.JpaConstants.OPERATION_META_ADD;
068import static ca.uhn.fhir.jpa.model.util.JpaConstants.OPERATION_META_DELETE;
069import static ca.uhn.fhir.rest.server.provider.ProviderConstants.OPERATION_META;
070
071public abstract class BaseJpaResourceProvider<T extends IBaseResource> extends BaseJpaProvider 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
085        protected IBaseParameters doExpunge(IIdType theIdParam, IPrimitiveType<? extends Integer> theLimit, IPrimitiveType<? extends Boolean> theExpungeDeletedResources, IPrimitiveType<? extends Boolean> theExpungeOldVersions, IPrimitiveType<? extends Boolean> theExpungeEverything, RequestDetails theRequest) {
086
087                ExpungeOptions options = createExpungeOptions(theLimit, theExpungeDeletedResources, theExpungeOldVersions, theExpungeEverything);
088
089                ExpungeOutcome outcome;
090                if (theIdParam != null) {
091                        outcome = getDao().expunge(theIdParam, options, theRequest);
092                } else {
093                        outcome = getDao().expunge(options, theRequest);
094                }
095
096                return createExpungeResponse(outcome);
097        }
098
099
100        public IFhirResourceDao<T> getDao() {
101                return myDao;
102        }
103
104        @Required
105        public void setDao(IFhirResourceDao<T> theDao) {
106                myDao = theDao;
107        }
108
109        @History
110        public IBundleProvider getHistoryForResourceInstance(
111                HttpServletRequest theRequest,
112                @Offset Integer theOffset,
113                @IdParam IIdType theId,
114                @Since Date theSince,
115                @At DateRangeParam theAt,
116                RequestDetails theRequestDetails) {
117
118                startRequest(theRequest);
119                try {
120                        DateRangeParam sinceOrAt = processSinceOrAt(theSince, theAt);
121                        return myDao.history(theId, new HistorySearchDateRangeParam(theRequestDetails.getParameters(), sinceOrAt, theOffset), theRequestDetails);
122                } finally {
123                        endRequest(theRequest);
124                }
125        }
126
127        @History
128        public IBundleProvider getHistoryForResourceType(
129                HttpServletRequest theRequest,
130                @Offset Integer theOffset,
131                @Since Date theSince,
132                @At DateRangeParam theAt,
133                RequestDetails theRequestDetails) {
134                startRequest(theRequest);
135                try {
136                        DateRangeParam sinceOrAt = processSinceOrAt(theSince, theAt);
137                        return myDao.history(sinceOrAt.getLowerBoundAsInstant(), sinceOrAt.getUpperBoundAsInstant(), theOffset, theRequestDetails);
138                } finally {
139                        endRequest(theRequest);
140                }
141        }
142
143        @Override
144        public Class<? extends IBaseResource> getResourceType() {
145                return myDao.getResourceType();
146        }
147
148        @Patch
149        public DaoMethodOutcome patch(HttpServletRequest theRequest, @IdParam IIdType theId, @ConditionalUrlParam String theConditionalUrl, RequestDetails theRequestDetails, @ResourceParam String theBody, PatchTypeEnum thePatchType, @ResourceParam IBaseParameters theRequestBody) {
150                startRequest(theRequest);
151                try {
152                        return myDao.patch(theId, theConditionalUrl, thePatchType, theBody, theRequestBody, theRequestDetails);
153                } finally {
154                        endRequest(theRequest);
155                }
156        }
157
158        @Read(version = true)
159        public T read(HttpServletRequest theRequest, @IdParam IIdType theId, RequestDetails theRequestDetails) {
160                startRequest(theRequest);
161                try {
162                        return myDao.read(theId, theRequestDetails);
163                } finally {
164                        endRequest(theRequest);
165                }
166        }
167
168        @Create
169        public MethodOutcome create(HttpServletRequest theRequest, @ResourceParam T theResource, @ConditionalUrlParam String theConditional, RequestDetails theRequestDetails) {
170                startRequest(theRequest);
171                try {
172                        if (theConditional != null) {
173                                return getDao().create(theResource, theConditional, theRequestDetails);
174                        } else {
175                                return getDao().create(theResource, theRequestDetails);
176                        }
177                } finally {
178                        endRequest(theRequest);
179                }
180        }
181
182        @Delete()
183        public MethodOutcome delete(HttpServletRequest theRequest, @IdParam IIdType theResource, @ConditionalUrlParam(supportsMultiple = true) String theConditional, RequestDetails theRequestDetails) {
184                startRequest(theRequest);
185                try {
186                        if (theConditional != null) {
187                                return getDao().deleteByUrl(theConditional, theRequestDetails);
188                        } else {
189                                return getDao().delete(theResource, theRequestDetails);
190                        }
191                } finally {
192                        endRequest(theRequest);
193                }
194        }
195
196        @Operation(name = ProviderConstants.OPERATION_EXPUNGE, idempotent = false, returnParameters = {
197                @OperationParam(name = JpaConstants.OPERATION_EXPUNGE_OUT_PARAM_EXPUNGE_COUNT, typeName = "integer")
198        })
199        public IBaseParameters expunge(
200                @IdParam IIdType theIdParam,
201                @OperationParam(name = ProviderConstants.OPERATION_EXPUNGE_PARAM_LIMIT, typeName = "integer") IPrimitiveType<Integer> theLimit,
202                @OperationParam(name = ProviderConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_DELETED_RESOURCES, typeName = "boolean") IPrimitiveType<Boolean> theExpungeDeletedResources,
203                @OperationParam(name = ProviderConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_PREVIOUS_VERSIONS, typeName = "boolean") IPrimitiveType<Boolean> theExpungeOldVersions,
204                RequestDetails theRequest) {
205                return doExpunge(theIdParam, theLimit, theExpungeDeletedResources, theExpungeOldVersions, null, theRequest);
206        }
207
208        @Operation(name = ProviderConstants.OPERATION_EXPUNGE, idempotent = false, returnParameters = {
209                @OperationParam(name = JpaConstants.OPERATION_EXPUNGE_OUT_PARAM_EXPUNGE_COUNT, typeName = "integer")
210        })
211        public IBaseParameters expunge(
212                @OperationParam(name = ProviderConstants.OPERATION_EXPUNGE_PARAM_LIMIT, typeName = "integer") IPrimitiveType<Integer> theLimit,
213                @OperationParam(name = ProviderConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_DELETED_RESOURCES, typeName = "boolean") IPrimitiveType<Boolean> theExpungeDeletedResources,
214                @OperationParam(name = ProviderConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_PREVIOUS_VERSIONS, typeName = "boolean") IPrimitiveType<Boolean> theExpungeOldVersions,
215                RequestDetails theRequest) {
216                return doExpunge(null, theLimit, theExpungeDeletedResources, theExpungeOldVersions, null, theRequest);
217        }
218
219        @Description("Request a global list of tags, profiles, and security labels")
220        @Operation(name = OPERATION_META, idempotent = true, returnParameters = {
221                @OperationParam(name = "return", typeName = "Meta")
222        })
223        public IBaseParameters meta(RequestDetails theRequestDetails) {
224                Class metaType = getContext().getElementDefinition("Meta").getImplementingClass();
225                IBaseMetaType metaGetOperation = getDao().metaGetOperation(metaType, theRequestDetails);
226                IBaseParameters parameters = ParametersUtil.newInstance(getContext());
227                ParametersUtil.addParameterToParameters(getContext(), parameters, "return", metaGetOperation);
228                return parameters;
229        }
230
231        @Description("Request a list of tags, profiles, and security labels for a specfic resource instance")
232        @Operation(name = OPERATION_META, idempotent = true, returnParameters = {
233                @OperationParam(name = "return", typeName = "Meta")
234        })
235        public IBaseParameters meta(@IdParam IIdType theId, RequestDetails theRequestDetails) {
236                Class metaType = getContext().getElementDefinition("Meta").getImplementingClass();
237                IBaseMetaType metaGetOperation = getDao().metaGetOperation(metaType, theId, theRequestDetails);
238
239                IBaseParameters parameters = ParametersUtil.newInstance(getContext());
240                ParametersUtil.addParameterToParameters(getContext(), parameters, "return", metaGetOperation);
241                return parameters;
242        }
243
244        @Description("Add tags, profiles, and/or security labels to a resource")
245        @Operation(name = OPERATION_META_ADD, idempotent = false, returnParameters = {
246                @OperationParam(name = "return", typeName = "Meta")
247        })
248        public IBaseParameters metaAdd(@IdParam IIdType theId, @OperationParam(name = "meta", typeName = "Meta") IBaseMetaType theMeta, RequestDetails theRequestDetails) {
249                if (theMeta == null) {
250                        throw new InvalidRequestException(Msg.code(554) + "Input contains no parameter with name 'meta'");
251                }
252                IBaseMetaType metaAddOperation = getDao().metaAddOperation(theId, theMeta, theRequestDetails);
253                IBaseParameters parameters = ParametersUtil.newInstance(getContext());
254                ParametersUtil.addParameterToParameters(getContext(), parameters, "return", metaAddOperation);
255                return parameters;
256        }
257
258        @Description("Delete tags, profiles, and/or security labels from a resource")
259        @Operation(name = OPERATION_META_DELETE, idempotent = false, returnParameters = {
260                @OperationParam(name = "return", typeName = "Meta")
261        })
262        public IBaseParameters metaDelete(@IdParam IIdType theId, @OperationParam(name = "meta", typeName = "Meta") IBaseMetaType theMeta, RequestDetails theRequestDetails) {
263                if (theMeta == null) {
264                        throw new InvalidRequestException(Msg.code(555) + "Input contains no parameter with name 'meta'");
265                }
266                IBaseMetaType metaDelete = getDao().metaDeleteOperation(theId, theMeta, theRequestDetails);
267                IBaseParameters parameters = ParametersUtil.newInstance(getContext());
268                ParametersUtil.addParameterToParameters(getContext(), parameters, "return", metaDelete);
269                return parameters;
270        }
271
272        @Update
273        public MethodOutcome update(HttpServletRequest theRequest, @ResourceParam T theResource, @IdParam IIdType theId, @ConditionalUrlParam String theConditional, RequestDetails theRequestDetails) {
274                startRequest(theRequest);
275                try {
276                        if (theConditional != null) {
277                                return getDao().update(theResource, theConditional, theRequestDetails);
278                        } else {
279                                return getDao().update(theResource, theRequestDetails);
280                        }
281                } finally {
282                        endRequest(theRequest);
283                }
284        }
285
286        @Validate
287        public MethodOutcome validate(@ResourceParam T theResource, @ResourceParam String theRawResource, @ResourceParam EncodingEnum theEncoding, @Validate.Mode ValidationModeEnum theMode,
288                                                                                        @Validate.Profile String theProfile, RequestDetails theRequestDetails) {
289                return validate(theResource, null, theRawResource, theEncoding, theMode, theProfile, theRequestDetails);
290        }
291
292        @Validate
293        public MethodOutcome validate(@ResourceParam T theResource, @IdParam IIdType theId, @ResourceParam String theRawResource, @ResourceParam EncodingEnum theEncoding, @Validate.Mode ValidationModeEnum theMode,
294                                                                                        @Validate.Profile String theProfile, RequestDetails theRequestDetails) {
295                return getDao().validate(theResource, theId, theRawResource, theEncoding, theMode, theProfile, theRequestDetails);
296        }
297
298}