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}