
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}