001package ca.uhn.fhir.rest.server.interceptor;
002
003/*-
004 * #%L
005 * HAPI FHIR - Server Framework
006 * %%
007 * Copyright (C) 2014 - 2022 Smile CDR, Inc.
008 * %%
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 *
013 *      http://www.apache.org/licenses/LICENSE-2.0
014 *
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 * #L%
021 */
022
023import ca.uhn.fhir.i18n.Msg;
024import ca.uhn.fhir.context.FhirContext;
025import ca.uhn.fhir.fhirpath.FhirPathExecutionException;
026import ca.uhn.fhir.fhirpath.IFhirPath;
027import ca.uhn.fhir.interceptor.api.Hook;
028import ca.uhn.fhir.interceptor.api.Pointcut;
029import ca.uhn.fhir.rest.api.Constants;
030import ca.uhn.fhir.rest.api.server.RequestDetails;
031import ca.uhn.fhir.rest.api.server.ResponseDetails;
032import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
033import ca.uhn.fhir.util.ParametersUtil;
034import org.hl7.fhir.instance.model.api.IBase;
035import org.hl7.fhir.instance.model.api.IBaseParameters;
036import org.hl7.fhir.instance.model.api.IBaseResource;
037
038import java.util.List;
039
040import static org.apache.commons.lang3.StringUtils.isNotBlank;
041
042/**
043 * This interceptor looks for a URL parameter on requests called <code>_fhirpath</code> and
044 * replaces the resource being returned with a Parameters resource containing the results of
045 * the given FHIRPath expression evaluated against the resource that would otherwise
046 * have been returned.
047 *
048 * @see <a href="https://hapifhir.io/hapi-fhir/docs/interceptors/built_in_server_interceptors.html#response-customizing-evaluate-fhirpath">Interceptors - Response Customization: Evaluate FHIRPath</a>
049 * @since 5.0.0
050 */
051public class FhirPathFilterInterceptor {
052
053        @Hook(Pointcut.SERVER_OUTGOING_RESPONSE)
054        public void preProcessOutgoingResponse(RequestDetails theRequestDetails, ResponseDetails theResponseDetails) {
055                IBaseResource responseResource = theResponseDetails.getResponseResource();
056                if (responseResource != null) {
057                        String[] fhirPathParams = theRequestDetails.getParameters().get(Constants.PARAM_FHIRPATH);
058                        if (fhirPathParams != null) {
059
060                                FhirContext ctx = theRequestDetails.getFhirContext();
061                                IBaseParameters responseParameters = ParametersUtil.newInstance(ctx);
062
063                                for (String expression : fhirPathParams) {
064                                        if (isNotBlank(expression)) {
065                                                IBase resultPart = ParametersUtil.addParameterToParameters(ctx, responseParameters, "result");
066                                                ParametersUtil.addPartString(ctx, resultPart, "expression", expression);
067
068                                                IFhirPath fhirPath = ctx.newFhirPath();
069                                                List<IBase> outputs;
070                                                try {
071                                                        outputs = fhirPath.evaluate(responseResource, expression, IBase.class);
072                                                } catch (FhirPathExecutionException e) {
073                                                        throw new InvalidRequestException(Msg.code(327) + "Error parsing FHIRPath expression: " + e.getMessage());
074                                                }
075
076                                                for (IBase nextOutput : outputs) {
077                                                        if (nextOutput instanceof IBaseResource) {
078                                                                ParametersUtil.addPartResource(ctx, resultPart, "result", (IBaseResource) nextOutput);
079                                                        } else {
080                                                                ParametersUtil.addPart(ctx, resultPart, "result", nextOutput);
081                                                        }
082                                                }
083                                        }
084                                }
085
086                                theResponseDetails.setResponseResource(responseParameters);
087                        }
088                }
089        }
090
091}