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