Add conditional deletes and updates
This commit is contained in:
parent
819dc67d71
commit
c2a6e78e67
|
@ -10,6 +10,7 @@ import ca.uhn.fhir.model.base.resource.BaseConformance;
|
||||||
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
|
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
|
||||||
import ca.uhn.fhir.model.dstu2.valueset.AdministrativeGenderEnum;
|
import ca.uhn.fhir.model.dstu2.valueset.AdministrativeGenderEnum;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Observation;
|
import ca.uhn.fhir.model.dstu2.resource.Observation;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Organization;
|
import ca.uhn.fhir.model.dstu2.resource.Organization;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
@ -68,6 +69,30 @@ public class GenericClientExample {
|
||||||
System.out.println("Got ID: " + id.getValue());
|
System.out.println("Got ID: " + id.getValue());
|
||||||
// END SNIPPET: create
|
// END SNIPPET: create
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
Patient patient = new Patient();
|
||||||
|
// START SNIPPET: createConditional
|
||||||
|
// One form
|
||||||
|
MethodOutcome outcome = client.create()
|
||||||
|
.resource(patient)
|
||||||
|
.conditionalByUrl("Patient?identifier=system%7C00001")
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
// Another form
|
||||||
|
MethodOutcome outcome2 = client.create()
|
||||||
|
.resource(patient)
|
||||||
|
.conditional()
|
||||||
|
.where(Patient.IDENTIFIER.exactly().systemAndIdentifier("system", "00001"))
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
// This will return true if the server responded with an HTTP 201 created,
|
||||||
|
// otherwise it will return null.
|
||||||
|
Boolean created = outcome.getCreated();
|
||||||
|
|
||||||
|
// The ID of the created, or the pre-existing resource
|
||||||
|
IdDt id = outcome.getId();
|
||||||
|
// END SNIPPET: createConditional
|
||||||
|
}
|
||||||
{
|
{
|
||||||
// START SNIPPET: update
|
// START SNIPPET: update
|
||||||
Patient patient = new Patient();
|
Patient patient = new Patient();
|
||||||
|
@ -95,6 +120,21 @@ public class GenericClientExample {
|
||||||
System.out.println("Got ID: " + id.getValue());
|
System.out.println("Got ID: " + id.getValue());
|
||||||
// END SNIPPET: update
|
// END SNIPPET: update
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
Patient patient = new Patient();
|
||||||
|
// START SNIPPET: updateConditional
|
||||||
|
client.update()
|
||||||
|
.resource(patient)
|
||||||
|
.conditionalByUrl("Patient?identifier=system%7C00001")
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
client.update()
|
||||||
|
.resource(patient)
|
||||||
|
.conditional()
|
||||||
|
.where(Patient.IDENTIFIER.exactly().systemAndIdentifier("system", "00001"))
|
||||||
|
.execute();
|
||||||
|
// END SNIPPET: updateConditional
|
||||||
|
}
|
||||||
{
|
{
|
||||||
// START SNIPPET: etagupdate
|
// START SNIPPET: etagupdate
|
||||||
// First, let's retrive the latest version of a resource
|
// First, let's retrive the latest version of a resource
|
||||||
|
@ -132,16 +172,27 @@ public class GenericClientExample {
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
// START SNIPPET: delete
|
// START SNIPPET: delete
|
||||||
// Retrieve the server's conformance statement and print its
|
BaseOperationOutcome resp = client.delete().resourceById(new IdDt("Patient", "1234")).execute();
|
||||||
// description
|
|
||||||
BaseOperationOutcome outcome = client.delete().resourceById(new IdDt("Patient", "1234")).execute();
|
|
||||||
|
|
||||||
// outcome may be null if the server didn't return one
|
// outcome may be null if the server didn't return one
|
||||||
if (outcome != null) {
|
if (resp != null) {
|
||||||
|
OperationOutcome outcome = (OperationOutcome) resp;
|
||||||
System.out.println(outcome.getIssueFirstRep().getDetailsElement().getValue());
|
System.out.println(outcome.getIssueFirstRep().getDetailsElement().getValue());
|
||||||
}
|
}
|
||||||
// END SNIPPET: delete
|
// END SNIPPET: delete
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
// START SNIPPET: deleteConditional
|
||||||
|
client.delete()
|
||||||
|
.resourceConditionalByUrl("Patient?identifier=system%7C00001")
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
client.delete()
|
||||||
|
.resourceConditionalByType("Patient")
|
||||||
|
.where(Patient.IDENTIFIER.exactly().systemAndIdentifier("system", "00001"))
|
||||||
|
.execute();
|
||||||
|
// END SNIPPET: deleteConditional
|
||||||
|
}
|
||||||
{
|
{
|
||||||
// START SNIPPET: search
|
// START SNIPPET: search
|
||||||
Bundle response = client.search()
|
Bundle response = client.search()
|
||||||
|
|
|
@ -34,6 +34,7 @@ import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||||
import ca.uhn.fhir.parser.DataFormatException;
|
import ca.uhn.fhir.parser.DataFormatException;
|
||||||
import ca.uhn.fhir.rest.annotation.AddTags;
|
import ca.uhn.fhir.rest.annotation.AddTags;
|
||||||
|
import ca.uhn.fhir.rest.annotation.ConditionalOperationParam;
|
||||||
import ca.uhn.fhir.rest.annotation.Count;
|
import ca.uhn.fhir.rest.annotation.Count;
|
||||||
import ca.uhn.fhir.rest.annotation.Create;
|
import ca.uhn.fhir.rest.annotation.Create;
|
||||||
import ca.uhn.fhir.rest.annotation.DeleteTags;
|
import ca.uhn.fhir.rest.annotation.DeleteTags;
|
||||||
|
@ -324,6 +325,23 @@ public void deletePatient(@IdParam IdDt theId) {
|
||||||
//END SNIPPET: delete
|
//END SNIPPET: delete
|
||||||
|
|
||||||
|
|
||||||
|
//START SNIPPET: deleteConditional
|
||||||
|
@Read()
|
||||||
|
public void deletePatientConditional(@IdParam IdDt theId, @ConditionalOperationParam String theConditionalUrl) {
|
||||||
|
// Only one of theId or theConditionalUrl will have a value depending
|
||||||
|
// on whether the URL receieved was a logical ID, or a conditional
|
||||||
|
// search string
|
||||||
|
if (theId != null) {
|
||||||
|
// do a normal delete
|
||||||
|
} else {
|
||||||
|
// do a conditional delete
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise, delete was successful
|
||||||
|
return; // can also return MethodOutcome
|
||||||
|
}
|
||||||
|
//END SNIPPET: deleteConditional
|
||||||
|
|
||||||
//START SNIPPET: history
|
//START SNIPPET: history
|
||||||
@History()
|
@History()
|
||||||
public List<Patient> getPatientHistory(@IdParam IdDt theId) {
|
public List<Patient> getPatientHistory(@IdParam IdDt theId) {
|
||||||
|
@ -681,6 +699,25 @@ public MethodOutcome createPatient(@ResourceParam Patient thePatient) {
|
||||||
public abstract MethodOutcome createNewPatient(@ResourceParam Patient thePatient);
|
public abstract MethodOutcome createNewPatient(@ResourceParam Patient thePatient);
|
||||||
//END SNIPPET: createClient
|
//END SNIPPET: createClient
|
||||||
|
|
||||||
|
//START SNIPPET: updateConditional
|
||||||
|
@Update
|
||||||
|
public MethodOutcome updatePatientConditional(
|
||||||
|
@ResourceParam Patient thePatient,
|
||||||
|
@IdParam IdDt theId,
|
||||||
|
@ConditionalOperationParam String theConditional) {
|
||||||
|
|
||||||
|
// Only one of theId or theConditional will have a value and the other will be null,
|
||||||
|
// depending on the URL passed into the server.
|
||||||
|
if (theConditional != null) {
|
||||||
|
// Do a conditional update. theConditional will have a value like "Patient?identifier=system%7C00001"
|
||||||
|
} else {
|
||||||
|
// Do a normal update. theId will have the identity of the resource to update
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MethodOutcome(); // populate this
|
||||||
|
}
|
||||||
|
//END SNIPPET: updateConditional
|
||||||
|
|
||||||
//START SNIPPET: update
|
//START SNIPPET: update
|
||||||
@Update
|
@Update
|
||||||
public MethodOutcome updatePatient(@IdParam IdDt theId, @ResourceParam Patient thePatient) {
|
public MethodOutcome updatePatient(@IdParam IdDt theId, @ResourceParam Patient thePatient) {
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
package ca.uhn.fhir.rest.annotation;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - Core Library
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2015 University Health Network
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.PARAMETER)
|
||||||
|
public @interface ConditionalOperationParam {
|
||||||
|
// just a marker
|
||||||
|
}
|
|
@ -131,6 +131,10 @@ public class MethodOutcome {
|
||||||
return myVersionId;
|
return myVersionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This will be set to {@link Boolean#TRUE} for instance of MethodOutcome which are
|
||||||
|
* returned to client instances, if the server has responded with an HTTP 201 Created.
|
||||||
|
*/
|
||||||
public Boolean getCreated() {
|
public Boolean getCreated() {
|
||||||
return myCreated;
|
return myCreated;
|
||||||
}
|
}
|
||||||
|
|
|
@ -273,11 +273,16 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
|
||||||
BufferedReader requestReader = theRequest.getServletRequest().getReader();
|
BufferedReader requestReader = theRequest.getServletRequest().getReader();
|
||||||
|
|
||||||
Class<? extends IBaseResource> wantedResourceType = requestContainsResourceType();
|
Class<? extends IBaseResource> wantedResourceType = requestContainsResourceType();
|
||||||
|
IResource retVal;
|
||||||
if (wantedResourceType != null) {
|
if (wantedResourceType != null) {
|
||||||
return (IResource) parser.parseResource(wantedResourceType, requestReader);
|
retVal = (IResource) parser.parseResource(wantedResourceType, requestReader);
|
||||||
} else {
|
} else {
|
||||||
return parser.parseResource(requestReader);
|
retVal = parser.parseResource(requestReader);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
retVal.setId(theRequest.getId());
|
||||||
|
|
||||||
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract Set<RequestType> provideAllowableRequestTypes();
|
protected abstract Set<RequestType> provideAllowableRequestTypes();
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
package ca.uhn.fhir.rest.method;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - Core Library
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2015 University Health Network
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
|
||||||
|
class ConditionalParamBinder implements IParameter {
|
||||||
|
|
||||||
|
ConditionalParamBinder() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments) throws InternalErrorException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object translateQueryParametersIntoServerArgument(Request theRequest, Object theRequestContents) throws InternalErrorException, InvalidRequestException {
|
||||||
|
if (theRequest.getId() != null && theRequest.getId().hasIdPart()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int questionMarkIndex = theRequest.getCompleteUrl().indexOf('?');
|
||||||
|
return theRequest.getResourceName() + theRequest.getCompleteUrl().substring(questionMarkIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initializeTypes(Method theMethod, Class<? extends Collection<?>> theOuterCollectionType, Class<? extends Collection<?>> theInnerCollectionType, Class<?> theParameterType) {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -47,6 +47,7 @@ import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||||
import ca.uhn.fhir.parser.IParser;
|
import ca.uhn.fhir.parser.IParser;
|
||||||
|
import ca.uhn.fhir.rest.annotation.ConditionalOperationParam;
|
||||||
import ca.uhn.fhir.rest.annotation.Count;
|
import ca.uhn.fhir.rest.annotation.Count;
|
||||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||||
import ca.uhn.fhir.rest.annotation.IncludeParam;
|
import ca.uhn.fhir.rest.annotation.IncludeParam;
|
||||||
|
@ -289,6 +290,10 @@ public class MethodUtil {
|
||||||
return MethodUtil.findParamAnnotationIndex(theMethod, TagListParam.class);
|
return MethodUtil.findParamAnnotationIndex(theMethod, TagListParam.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Integer findConditionalOperationParameterIndex(Method theMethod) {
|
||||||
|
return MethodUtil.findParamAnnotationIndex(theMethod, ConditionalOperationParam.class);
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public static Integer findVersionIdParameterIndex(Method theMethod) {
|
public static Integer findVersionIdParameterIndex(Method theMethod) {
|
||||||
return MethodUtil.findParamAnnotationIndex(theMethod, VersionIdParam.class);
|
return MethodUtil.findParamAnnotationIndex(theMethod, VersionIdParam.class);
|
||||||
|
@ -390,6 +395,8 @@ public class MethodUtil {
|
||||||
param = new SortParameter();
|
param = new SortParameter();
|
||||||
} else if (nextAnnotation instanceof TransactionParam) {
|
} else if (nextAnnotation instanceof TransactionParam) {
|
||||||
param = new TransactionParamBinder(theContext);
|
param = new TransactionParamBinder(theContext);
|
||||||
|
} else if (nextAnnotation instanceof ConditionalOperationParam) {
|
||||||
|
param = new ConditionalParamBinder();
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,19 +20,17 @@ package ca.uhn.fhir.rest.method;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.*;
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.ConfigurationException;
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.model.api.IResource;
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
|
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
|
||||||
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
|
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
|
||||||
import ca.uhn.fhir.rest.annotation.Update;
|
import ca.uhn.fhir.rest.annotation.Update;
|
||||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
||||||
|
@ -43,16 +41,11 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceParam {
|
class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceParam {
|
||||||
|
|
||||||
private Integer myIdParameterIndex;
|
private Integer myIdParameterIndex;
|
||||||
private Integer myVersionIdParameterIndex;
|
|
||||||
|
|
||||||
public UpdateMethodBinding(Method theMethod, FhirContext theContext, Object theProvider) {
|
public UpdateMethodBinding(Method theMethod, FhirContext theContext, Object theProvider) {
|
||||||
super(theMethod, theContext, Update.class, theProvider);
|
super(theMethod, theContext, Update.class, theProvider);
|
||||||
|
|
||||||
myIdParameterIndex = MethodUtil.findIdParameterIndex(theMethod);
|
myIdParameterIndex = MethodUtil.findIdParameterIndex(theMethod);
|
||||||
if (myIdParameterIndex == null) {
|
|
||||||
throw new ConfigurationException("Method '" + theMethod.getName() + "' on type '" + theMethod.getDeclaringClass().getCanonicalName() + "' has no parameter annotated with the @" + IdParam.class.getSimpleName() + " annotation");
|
|
||||||
}
|
|
||||||
myVersionIdParameterIndex = MethodUtil.findVersionIdParameterIndex(theMethod);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -92,7 +85,7 @@ class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceP
|
||||||
|
|
||||||
if (theRequest.getId() != null && theRequest.getId().hasVersionIdPart() == false) {
|
if (theRequest.getId() != null && theRequest.getId().hasVersionIdPart() == false) {
|
||||||
if (id != null && id.hasVersionIdPart()) {
|
if (id != null && id.hasVersionIdPart()) {
|
||||||
theRequest.setId(id);
|
theRequest.getId().setValue(id.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,9 +97,8 @@ class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceP
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
theParams[myIdParameterIndex] = theRequest.getId();
|
if (myIdParameterIndex != null) {
|
||||||
if (myVersionIdParameterIndex != null) {
|
theParams[myIdParameterIndex] = theRequest.getId();
|
||||||
theParams[myVersionIdParameterIndex] = theRequest.getId();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,12 +109,6 @@ class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceP
|
||||||
throw new NullPointerException("ID can not be null");
|
throw new NullPointerException("ID can not be null");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (myVersionIdParameterIndex != null) {
|
|
||||||
IdDt versionIdDt = (IdDt) theArgs[myVersionIdParameterIndex];
|
|
||||||
if (idDt.hasVersionIdPart() == false) {
|
|
||||||
idDt = idDt.withVersion(versionIdDt.getIdPart());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FhirContext context = getContext();
|
FhirContext context = getContext();
|
||||||
|
|
||||||
HttpPutClientInvocation retVal = MethodUtil.createUpdateInvocation(theResource, null, idDt, context);
|
HttpPutClientInvocation retVal = MethodUtil.createUpdateInvocation(theResource, null, idDt, context);
|
||||||
|
|
|
@ -119,6 +119,32 @@ public class GenericClientTest {
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreatePopulatesIsCreated() throws Exception {
|
||||||
|
|
||||||
|
Patient p1 = new Patient();
|
||||||
|
p1.addIdentifier("foo:bar", "12345");
|
||||||
|
p1.addName().addFamily("Smith").addGiven("John");
|
||||||
|
|
||||||
|
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||||
|
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||||
|
when(myHttpResponse.getAllHeaders()).thenReturn(new Header[] { new BasicHeader(Constants.HEADER_LOCATION, "/Patient/44/_history/22") });
|
||||||
|
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||||
|
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8")));
|
||||||
|
|
||||||
|
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||||
|
|
||||||
|
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 201, "OK"));
|
||||||
|
MethodOutcome resp = client.create().resource(ourCtx.newXmlParser().encodeResourceToString(p1)).execute();
|
||||||
|
assertTrue(resp.getCreated());
|
||||||
|
|
||||||
|
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||||
|
resp = client.create().resource(ourCtx.newXmlParser().encodeResourceToString(p1)).execute();
|
||||||
|
assertNull(resp.getCreated());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreateWithStringAutoDetectsEncoding() throws Exception {
|
public void testCreateWithStringAutoDetectsEncoding() throws Exception {
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package ca.uhn.fhir.model.dev;
|
package ca.uhn.fhir.model.dstu2;
|
||||||
|
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
|
@ -0,0 +1,125 @@
|
||||||
|
package ca.uhn.fhir.rest.server;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.apache.http.HttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpDelete;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
import org.apache.http.impl.client.HttpClientBuilder;
|
||||||
|
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||||
|
import org.eclipse.jetty.server.Server;
|
||||||
|
import org.eclipse.jetty.servlet.ServletHandler;
|
||||||
|
import org.eclipse.jetty.servlet.ServletHolder;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||||
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
import ca.uhn.fhir.rest.annotation.ConditionalOperationParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Delete;
|
||||||
|
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||||
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
|
import ca.uhn.fhir.util.PortUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by dsotnikov on 2/25/2014.
|
||||||
|
*/
|
||||||
|
public class DeleteConditionalTest {
|
||||||
|
private static CloseableHttpClient ourClient;
|
||||||
|
private static String ourLastConditionalUrl;
|
||||||
|
private static int ourPort;
|
||||||
|
|
||||||
|
private static Server ourServer;
|
||||||
|
private static IdDt ourLastIdParam;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void before() {
|
||||||
|
ourLastConditionalUrl = null;
|
||||||
|
ourLastIdParam = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateWithConditionalUrl() throws Exception {
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.addIdentifier().setValue("002");
|
||||||
|
|
||||||
|
HttpDelete httpPost = new HttpDelete("http://localhost:" + ourPort + "/Patient?identifier=system%7C001");
|
||||||
|
|
||||||
|
HttpResponse status = ourClient.execute(httpPost);
|
||||||
|
|
||||||
|
assertEquals(204, status.getStatusLine().getStatusCode());
|
||||||
|
|
||||||
|
assertNull(ourLastIdParam);
|
||||||
|
assertEquals("Patient?identifier=system%7C001", ourLastConditionalUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateWithoutConditionalUrl() throws Exception {
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.addIdentifier().setValue("002");
|
||||||
|
|
||||||
|
HttpDelete httpPost = new HttpDelete("http://localhost:" + ourPort + "/Patient/2");
|
||||||
|
|
||||||
|
HttpResponse status = ourClient.execute(httpPost);
|
||||||
|
|
||||||
|
assertEquals(204, status.getStatusLine().getStatusCode());
|
||||||
|
|
||||||
|
assertEquals("Patient/2", ourLastIdParam.toUnqualified().getValue());
|
||||||
|
assertNull(ourLastConditionalUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void afterClass() throws Exception {
|
||||||
|
ourServer.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void beforeClass() throws Exception {
|
||||||
|
ourPort = PortUtil.findFreePort();
|
||||||
|
ourServer = new Server(ourPort);
|
||||||
|
|
||||||
|
PatientProvider patientProvider = new PatientProvider();
|
||||||
|
|
||||||
|
ServletHandler proxyHandler = new ServletHandler();
|
||||||
|
RestfulServer servlet = new RestfulServer();
|
||||||
|
servlet.setResourceProviders(patientProvider);
|
||||||
|
ServletHolder servletHolder = new ServletHolder(servlet);
|
||||||
|
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
||||||
|
ourServer.setHandler(proxyHandler);
|
||||||
|
ourServer.start();
|
||||||
|
|
||||||
|
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||||
|
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||||
|
builder.setConnectionManager(connectionManager);
|
||||||
|
ourClient = builder.build();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PatientProvider implements IResourceProvider {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends IResource> getResourceType() {
|
||||||
|
return Patient.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Delete()
|
||||||
|
public MethodOutcome updatePatient(@ConditionalOperationParam String theConditional, @IdParam IdDt theIdParam) {
|
||||||
|
ourLastConditionalUrl = theConditional;
|
||||||
|
ourLastIdParam = theIdParam;
|
||||||
|
return new MethodOutcome(new IdDt("Patient/001/_history/002"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -31,6 +31,7 @@ import ca.uhn.fhir.model.dstu2.resource.OperationOutcome;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Organization;
|
import ca.uhn.fhir.model.dstu2.resource.Organization;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
import ca.uhn.fhir.rest.annotation.ConditionalOperationParam;
|
||||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||||
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||||
import ca.uhn.fhir.rest.annotation.Update;
|
import ca.uhn.fhir.rest.annotation.Update;
|
||||||
|
@ -42,17 +43,30 @@ import ca.uhn.fhir.util.PortUtil;
|
||||||
*/
|
*/
|
||||||
public class UpdateConditionalTest {
|
public class UpdateConditionalTest {
|
||||||
private static CloseableHttpClient ourClient;
|
private static CloseableHttpClient ourClient;
|
||||||
|
private static String ourLastConditionalUrl;
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(UpdateConditionalTest.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(UpdateConditionalTest.class);
|
||||||
private static int ourPort;
|
private static int ourPort;
|
||||||
|
|
||||||
private static Server ourServer;
|
private static Server ourServer;
|
||||||
|
private static IdDt ourLastId;
|
||||||
|
private static IdDt ourLastIdParam;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void before() {
|
||||||
|
ourLastId = null;
|
||||||
|
ourLastConditionalUrl = null;
|
||||||
|
ourLastIdParam = null;
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUpdate() throws Exception {
|
public void testUpdateWithConditionalUrl() throws Exception {
|
||||||
|
|
||||||
Patient patient = new Patient();
|
Patient patient = new Patient();
|
||||||
patient.addIdentifier().setValue("002");
|
patient.addIdentifier().setValue("002");
|
||||||
|
|
||||||
HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient/001");
|
HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient?identifier=system%7C001");
|
||||||
httpPost.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
httpPost.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||||
|
|
||||||
HttpResponse status = ourClient.execute(httpPost);
|
HttpResponse status = ourClient.execute(httpPost);
|
||||||
|
@ -62,22 +76,48 @@ public class UpdateConditionalTest {
|
||||||
|
|
||||||
ourLog.info("Response was:\n{}", responseContent);
|
ourLog.info("Response was:\n{}", responseContent);
|
||||||
|
|
||||||
OperationOutcome oo = new FhirContext().newXmlParser().parseResource(OperationOutcome.class, responseContent);
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
assertEquals("OODETAILS", oo.getIssueFirstRep().getDetails());
|
assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue());
|
||||||
|
assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue());
|
||||||
|
|
||||||
|
assertNull(ourLastId.getValue());
|
||||||
|
assertNull(ourLastIdParam);
|
||||||
|
assertEquals("Patient?identifier=system%7C001", ourLastConditionalUrl);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateWithoutConditionalUrl() throws Exception {
|
||||||
|
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.addIdentifier().setValue("002");
|
||||||
|
|
||||||
|
HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient/2");
|
||||||
|
httpPost.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||||
|
|
||||||
|
HttpResponse status = ourClient.execute(httpPost);
|
||||||
|
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
ourLog.info("Response was:\n{}", responseContent);
|
||||||
|
|
||||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue());
|
assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue());
|
||||||
assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue());
|
assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue());
|
||||||
|
|
||||||
|
assertEquals("Patient/2", ourLastId.toUnqualified().getValue());
|
||||||
|
assertEquals("Patient/2", ourLastIdParam.toUnqualified().getValue());
|
||||||
|
assertNull(ourLastConditionalUrl);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
public static void afterClass() throws Exception {
|
public static void afterClass() throws Exception {
|
||||||
ourServer.stop();
|
ourServer.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void beforeClass() throws Exception {
|
public static void beforeClass() throws Exception {
|
||||||
ourPort = PortUtil.findFreePort();
|
ourPort = PortUtil.findFreePort();
|
||||||
|
@ -99,14 +139,6 @@ public class UpdateConditionalTest {
|
||||||
ourClient = builder.build();
|
ourClient = builder.build();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String ourLastConditionalUrl;
|
|
||||||
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void before() {
|
|
||||||
ourLastConditionalUrl=null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class PatientProvider implements IResourceProvider {
|
public static class PatientProvider implements IResourceProvider {
|
||||||
|
|
||||||
|
@ -117,15 +149,11 @@ public class UpdateConditionalTest {
|
||||||
|
|
||||||
|
|
||||||
@Update()
|
@Update()
|
||||||
public MethodOutcome updatePatient(@IdParam IdDt theId, @ResourceParam Patient thePatient) {
|
public MethodOutcome updatePatient(@ResourceParam Patient thePatient, @ConditionalOperationParam String theConditional, @IdParam IdDt theIdParam) {
|
||||||
IdDt id = theId.withVersion(thePatient.getIdentifierFirstRep().getValue());
|
ourLastConditionalUrl = theConditional;
|
||||||
OperationOutcome oo = new OperationOutcome();
|
ourLastId = thePatient.getId();
|
||||||
oo.addIssue().setDetails("OODETAILS");
|
ourLastIdParam = theIdParam;
|
||||||
if (theId.getValueAsString().contains("CREATE")) {
|
return new MethodOutcome(new IdDt("Patient/001/_history/002"));
|
||||||
return new MethodOutcome(id,oo, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new MethodOutcome(id,oo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,25 @@
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
.well {
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-list {
|
||||||
|
padding-right: 0px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-list LI {
|
||||||
|
line-height: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
.nav-list .divider {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
body {
|
body {
|
||||||
padding-top: 40px;
|
padding-top: 40px;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
|
@ -126,6 +145,7 @@ h4 {
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
margin-bottom: 0px;
|
margin-bottom: 0px;
|
||||||
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
li.expanded ul {
|
li.expanded ul {
|
||||||
|
|
|
@ -12,9 +12,6 @@
|
||||||
<!-- The body of the document contains a number of sections -->
|
<!-- The body of the document contains a number of sections -->
|
||||||
<section name="Creating a RESTful Client">
|
<section name="Creating a RESTful Client">
|
||||||
|
|
||||||
<macro name="toc">
|
|
||||||
</macro>
|
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
HAPI provides a built-in mechanism for connecting to FHIR RESTful
|
HAPI provides a built-in mechanism for connecting to FHIR RESTful
|
||||||
servers.
|
servers.
|
||||||
|
@ -151,13 +148,23 @@
|
||||||
value="examples/src/main/java/example/GenericClientExample.java" />
|
value="examples/src/main/java/example/GenericClientExample.java" />
|
||||||
</macro>
|
</macro>
|
||||||
|
|
||||||
<h4>Search - Using HTTP POST or GET with _search</h4>
|
<h4>Search - Using HTTP POST</h4>
|
||||||
|
<p>
|
||||||
|
The FHIR specification allows the use of an HTTP POST to transmit a search to a server instead of using
|
||||||
|
an HTTP GET. With this style of search, the search parameters are included in the request body instead
|
||||||
|
of the request URL, which can be useful if you need to transmit a search with a large number
|
||||||
|
of parameters.
|
||||||
|
</p>
|
||||||
<p>
|
<p>
|
||||||
The FHIR specification allows several styles of search (HTTP POST, a GET with _search at the end of the URL, etc.)
|
|
||||||
The <code>usingStyle()</code> method controls which style to use. By default, GET style is used
|
The <code>usingStyle()</code> method controls which style to use. By default, GET style is used
|
||||||
unless the client detects that the request would result in a very long URL (over 8000 chars) in which
|
unless the client detects that the request would result in a very long URL (over 8000 chars) in which
|
||||||
case the client automatically switches to POST.
|
case the client automatically switches to POST.
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
An alternate form of the search URL (using a URL ending with <code>_search</code>) was also
|
||||||
|
supported in FHIR DSTU1. This form is no longer valid in FHIR DSTU2, but HAPI retains support
|
||||||
|
for using this form in order to interoperate with servers which use it.
|
||||||
|
</p>
|
||||||
<macro name="snippet">
|
<macro name="snippet">
|
||||||
<param name="id" value="searchPost" />
|
<param name="id" value="searchPost" />
|
||||||
<param name="file"
|
<param name="file"
|
||||||
|
@ -189,6 +196,19 @@
|
||||||
<param name="file"
|
<param name="file"
|
||||||
value="examples/src/main/java/example/GenericClientExample.java" />
|
value="examples/src/main/java/example/GenericClientExample.java" />
|
||||||
</macro>
|
</macro>
|
||||||
|
|
||||||
|
<h4>Conditional Creates</h4>
|
||||||
|
<p>
|
||||||
|
FHIR also specifies a type of update called "conditional create", where
|
||||||
|
a set of search parameters are provided and a new resource is only
|
||||||
|
created if no existing resource matches those parameters. See the
|
||||||
|
FHIR specification for more information on conditional creation.
|
||||||
|
</p>
|
||||||
|
<macro name="snippet">
|
||||||
|
<param name="id" value="updateConditional" />
|
||||||
|
<param name="file"
|
||||||
|
value="examples/src/main/java/example/GenericClientExample.java" />
|
||||||
|
</macro>
|
||||||
</subsection>
|
</subsection>
|
||||||
|
|
||||||
<subsection name="Instance - Read / VRead">
|
<subsection name="Instance - Read / VRead">
|
||||||
|
@ -239,7 +259,21 @@
|
||||||
<param name="file"
|
<param name="file"
|
||||||
value="examples/src/main/java/example/GenericClientExample.java" />
|
value="examples/src/main/java/example/GenericClientExample.java" />
|
||||||
</macro>
|
</macro>
|
||||||
</subsection>
|
<h4>Conditional Deletes</h4>
|
||||||
|
<p>
|
||||||
|
Conditional deletions are also possible, which is a form where
|
||||||
|
instead of deleting a resource using its logical ID, you specify
|
||||||
|
a set of search criteria and a single resource is deleted if
|
||||||
|
it matches that criteria. Note that this is not a mechanism
|
||||||
|
for bulk deletion; see the FHIR specification for information
|
||||||
|
on conditional deletes and how they are used.
|
||||||
|
</p>
|
||||||
|
<macro name="snippet">
|
||||||
|
<param name="id" value="deleteConditional" />
|
||||||
|
<param name="file"
|
||||||
|
value="examples/src/main/java/example/GenericClientExample.java" />
|
||||||
|
</macro>
|
||||||
|
</subsection>
|
||||||
|
|
||||||
<subsection name="Instance - Update">
|
<subsection name="Instance - Update">
|
||||||
<p>
|
<p>
|
||||||
|
@ -257,6 +291,21 @@
|
||||||
value="examples/src/main/java/example/GenericClientExample.java" />
|
value="examples/src/main/java/example/GenericClientExample.java" />
|
||||||
</macro>
|
</macro>
|
||||||
|
|
||||||
|
<h4>Conditional Updates</h4>
|
||||||
|
<p>
|
||||||
|
FHIR also specifies a type of update called "conditional updates", where
|
||||||
|
insetad of using the logical ID of a resource to update, a set of
|
||||||
|
search parameters is provided. If a single resource matches that set of
|
||||||
|
parameters, that resource is updated. See the FHIR specification for
|
||||||
|
information on how conditional updates work.
|
||||||
|
</p>
|
||||||
|
<macro name="snippet">
|
||||||
|
<param name="id" value="updateConditional" />
|
||||||
|
<param name="file"
|
||||||
|
value="examples/src/main/java/example/GenericClientExample.java" />
|
||||||
|
</macro>
|
||||||
|
|
||||||
|
<h4>ETags and Resource Contention</h4>
|
||||||
<p>
|
<p>
|
||||||
<b>See also</b> the page on
|
<b>See also</b> the page on
|
||||||
<a href="./doc_rest_etag.html#client_update">ETag Support</a>
|
<a href="./doc_rest_etag.html#client_update">ETag Support</a>
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
<body>
|
<body>
|
||||||
<section name="Implementing Resource Provider Operations">
|
<section name="Implementing Resource Provider Operations">
|
||||||
|
|
||||||
|
<!--
|
||||||
<p>
|
<p>
|
||||||
Jump To...
|
Jump To...
|
||||||
</p>
|
</p>
|
||||||
|
@ -44,15 +45,17 @@
|
||||||
implementations, but client methods will follow the same patterns.
|
implementations, but client methods will follow the same patterns.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<a name="operations" />
|
-->
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section name="Operations">
|
<section name="Operations">
|
||||||
|
<a name="operations" />
|
||||||
<p>
|
<p>
|
||||||
The following table lists the operations supported by
|
The following table lists the operations supported by
|
||||||
HAPI FHIR RESTful Servers and Clients.
|
HAPI FHIR RESTful Servers and Clients.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<!--
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr style="font-weight: bold; font-size: 1.2em;">
|
<tr style="font-weight: bold; font-size: 1.2em;">
|
||||||
|
@ -180,6 +183,7 @@
|
||||||
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
-->
|
||||||
|
|
||||||
<a name="instance_read" />
|
<a name="instance_read" />
|
||||||
</section>
|
</section>
|
||||||
|
@ -279,10 +283,10 @@
|
||||||
annotation. This parameter contains the resource instance to be created.
|
annotation. This parameter contains the resource instance to be created.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
In addition, the method must have a parameter annotated with the
|
In addition, the method may optionally have a parameter annotated with the
|
||||||
<a href="./apidocs/ca/uhn/fhir/rest/annotation/IdParam.html">@IdParam</a>
|
<a href="./apidocs/ca/uhn/fhir/rest/annotation/IdParam.html">@IdParam</a>
|
||||||
annotation, and optionally may have a parameter annotated with the
|
annotation, or they may obtain the ID of the resource being updated from
|
||||||
<a href="./apidocs/ca/uhn/fhir/rest/annotation/VersionIdParam.html">@VersionIdParam</a>
|
the resource itself. Either way, this ID comes from the URL passed in.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Update methods must return an object of type
|
Update methods must return an object of type
|
||||||
|
@ -315,6 +319,27 @@
|
||||||
<param name="id" value="updateClient" />
|
<param name="id" value="updateClient" />
|
||||||
<param name="file" value="examples/src/main/java/example/RestfulPatientResourceProviderMore.java" />
|
<param name="file" value="examples/src/main/java/example/RestfulPatientResourceProviderMore.java" />
|
||||||
</macro>
|
</macro>
|
||||||
|
|
||||||
|
<h4>Conditional Updates</h4>
|
||||||
|
<p>
|
||||||
|
If you wish to suport conditional updates, you can add a parameter
|
||||||
|
tagged with a
|
||||||
|
<a href="./apidocs/ca/uhn/fhir/rest/annotation/ConditionalOperationParam.html">@ConditionalOperationParam</a>
|
||||||
|
annotation. If the request URL contains search parameters instead of a
|
||||||
|
resource ID, then this parameter will be populated.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<macro name="snippet">
|
||||||
|
<param name="id" value="updateConditional" />
|
||||||
|
<param name="file" value="examples/src/main/java/example/RestfulPatientResourceProviderMore.java" />
|
||||||
|
</macro>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Example URL to invoke this method (this would be invoked using an HTTP PUT,
|
||||||
|
with the resource in the PUT body):
|
||||||
|
<br />
|
||||||
|
<code>http://fhir.example.com/Patient?identifier=system%7C00001</code>
|
||||||
|
</p>
|
||||||
|
|
||||||
<a name="instance_delete" />
|
<a name="instance_delete" />
|
||||||
</section>
|
</section>
|
||||||
|
@ -373,6 +398,26 @@
|
||||||
<code>http://fhir.example.com/Patient/111</code>
|
<code>http://fhir.example.com/Patient/111</code>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<h4>Conditional Deletes</h4>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The FHIR specification also allows "conditional deletes". A conditional
|
||||||
|
delete uses a search style URL instead of a read style URL, and
|
||||||
|
deletes a single resource if it matches the given search parameters.
|
||||||
|
The following example shows how to
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<macro name="snippet">
|
||||||
|
<param name="id" value="deleteConditional" />
|
||||||
|
<param name="file" value="examples/src/main/java/example/RestfulPatientResourceProviderMore.java" />
|
||||||
|
</macro>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Example URL to perform a conditional delete (HTTP DELETE):
|
||||||
|
<br />
|
||||||
|
<code>http://fhir.example.com/Patient?identifier=system%7C0001</code>
|
||||||
|
</p>
|
||||||
|
|
||||||
<a name="type_create" />
|
<a name="type_create" />
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue