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