001package ca.uhn.fhir.rest.method;
002
003/*
004 * #%L
005 * HAPI FHIR - Core Library
006 * %%
007 * Copyright (C) 2014 - 2016 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.isNotBlank;
023
024import java.lang.reflect.Method;
025import java.util.Collections;
026import java.util.Set;
027
028import org.hl7.fhir.instance.model.api.IIdType;
029
030import ca.uhn.fhir.context.FhirContext;
031import ca.uhn.fhir.model.api.IResource;
032import ca.uhn.fhir.model.primitive.IdDt;
033import ca.uhn.fhir.rest.annotation.Update;
034import ca.uhn.fhir.rest.api.MethodOutcome;
035import ca.uhn.fhir.rest.api.RequestTypeEnum;
036import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
037import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
038import ca.uhn.fhir.rest.server.Constants;
039import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
040
041public class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceParam {
042
043        private Integer myIdParameterIndex;
044
045        public UpdateMethodBinding(Method theMethod, FhirContext theContext, Object theProvider) {
046                super(theMethod, theContext, Update.class, theProvider);
047
048                myIdParameterIndex = MethodUtil.findIdParameterIndex(theMethod, getContext());
049        }
050
051        @Override
052        public RestOperationTypeEnum getRestOperationType() {
053                return RestOperationTypeEnum.UPDATE;
054        }
055
056        @Override
057        protected void addParametersForServerRequest(RequestDetails theRequest, Object[] theParams) {
058                /*
059                 * We are being a bit lenient here, since technically the client is supposed to include the version in the
060                 * Content-Location header, but we allow it in the PUT URL as well..
061                 */
062                String locationHeader = theRequest.getHeader(Constants.HEADER_CONTENT_LOCATION);
063                IIdType id = theRequest.getId();
064                if (isNotBlank(locationHeader)) {
065                        id.setValue(locationHeader);
066                        if (isNotBlank(id.getResourceType())) {
067                                if (!getResourceName().equals(id.getResourceType())) {
068                                        throw new InvalidRequestException("Attempting to update '" + getResourceName() + "' but content-location header specifies different resource type '" + id.getResourceType() + "' - header value: " + locationHeader);
069                                }
070                        }
071                }
072
073                String ifMatchValue = theRequest.getHeader(Constants.HEADER_IF_MATCH);
074                if (isNotBlank(ifMatchValue)) {
075                        ifMatchValue = MethodUtil.parseETagValue(ifMatchValue);
076                        if (id != null && id.hasVersionIdPart() == false) {
077                                id = id.withVersion(ifMatchValue);
078                        }
079                }
080                
081                if (theRequest.getId() != null && theRequest.getId().hasVersionIdPart() == false) {
082                        if (id != null && id.hasVersionIdPart()) {
083                                theRequest.getId().setValue(id.getValue());
084                        }
085                }
086
087                if (isNotBlank(locationHeader)) {
088                        MethodOutcome mo = new MethodOutcome();
089                        parseContentLocation(getContext(), mo, getResourceName(), locationHeader);
090                        if (mo.getId() == null || mo.getId().isEmpty()) {
091                                throw new InvalidRequestException("Invalid Content-Location header for resource " + getResourceName() + ": " + locationHeader);
092                        }
093                }
094
095                if (myIdParameterIndex != null) {
096                        theParams[myIdParameterIndex] = theRequest.getId();
097                }
098        }
099
100        @Override
101        protected BaseHttpClientInvocation createClientInvocation(Object[] theArgs, IResource theResource) {
102                IdDt idDt = (IdDt) theArgs[myIdParameterIndex];
103                if (idDt == null) {
104                        throw new NullPointerException("ID can not be null");
105                }
106
107                FhirContext context = getContext();
108
109                HttpPutClientInvocation retVal = MethodUtil.createUpdateInvocation(theResource, null, idDt, context);
110
111                for (int idx = 0; idx < theArgs.length; idx++) {
112                        IParameter nextParam = getParameters().get(idx);
113                        nextParam.translateClientArgumentIntoQueryArgument(getContext(), theArgs[idx], null, null);
114                }
115
116                return retVal;
117        }
118
119        /*
120         * @Override public boolean incomingServerRequestMatchesMethod(RequestDetails theRequest) { if
121         * (super.incomingServerRequestMatchesMethod(theRequest)) { if (myVersionIdParameterIndex != null) { if
122         * (theRequest.getVersionId() == null) { return false; } } else { if (theRequest.getVersionId() != null) { return
123         * false; } } return true; } else { return false; } }
124         */
125
126        @Override
127        protected Set<RequestTypeEnum> provideAllowableRequestTypes() {
128                return Collections.singleton(RequestTypeEnum.PUT);
129        }
130
131        @Override
132        protected String getMatchingOperation() {
133                return null;
134        }
135
136}