001package ca.uhn.fhir.rest.client.method; 002 003/* 004 * #%L 005 * HAPI FHIR - Client 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 */ 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.getResourceDefinition(type).getName(); 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}