001package ca.uhn.fhir.rest.client.method;
002
003/*
004 * #%L
005 * HAPI FHIR - Client Framework
006 * %%
007 * Copyright (C) 2014 - 2021 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 */
022import static org.apache.commons.lang3.StringUtils.isBlank;
023import static org.apache.commons.lang3.StringUtils.isNotBlank;
024
025import java.lang.reflect.Method;
026import java.lang.reflect.Modifier;
027import java.util.Date;
028
029import ca.uhn.fhir.rest.param.DateParam;
030import ca.uhn.fhir.rest.param.DateRangeParam;
031import org.hl7.fhir.instance.model.api.*;
032
033import ca.uhn.fhir.context.FhirContext;
034import ca.uhn.fhir.model.api.IResource;
035import ca.uhn.fhir.model.valueset.BundleTypeEnum;
036import ca.uhn.fhir.rest.annotation.History;
037import ca.uhn.fhir.rest.api.Constants;
038import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
039import ca.uhn.fhir.rest.client.impl.BaseHttpClientInvocation;
040import ca.uhn.fhir.rest.param.ParameterUtil;
041import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
042
043public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
044
045        private final Integer myIdParamIndex;
046        private String myResourceName;
047        private final RestOperationTypeEnum myResourceOperationType;
048
049        public HistoryMethodBinding(Method theMethod, FhirContext theContext, Object theProvider) {
050                super(toReturnType(theMethod, theProvider), theMethod, theContext, theProvider);
051
052                myIdParamIndex = ParameterUtil.findIdParameterIndex(theMethod, getContext());
053
054                History historyAnnotation = theMethod.getAnnotation(History.class);
055                Class<? extends IBaseResource> type = historyAnnotation.type();
056                if (Modifier.isInterface(type.getModifiers())) {
057                        myResourceOperationType = RestOperationTypeEnum.HISTORY_SYSTEM;
058                } else {
059                        if (myIdParamIndex != null) {
060                                myResourceOperationType = RestOperationTypeEnum.HISTORY_INSTANCE;
061                        } else {
062                                myResourceOperationType = RestOperationTypeEnum.HISTORY_TYPE;
063                        }
064                }
065
066                if (type != IBaseResource.class && type != IResource.class) {
067                        myResourceName = theContext.getResourceType(type);
068                } else {
069                        myResourceName = null;
070                }
071
072        }
073
074        @Override
075        public RestOperationTypeEnum getRestOperationType() {
076                return myResourceOperationType;
077        }
078
079        @Override
080        protected BundleTypeEnum getResponseBundleType() {
081                return BundleTypeEnum.HISTORY;
082        }
083
084        @Override
085        public ReturnTypeEnum getReturnType() {
086                return ReturnTypeEnum.BUNDLE;
087        }
088
089        @Override
090        public BaseHttpClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
091                IIdType id = null;
092                String resourceName = myResourceName;
093                if (myIdParamIndex != null) {
094                        id = (IIdType) theArgs[myIdParamIndex];
095                        if (id == null || isBlank(id.getValue())) {
096                                throw new NullPointerException("ID can not be null");
097                        }
098                }
099
100                String historyId = id != null ? id.getIdPart() : null;
101                HttpGetClientInvocation retVal = createHistoryInvocation(getContext(), resourceName, historyId, null, null, null);
102
103                if (theArgs != null) {
104                        for (int idx = 0; idx < theArgs.length; idx++) {
105                                IParameter nextParam = getParameters().get(idx);
106                                nextParam.translateClientArgumentIntoQueryArgument(getContext(), theArgs[idx], retVal.getParameters(), null);
107                        }
108                }
109
110                return retVal;
111        }
112
113        public static HttpGetClientInvocation createHistoryInvocation(FhirContext theContext, String theResourceName, String theId, IPrimitiveType<Date> theSince, Integer theLimit, DateRangeParam theAt) {
114                StringBuilder b = new StringBuilder();
115                if (theResourceName != null) {
116                        b.append(theResourceName);
117                        if (isNotBlank(theId)) {
118                                b.append('/');
119                                b.append(theId);
120                        }
121                }
122                if (b.length() > 0) {
123                        b.append('/');
124                }
125                b.append(Constants.PARAM_HISTORY);
126
127                boolean haveParam = false;
128                if (theSince != null && !theSince.isEmpty()) {
129                        haveParam = true;
130                        b.append('?').append(Constants.PARAM_SINCE).append('=').append(theSince.getValueAsString());
131                }
132                if (theLimit != null) {
133                        b.append(haveParam ? '&' : '?');
134                        haveParam = true;
135                        b.append(Constants.PARAM_COUNT).append('=').append(theLimit);
136                }
137                if (theAt != null) {
138                        for (DateParam next : theAt.getValuesAsQueryTokens()) {
139                                b.append(haveParam ? '&' : '?');
140                                haveParam = true;
141                                b.append(Constants.PARAM_AT);
142                                b.append("=");
143                                b.append(next.getValueAsQueryToken(theContext));
144                        }
145                }
146
147                HttpGetClientInvocation retVal = new HttpGetClientInvocation(theContext, b.toString());
148                return retVal;
149        }
150
151        private static Class<? extends IBaseResource> toReturnType(Method theMethod, Object theProvider) {
152                History historyAnnotation = theMethod.getAnnotation(History.class);
153                Class<? extends IBaseResource> type = historyAnnotation.type();
154                if (type != IBaseResource.class && type != IResource.class) {
155                        return type;
156                }
157                return null;
158        }
159
160}