001package ca.uhn.fhir.rest.server.method; 002 003import static org.apache.commons.lang3.StringUtils.isBlank; 004/* 005 * #%L 006 * HAPI FHIR - Server Framework 007 * %% 008 * Copyright (C) 2014 - 2019 University Health Network 009 * %% 010 * Licensed under the Apache License, Version 2.0 (the "License"); 011 * you may not use this file except in compliance with the License. 012 * You may obtain a copy of the License at 013 * 014 * http://www.apache.org/licenses/LICENSE-2.0 015 * 016 * Unless required by applicable law or agreed to in writing, software 017 * distributed under the License is distributed on an "AS IS" BASIS, 018 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 019 * See the License for the specific language governing permissions and 020 * limitations under the License. 021 * #L% 022 */ 023import static org.apache.commons.lang3.StringUtils.isNotBlank; 024 025import java.lang.reflect.Method; 026import java.util.Collections; 027import java.util.Set; 028 029import org.hl7.fhir.instance.model.api.IBaseResource; 030import org.hl7.fhir.instance.model.api.IIdType; 031 032import ca.uhn.fhir.context.FhirContext; 033import ca.uhn.fhir.rest.annotation.Update; 034import ca.uhn.fhir.rest.api.Constants; 035import ca.uhn.fhir.rest.api.MethodOutcome; 036import ca.uhn.fhir.rest.api.RequestTypeEnum; 037import ca.uhn.fhir.rest.api.RestOperationTypeEnum; 038import ca.uhn.fhir.rest.api.server.RequestDetails; 039import ca.uhn.fhir.rest.param.ParameterUtil; 040import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; 041 042import javax.annotation.Nonnull; 043 044public class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceParam { 045 046 public UpdateMethodBinding(Method theMethod, FhirContext theContext, Object theProvider) { 047 super(theMethod, theContext, Update.class, theProvider); 048 } 049 050 @Override 051 protected void addParametersForServerRequest(RequestDetails theRequest, Object[] theParams) { 052 /* 053 * We are being a bit lenient here, since technically the client is supposed to include the version in the 054 * Content-Location header, but we allow it in the PUT URL as well.. 055 */ 056 String locationHeader = theRequest.getHeader(Constants.HEADER_CONTENT_LOCATION); 057 IIdType id = theRequest.getId(); 058 if (isNotBlank(locationHeader)) { 059 id.setValue(locationHeader); 060 if (isNotBlank(id.getResourceType())) { 061 if (!getResourceName().equals(id.getResourceType())) { 062 throw new InvalidRequestException( 063 "Attempting to update '" + getResourceName() + "' but content-location header specifies different resource type '" + id.getResourceType() + "' - header value: " + locationHeader); 064 } 065 } 066 } 067 068 id = applyETagAsVersion(theRequest, id); 069 070 if (theRequest.getId() != null && theRequest.getId().hasVersionIdPart() == false) { 071 if (id != null && id.hasVersionIdPart()) { 072 theRequest.getId().setValue(id.getValue()); 073 } 074 } 075 076 if (isNotBlank(locationHeader)) { 077 MethodOutcome mo = new MethodOutcome(); 078 parseContentLocation(getContext(), mo, locationHeader); 079 if (mo.getId() == null || mo.getId().isEmpty()) { 080 throw new InvalidRequestException("Invalid Content-Location header for resource " + getResourceName() + ": " + locationHeader); 081 } 082 } 083 084 super.addParametersForServerRequest(theRequest, theParams); 085 } 086 087 public static IIdType applyETagAsVersion(RequestDetails theRequest, IIdType id) { 088 String ifMatchValue = theRequest.getHeader(Constants.HEADER_IF_MATCH); 089 if (isNotBlank(ifMatchValue)) { 090 ifMatchValue = ParameterUtil.parseETagValue(ifMatchValue); 091 if (id != null && id.hasVersionIdPart() == false) { 092 id = id.withVersion(ifMatchValue); 093 } 094 } 095 return id; 096 } 097 098 @Override 099 protected String getMatchingOperation() { 100 return null; 101 } 102 103 @Nonnull 104 @Override 105 public RestOperationTypeEnum getRestOperationType() { 106 return RestOperationTypeEnum.UPDATE; 107 } 108 109 /* 110 * @Override public boolean incomingServerRequestMatchesMethod(RequestDetails theRequest) { if 111 * (super.incomingServerRequestMatchesMethod(theRequest)) { if (myVersionIdParameterIndex != null) { if 112 * (theRequest.getVersionId() == null) { return false; } } else { if (theRequest.getVersionId() != null) { return 113 * false; } } return true; } else { return false; } } 114 */ 115 116 @Override 117 protected Set<RequestTypeEnum> provideAllowableRequestTypes() { 118 return Collections.singleton(RequestTypeEnum.PUT); 119 } 120 121 @Override 122 protected void validateResourceIdAndUrlIdForNonConditionalOperation(IBaseResource theResource, String theResourceId, String theUrlId, String theMatchUrl) { 123 if (isBlank(theMatchUrl)) { 124 if (isBlank(theUrlId)) { 125 String msg = getContext().getLocalizer().getMessage(BaseOutcomeReturningMethodBindingWithResourceParam.class, "noIdInUrlForUpdate"); 126 throw new InvalidRequestException(msg); 127 } 128 if (isBlank(theResourceId)) { 129 String msg = getContext().getLocalizer().getMessage(BaseOutcomeReturningMethodBindingWithResourceParam.class, "noIdInBodyForUpdate"); 130 throw new InvalidRequestException(msg); 131 } 132 if (!theResourceId.equals(theUrlId)) { 133 String msg = getContext().getLocalizer().getMessage(BaseOutcomeReturningMethodBindingWithResourceParam.class, "incorrectIdForUpdate", theResourceId, theUrlId); 134 throw new InvalidRequestException(msg); 135 } 136 } else { 137 theResource.setId((IIdType) null); 138 } 139 140 } 141}