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}