001/* 002 * #%L 003 * HAPI FHIR JPA Server 004 * %% 005 * Copyright (C) 2014 - 2025 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.context.FhirContext; 023import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoPatient; 024import ca.uhn.fhir.jpa.api.dao.PatientEverythingParameters; 025import ca.uhn.fhir.jpa.model.util.JpaConstants; 026import ca.uhn.fhir.model.api.annotation.Description; 027import ca.uhn.fhir.model.primitive.IdDt; 028import ca.uhn.fhir.model.valueset.BundleTypeEnum; 029import ca.uhn.fhir.rest.annotation.IdParam; 030import ca.uhn.fhir.rest.annotation.Operation; 031import ca.uhn.fhir.rest.annotation.OperationParam; 032import ca.uhn.fhir.rest.annotation.Sort; 033import ca.uhn.fhir.rest.api.Constants; 034import ca.uhn.fhir.rest.api.SortSpec; 035import ca.uhn.fhir.rest.api.server.IBundleProvider; 036import ca.uhn.fhir.rest.api.server.RequestDetails; 037import ca.uhn.fhir.rest.param.DateRangeParam; 038import ca.uhn.fhir.rest.param.StringAndListParam; 039import ca.uhn.fhir.rest.param.StringOrListParam; 040import ca.uhn.fhir.rest.param.StringParam; 041import ca.uhn.fhir.rest.param.TokenOrListParam; 042import ca.uhn.fhir.rest.param.TokenParam; 043import org.hl7.fhir.instance.model.api.IBaseResource; 044import org.hl7.fhir.instance.model.api.IIdType; 045import org.hl7.fhir.instance.model.api.IPrimitiveType; 046import org.springframework.beans.factory.annotation.Autowired; 047 048import java.util.Arrays; 049import java.util.List; 050 051import static org.apache.commons.lang3.StringUtils.isNotBlank; 052 053public abstract class BaseJpaResourceProviderPatient<T extends IBaseResource> extends BaseJpaResourceProvider<T> { 054 055 @Autowired 056 private FhirContext myFhirContext; 057 /** 058 * Patient/123/$everything 059 */ 060 @Operation( 061 name = JpaConstants.OPERATION_EVERYTHING, 062 canonicalUrl = "http://hl7.org/fhir/OperationDefinition/Patient-everything", 063 idempotent = true, 064 bundleType = BundleTypeEnum.SEARCHSET) 065 public IBundleProvider patientInstanceEverything( 066 jakarta.servlet.http.HttpServletRequest theServletRequest, 067 @IdParam IIdType theId, 068 @Description( 069 shortDefinition = 070 "Results from this method are returned across multiple pages. This parameter controls the size of those pages.") 071 @OperationParam(name = Constants.PARAM_COUNT, typeName = "unsignedInt") 072 IPrimitiveType<Integer> theCount, 073 @Description( 074 shortDefinition = 075 "Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.") 076 @OperationParam(name = Constants.PARAM_OFFSET, typeName = "unsignedInt") 077 IPrimitiveType<Integer> theOffset, 078 @Description( 079 shortDefinition = 080 "Only return resources which were last updated as specified by the given range") 081 @OperationParam(name = Constants.PARAM_LASTUPDATED, min = 0, max = 1) 082 DateRangeParam theLastUpdated, 083 @Description( 084 shortDefinition = 085 "Filter the resources to return only resources matching the given _content filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") 086 @OperationParam( 087 name = Constants.PARAM_CONTENT, 088 min = 0, 089 max = OperationParam.MAX_UNLIMITED, 090 typeName = "string") 091 List<IPrimitiveType<String>> theContent, 092 @Description( 093 shortDefinition = 094 "Filter the resources to return only resources matching the given _text filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") 095 @OperationParam( 096 name = Constants.PARAM_TEXT, 097 min = 0, 098 max = OperationParam.MAX_UNLIMITED, 099 typeName = "string") 100 List<IPrimitiveType<String>> theNarrative, 101 @Description( 102 shortDefinition = 103 "Filter the resources to return only resources matching the given _filter filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") 104 @OperationParam( 105 name = Constants.PARAM_FILTER, 106 min = 0, 107 max = OperationParam.MAX_UNLIMITED, 108 typeName = "string") 109 List<IPrimitiveType<String>> theFilter, 110 @Description( 111 shortDefinition = 112 "Filter the resources to return only resources matching the given _type filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") 113 @OperationParam( 114 name = Constants.PARAM_TYPE, 115 min = 0, 116 max = OperationParam.MAX_UNLIMITED, 117 typeName = "string") 118 List<IPrimitiveType<String>> theTypes, 119 @Description( 120 shortDefinition = 121 "Filter the resources to return only resources matching the given _type filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") 122 @OperationParam(name = Constants.PARAM_MDM, min = 0, max = 1, typeName = "boolean") 123 IPrimitiveType<Boolean> theMdmExpand, 124 @Sort SortSpec theSortSpec, 125 RequestDetails theRequestDetails) { 126 127 startRequest(theServletRequest); 128 try { 129 PatientEverythingParameters everythingParams = new PatientEverythingParameters(); 130 everythingParams.setCount(theCount); 131 everythingParams.setOffset(theOffset); 132 everythingParams.setLastUpdated(theLastUpdated); 133 everythingParams.setSort(theSortSpec); 134 everythingParams.setContent(toStringAndList(theContent)); 135 everythingParams.setNarrative(toStringAndList(theNarrative)); 136 everythingParams.setFilter(toStringAndList(theFilter)); 137 everythingParams.setTypes(toStringAndList(theTypes)); 138 everythingParams.setMdmExpand(resolveNullValue(theMdmExpand)); 139 140 return ((IFhirResourceDaoPatient<?>) getDao()) 141 .patientInstanceEverything(theServletRequest, theRequestDetails, everythingParams, theId); 142 } finally { 143 endRequest(theServletRequest); 144 } 145 } 146 147 /** 148 * /Patient/$everything 149 */ 150 @Operation( 151 name = JpaConstants.OPERATION_EVERYTHING, 152 canonicalUrl = "http://hl7.org/fhir/OperationDefinition/Patient-everything", 153 idempotent = true, 154 bundleType = BundleTypeEnum.SEARCHSET) 155 public IBundleProvider patientTypeEverything( 156 jakarta.servlet.http.HttpServletRequest theServletRequest, 157 @Description( 158 shortDefinition = 159 "Results from this method are returned across multiple pages. This parameter controls the size of those pages.") 160 @OperationParam(name = Constants.PARAM_COUNT, typeName = "unsignedInt") 161 IPrimitiveType<Integer> theCount, 162 @Description( 163 shortDefinition = 164 "Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.") 165 @OperationParam(name = Constants.PARAM_OFFSET, typeName = "unsignedInt") 166 IPrimitiveType<Integer> theOffset, 167 @Description( 168 shortDefinition = 169 "Only return resources which were last updated as specified by the given range") 170 @OperationParam(name = Constants.PARAM_LASTUPDATED, min = 0, max = 1) 171 DateRangeParam theLastUpdated, 172 @Description( 173 shortDefinition = 174 "Filter the resources to return only resources matching the given _content filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") 175 @OperationParam( 176 name = Constants.PARAM_CONTENT, 177 min = 0, 178 max = OperationParam.MAX_UNLIMITED, 179 typeName = "string") 180 List<IPrimitiveType<String>> theContent, 181 @Description( 182 shortDefinition = 183 "Filter the resources to return only resources matching the given _text filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") 184 @OperationParam( 185 name = Constants.PARAM_TEXT, 186 min = 0, 187 max = OperationParam.MAX_UNLIMITED, 188 typeName = "string") 189 List<IPrimitiveType<String>> theNarrative, 190 @Description( 191 shortDefinition = 192 "Filter the resources to return only resources matching the given _filter filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") 193 @OperationParam( 194 name = Constants.PARAM_FILTER, 195 min = 0, 196 max = OperationParam.MAX_UNLIMITED, 197 typeName = "string") 198 List<IPrimitiveType<String>> theFilter, 199 @Description( 200 shortDefinition = 201 "Filter the resources to return only resources matching the given _type filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") 202 @OperationParam( 203 name = Constants.PARAM_TYPE, 204 min = 0, 205 max = OperationParam.MAX_UNLIMITED, 206 typeName = "string") 207 List<IPrimitiveType<String>> theTypes, 208 @Description(shortDefinition = "Filter the resources to return based on the patient ids provided.") 209 @OperationParam( 210 name = Constants.PARAM_ID, 211 min = 0, 212 max = OperationParam.MAX_UNLIMITED, 213 typeName = "id") 214 List<IIdType> theId, 215 @Description( 216 shortDefinition = 217 "Filter the resources to return only resources matching the given _type filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") 218 @OperationParam(name = Constants.PARAM_MDM, min = 0, max = 1, typeName = "boolean") 219 IPrimitiveType<Boolean> theMdmExpand, 220 @Sort SortSpec theSortSpec, 221 RequestDetails theRequestDetails) { 222 223 startRequest(theServletRequest); 224 try { 225 PatientEverythingParameters everythingParams = new PatientEverythingParameters(); 226 everythingParams.setCount(theCount); 227 everythingParams.setOffset(theOffset); 228 everythingParams.setLastUpdated(theLastUpdated); 229 everythingParams.setSort(theSortSpec); 230 everythingParams.setContent(toStringAndList(theContent)); 231 everythingParams.setNarrative(toStringAndList(theNarrative)); 232 everythingParams.setFilter(toStringAndList(theFilter)); 233 everythingParams.setTypes(toStringAndList(theTypes)); 234 everythingParams.setMdmExpand(resolveNullValue(theMdmExpand)); 235 236 return ((IFhirResourceDaoPatient<?>) getDao()) 237 .patientTypeEverything( 238 theServletRequest, 239 theRequestDetails, 240 everythingParams, 241 toFlattenedPatientIdTokenParamList(theId)); 242 } finally { 243 endRequest(theServletRequest); 244 } 245 } 246 247 /** 248 * Given a list of string types, return only the ID portions of any parameters passed in. 249 */ 250 private TokenOrListParam toFlattenedPatientIdTokenParamList(List<IIdType> theId) { 251 TokenOrListParam retVal = new TokenOrListParam(); 252 if (theId != null) { 253 for (IIdType next : theId) { 254 if (isNotBlank(next.getValue())) { 255 String[] split = next.getValueAsString().split(","); 256 Arrays.stream(split).map(IdDt::new).forEach(id -> { 257 retVal.addOr(new TokenParam(id.getIdPart())); 258 }); 259 } 260 } 261 } 262 263 return retVal.getValuesAsQueryTokens().isEmpty() ? null : retVal; 264 } 265 266 private StringAndListParam toStringAndList(List<IPrimitiveType<String>> theNarrative) { 267 StringAndListParam retVal = new StringAndListParam(); 268 if (theNarrative != null) { 269 for (IPrimitiveType<String> next : theNarrative) { 270 if (isNotBlank(next.getValue())) { 271 retVal.addAnd(new StringOrListParam().addOr(new StringParam(next.getValue()))); 272 } 273 } 274 } 275 if (retVal.getValuesAsQueryTokens().isEmpty()) { 276 return null; 277 } 278 return retVal; 279 } 280 281 private boolean resolveNullValue(IPrimitiveType<Boolean> theMdmExpand) { 282 return theMdmExpand == null ? Boolean.FALSE : theMdmExpand.getValue(); 283 } 284}