001/*
002 * #%L
003 * HAPI FHIR - Server 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.server.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.api.server.RequestDetails;
031import jakarta.annotation.Nonnull;
032import org.hl7.fhir.instance.model.api.IIdType;
033
034import java.lang.annotation.Annotation;
035import java.lang.reflect.Method;
036import java.util.Arrays;
037import java.util.Collections;
038import java.util.ListIterator;
039import java.util.Set;
040
041/**
042 * Base class for an operation that has a resource type but not a resource body in the
043 * request body
044 *
045 */
046public class PatchMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceIdButNoResourceBody {
047
048        private int myPatchTypeParameterIndex = -1;
049        private int myResourceParamIndex;
050
051        public PatchMethodBinding(Method theMethod, FhirContext theContext, Object theProvider) {
052                super(
053                                theMethod,
054                                theContext,
055                                theProvider,
056                                Patch.class,
057                                theMethod.getAnnotation(Patch.class).type(),
058                                theMethod.getAnnotation(Patch.class).typeName());
059
060                for (ListIterator<Class<?>> iter =
061                                                Arrays.asList(theMethod.getParameterTypes()).listIterator();
062                                iter.hasNext(); ) {
063                        int nextIndex = iter.nextIndex();
064                        Class<?> next = iter.next();
065                        if (next.equals(PatchTypeEnum.class)) {
066                                myPatchTypeParameterIndex = nextIndex;
067                        }
068                        for (Annotation nextAnnotation : theMethod.getParameterAnnotations()[nextIndex]) {
069                                if (nextAnnotation instanceof ResourceParam) {
070                                        myResourceParamIndex = nextIndex;
071                                }
072                        }
073                }
074
075                if (myPatchTypeParameterIndex == -1) {
076                        throw new ConfigurationException(Msg.code(370) + "Method has no parameter of type "
077                                        + PatchTypeEnum.class.getName() + " - " + theMethod.toString());
078                }
079                if (myResourceParamIndex == -1) {
080                        throw new ConfigurationException(Msg.code(371) + "Method has no parameter with @"
081                                        + ResourceParam.class.getSimpleName() + " annotation - " + theMethod.toString());
082                }
083        }
084
085        @Override
086        protected boolean allowVoidReturnType() {
087                return true;
088        }
089
090        @Override
091        public MethodMatchEnum incomingServerRequestMatchesMethod(RequestDetails theRequest) {
092                MethodMatchEnum retVal = super.incomingServerRequestMatchesMethod(theRequest);
093                if (retVal.ordinal() > MethodMatchEnum.NONE.ordinal()) {
094                        PatchTypeParameter.getTypeForRequestOrThrowInvalidRequestException(theRequest);
095                }
096                return retVal;
097        }
098
099        @Nonnull
100        @Override
101        public RestOperationTypeEnum getRestOperationType() {
102                return RestOperationTypeEnum.PATCH;
103        }
104
105        @Override
106        protected Set<RequestTypeEnum> provideAllowableRequestTypes() {
107                return Collections.singleton(RequestTypeEnum.PATCH);
108        }
109
110        @Override
111        protected void addParametersForServerRequest(RequestDetails theRequest, Object[] theParams) {
112                IIdType id = theRequest.getId();
113                id = UpdateMethodBinding.applyETagAsVersion(theRequest, id);
114                theParams[getIdParameterIndex()] = id;
115        }
116
117        @Override
118        protected String getMatchingOperation() {
119                return null;
120        }
121}