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