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