001package ca.uhn.fhir.rest.server.method;
002
003/*
004 * #%L
005 * HAPI FHIR - Server Framework
006 * %%
007 * Copyright (C) 2014 - 2019 University Health Network
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.context.FhirContext;
024import ca.uhn.fhir.model.api.Include;
025import ca.uhn.fhir.model.valueset.BundleTypeEnum;
026import ca.uhn.fhir.rest.api.Constants;
027import ca.uhn.fhir.rest.api.EncodingEnum;
028import ca.uhn.fhir.rest.api.RequestTypeEnum;
029import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
030import ca.uhn.fhir.rest.api.server.IBundleProvider;
031import ca.uhn.fhir.rest.api.server.IRestfulServer;
032import ca.uhn.fhir.rest.api.server.RequestDetails;
033import ca.uhn.fhir.rest.server.IPagingProvider;
034import ca.uhn.fhir.rest.server.RestfulServerUtils;
035import ca.uhn.fhir.rest.server.RestfulServerUtils.ResponseEncoding;
036import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
037import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
038import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
039import org.hl7.fhir.instance.model.api.IBaseResource;
040
041import javax.annotation.Nonnull;
042import java.lang.reflect.Method;
043import java.util.HashSet;
044import java.util.Set;
045
046import static org.apache.commons.lang3.StringUtils.isBlank;
047import static org.apache.commons.lang3.StringUtils.isNotBlank;
048
049public class PageMethodBinding extends BaseResourceReturningMethodBinding {
050
051        public PageMethodBinding(FhirContext theContext, Method theMethod) {
052                super(null, theMethod, theContext, null);
053        }
054
055        private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PageMethodBinding.class);
056
057        public IBaseResource provider() {
058                return null;
059        }
060
061        @Override
062        protected BundleTypeEnum getResponseBundleType() {
063                return null;
064        }
065
066        @Override
067        public ReturnTypeEnum getReturnType() {
068                return ReturnTypeEnum.BUNDLE;
069        }
070
071        @Override
072        public Object invokeServer(IRestfulServer<?> theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
073                return handlePagingRequest(theServer, theRequest, theRequest.getParameters().get(Constants.PARAM_PAGINGACTION)[0]);
074        }
075
076        @Override
077        public IBaseResource doInvokeServer(IRestfulServer<?> theServer, RequestDetails theRequest) {
078                return handlePagingRequest(theServer, theRequest, theRequest.getParameters().get(Constants.PARAM_PAGINGACTION)[0]);
079        }
080        
081        private IBaseResource handlePagingRequest(IRestfulServer<?> theServer, RequestDetails theRequest, String thePagingAction) {
082                IPagingProvider pagingProvider = theServer.getPagingProvider();
083                if (pagingProvider == null) {
084                        throw new InvalidRequestException("This server does not support paging");
085                }
086
087                Integer offsetI;
088                int start = 0;
089                IBundleProvider resultList;
090
091                String pageId = null;
092                String[] pageIdParams = theRequest.getParameters().get(Constants.PARAM_PAGEID);
093                if (pageIdParams != null) {
094                        if (pageIdParams.length > 0) {
095                                if (isNotBlank(pageIdParams[0])) {
096                                        pageId = pageIdParams[0];
097                                }
098                        }
099                }
100
101                if (pageId != null) {
102                        // This is a page request by Search ID and Page ID
103
104                        resultList = pagingProvider.retrieveResultList(theRequest, thePagingAction, pageId);
105                        validateHaveBundleProvider(thePagingAction, resultList);
106
107                } else {
108                        // This is a page request by Search ID and Offset
109
110                        resultList = pagingProvider.retrieveResultList(theRequest, thePagingAction);
111                        validateHaveBundleProvider(thePagingAction, resultList);
112
113                        offsetI = RestfulServerUtils.tryToExtractNamedParameter(theRequest, Constants.PARAM_PAGINGOFFSET);
114                        if (offsetI == null || offsetI < 0) {
115                                offsetI = 0;
116                        }
117
118                        Integer totalNum = resultList.size();
119                        start = offsetI;
120                        if (totalNum != null) {
121                                start = Math.min(start, totalNum);
122                        }
123                }
124
125                ResponseEncoding responseEncoding = RestfulServerUtils.determineResponseEncodingNoDefault(theRequest, theServer.getDefaultResponseEncoding());
126
127                Set<Include> includes = new HashSet<>();
128                String[] reqIncludes = theRequest.getParameters().get(Constants.PARAM_INCLUDE);
129                if (reqIncludes != null) {
130                        for (String nextInclude : reqIncludes) {
131                                includes.add(new Include(nextInclude));
132                        }
133                }
134
135                String linkSelfBase = theRequest.getFhirServerBase(); // myServerAddressStrategy.determineServerBase(getServletContext(),
136                                                                                                                                                                // theRequest.getServletRequest());
137                String completeUrl = theRequest.getCompleteUrl();
138                String linkSelf = linkSelfBase + completeUrl.substring(theRequest.getCompleteUrl().indexOf('?'));
139
140                BundleTypeEnum bundleType = null;
141                String[] bundleTypeValues = theRequest.getParameters().get(Constants.PARAM_BUNDLETYPE);
142                if (bundleTypeValues != null) {
143                        bundleType = BundleTypeEnum.VALUESET_BINDER.fromCodeString(bundleTypeValues[0]);
144                }
145
146                EncodingEnum encodingEnum = null;
147                if (responseEncoding != null) {
148                        encodingEnum = responseEncoding.getEncoding();
149                }
150
151                Integer count = RestfulServerUtils.extractCountParameter(theRequest);
152                if (count == null) {
153                        count = pagingProvider.getDefaultPageSize();
154                } else if (count > pagingProvider.getMaximumPageSize()) {
155                        count = pagingProvider.getMaximumPageSize();
156                }
157
158                return createBundleFromBundleProvider(theServer, theRequest, count, linkSelf, includes, resultList, start, bundleType, encodingEnum, thePagingAction);
159        }
160
161        private void validateHaveBundleProvider(String thePagingAction, IBundleProvider theBundleProvider) {
162                // Return an HTTP 410 if the search is not known
163                if (theBundleProvider == null) {
164                        ourLog.info("Client requested unknown paging ID[{}]", thePagingAction);
165                        String msg = getContext().getLocalizer().getMessage(PageMethodBinding.class, "unknownSearchId", thePagingAction);
166                        throw new ResourceGoneException(msg);
167                }
168        }
169
170        @Nonnull
171        @Override
172        public RestOperationTypeEnum getRestOperationType() {
173                return RestOperationTypeEnum.GET_PAGE;
174        }
175
176        @Override
177        public boolean incomingServerRequestMatchesMethod(RequestDetails theRequest) {
178                String[] pageId = theRequest.getParameters().get(Constants.PARAM_PAGINGACTION);
179                if (pageId == null || pageId.length == 0 || isBlank(pageId[0])) {
180                        return false;
181                }
182                return theRequest.getRequestType() == RequestTypeEnum.GET;
183        }
184
185
186}