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.ConfigurationException; 023import ca.uhn.fhir.context.FhirContext; 024import ca.uhn.fhir.i18n.Msg; 025import ca.uhn.fhir.rest.annotation.Patch; 026import ca.uhn.fhir.rest.annotation.ResourceParam; 027import ca.uhn.fhir.rest.api.PatchTypeEnum; 028import ca.uhn.fhir.rest.api.RequestTypeEnum; 029import ca.uhn.fhir.rest.api.RestOperationTypeEnum; 030import ca.uhn.fhir.rest.client.impl.BaseHttpClientInvocation; 031import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; 032import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; 033import org.hl7.fhir.instance.model.api.IBaseResource; 034import org.hl7.fhir.instance.model.api.IIdType; 035 036import java.lang.annotation.Annotation; 037import java.lang.reflect.Method; 038import java.util.Arrays; 039import java.util.Collections; 040import java.util.List; 041import java.util.ListIterator; 042import java.util.Map; 043import java.util.Set; 044 045/** 046 * Base class for an operation that has a resource type but not a resource body in the 047 * request body 048 * 049 */ 050public class PatchMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceIdButNoResourceBody { 051 052 private int myPatchTypeParameterIndex = -1; 053 private int myResourceParamIndex; 054 055 public PatchMethodBinding(Method theMethod, FhirContext theContext, Object theProvider) { 056 super( 057 theMethod, 058 theContext, 059 theProvider, 060 Patch.class, 061 theMethod.getAnnotation(Patch.class).type()); 062 063 for (ListIterator<Class<?>> iter = 064 Arrays.asList(theMethod.getParameterTypes()).listIterator(); 065 iter.hasNext(); ) { 066 int nextIndex = iter.nextIndex(); 067 Class<?> next = iter.next(); 068 if (next.equals(PatchTypeEnum.class)) { 069 myPatchTypeParameterIndex = nextIndex; 070 } 071 for (Annotation nextAnnotation : theMethod.getParameterAnnotations()[nextIndex]) { 072 if (nextAnnotation instanceof ResourceParam) { 073 myResourceParamIndex = nextIndex; 074 } 075 } 076 } 077 078 if (myPatchTypeParameterIndex == -1) { 079 throw new ConfigurationException(Msg.code(1414) + "Method has no parameter of type " 080 + PatchTypeEnum.class.getName() + " - " + theMethod.toString()); 081 } 082 if (myResourceParamIndex == -1) { 083 throw new ConfigurationException(Msg.code(1415) + "Method has no parameter with @" 084 + ResourceParam.class.getSimpleName() + " annotation - " + theMethod.toString()); 085 } 086 } 087 088 @Override 089 public RestOperationTypeEnum getRestOperationType() { 090 return RestOperationTypeEnum.PATCH; 091 } 092 093 @Override 094 protected Set<RequestTypeEnum> provideAllowableRequestTypes() { 095 return Collections.singleton(RequestTypeEnum.PATCH); 096 } 097 098 @Override 099 protected BaseHttpClientInvocation createClientInvocation(Object[] theArgs, IBaseResource theResource) { 100 StringBuilder urlExtension = new StringBuilder(); 101 urlExtension.append(getContext().getResourceType(theResource)); 102 103 return new HttpPostClientInvocation(getContext(), theResource, urlExtension.toString()); 104 } 105 106 @Override 107 protected boolean allowVoidReturnType() { 108 return true; 109 } 110 111 @Override 112 public BaseHttpClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException { 113 IIdType idDt = (IIdType) theArgs[getIdParameterIndex()]; 114 if (idDt == null) { 115 throw new NullPointerException(Msg.code(1416) + "ID can not be null"); 116 } 117 118 if (idDt.hasResourceType() == false) { 119 idDt = idDt.withResourceType(getResourceName()); 120 } else if (getResourceName().equals(idDt.getResourceType()) == false) { 121 throw new InvalidRequestException(Msg.code(1417) + "ID parameter has the wrong resource type, expected '" 122 + getResourceName() + "', found: " + idDt.getResourceType()); 123 } 124 125 PatchTypeEnum patchType = (PatchTypeEnum) theArgs[myPatchTypeParameterIndex]; 126 String body = (String) theArgs[myResourceParamIndex]; 127 128 HttpPatchClientInvocation retVal = createPatchInvocation(getContext(), idDt, patchType, body); 129 130 for (int idx = 0; idx < theArgs.length; idx++) { 131 IParameter nextParam = getParameters().get(idx); 132 nextParam.translateClientArgumentIntoQueryArgument(getContext(), theArgs[idx], null, null); 133 } 134 135 return retVal; 136 } 137 138 public static HttpPatchClientInvocation createPatchInvocation( 139 FhirContext theContext, IIdType theId, PatchTypeEnum thePatchType, String theBody) { 140 HttpPatchClientInvocation retVal = 141 new HttpPatchClientInvocation(theContext, theId, thePatchType.getContentType(), theBody); 142 return retVal; 143 } 144 145 public static HttpPatchClientInvocation createPatchInvocation( 146 FhirContext theContext, String theUrlPath, PatchTypeEnum thePatchType, String theBody) { 147 HttpPatchClientInvocation retVal = 148 new HttpPatchClientInvocation(theContext, theUrlPath, thePatchType.getContentType(), theBody); 149 return retVal; 150 } 151 152 @Override 153 protected String getMatchingOperation() { 154 return null; 155 } 156 157 public static HttpPatchClientInvocation createPatchInvocation( 158 FhirContext theContext, 159 PatchTypeEnum thePatchType, 160 String theBody, 161 String theResourceType, 162 Map<String, List<String>> theMatchParams) { 163 StringBuilder urlBuilder = MethodUtil.createUrl(theResourceType, theMatchParams); 164 String url = urlBuilder.toString(); 165 HttpPatchClientInvocation retVal = 166 new HttpPatchClientInvocation(theContext, url, thePatchType.getContentType(), theBody); 167 return retVal; 168 } 169}