Merge branch 'master' of github.com:jamesagnew/hapi-fhir

This commit is contained in:
James Agnew 2015-04-20 14:48:18 -04:00
commit dfbfed2d35
12 changed files with 211 additions and 197 deletions

View File

@ -74,30 +74,30 @@ public class GenericClientExample {
// END SNIPPET: create // END SNIPPET: create
} }
{ {
Patient patient = new Patient(); Patient patient = new Patient();
// START SNIPPET: createConditional // START SNIPPET: createConditional
// One form // One form
MethodOutcome outcome = client.create() MethodOutcome outcome = client.create()
.resource(patient) .resource(patient)
.conditionalByUrl("Patient?identifier=system%7C00001") .conditionalByUrl("Patient?identifier=system%7C00001")
.execute(); .execute();
// Another form // Another form
MethodOutcome outcome2 = client.create() MethodOutcome outcome2 = client.create()
.resource(patient) .resource(patient)
.conditional() .conditional()
.where(Patient.IDENTIFIER.exactly().systemAndIdentifier("system", "00001")) .where(Patient.IDENTIFIER.exactly().systemAndIdentifier("system", "00001"))
.execute(); .execute();
// This will return true if the server responded with an HTTP 201 created, // This will return true if the server responded with an HTTP 201 created,
// otherwise it will return null. // otherwise it will return null.
Boolean created = outcome.getCreated(); Boolean created = outcome.getCreated();
// The ID of the created, or the pre-existing resource // The ID of the created, or the pre-existing resource
IdDt id = outcome.getId(); IdDt id = outcome.getId();
// END SNIPPET: createConditional // END SNIPPET: createConditional
} }
{ {
// START SNIPPET: update // START SNIPPET: update
Patient patient = new Patient(); Patient patient = new Patient();
// ..populate the patient object.. // ..populate the patient object..
@ -125,21 +125,21 @@ public class GenericClientExample {
// END SNIPPET: update // END SNIPPET: update
} }
{ {
Patient patient = new Patient(); Patient patient = new Patient();
// START SNIPPET: updateConditional // START SNIPPET: updateConditional
client.update() client.update()
.resource(patient) .resource(patient)
.conditionalByUrl("Patient?identifier=system%7C00001") .conditionalByUrl("Patient?identifier=system%7C00001")
.execute(); .execute();
client.update() client.update()
.resource(patient) .resource(patient)
.conditional() .conditional()
.where(Patient.IDENTIFIER.exactly().systemAndIdentifier("system", "00001")) .where(Patient.IDENTIFIER.exactly().systemAndIdentifier("system", "00001"))
.execute(); .execute();
// END SNIPPET: updateConditional // 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
// from the server // from the server
@ -176,26 +176,26 @@ public class GenericClientExample {
} }
{ {
// START SNIPPET: delete // START SNIPPET: delete
BaseOperationOutcome resp = client.delete().resourceById(new IdDt("Patient", "1234")).execute(); BaseOperationOutcome resp = 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 (resp != null) { if (resp != null) {
OperationOutcome outcome = (OperationOutcome) resp; 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 // START SNIPPET: deleteConditional
client.delete() client.delete()
.resourceConditionalByUrl("Patient?identifier=system%7C00001") .resourceConditionalByUrl("Patient?identifier=system%7C00001")
.execute(); .execute();
client.delete() client.delete()
.resourceConditionalByType("Patient") .resourceConditionalByType("Patient")
.where(Patient.IDENTIFIER.exactly().systemAndIdentifier("system", "00001")) .where(Patient.IDENTIFIER.exactly().systemAndIdentifier("system", "00001"))
.execute(); .execute();
// END SNIPPET: deleteConditional // END SNIPPET: deleteConditional
} }
{ {
// START SNIPPET: search // START SNIPPET: search

View File

@ -49,13 +49,14 @@ import javax.json.stream.JsonGenerator;
import javax.json.stream.JsonGeneratorFactory; import javax.json.stream.JsonGeneratorFactory;
import javax.json.stream.JsonParsingException; import javax.json.stream.JsonParsingException;
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
import ca.uhn.fhir.model.primitive.*;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.IBase; import org.hl7.fhir.instance.model.IBase;
import org.hl7.fhir.instance.model.IBaseResource; import org.hl7.fhir.instance.model.IBaseResource;
import org.hl7.fhir.instance.model.IPrimitiveType; import org.hl7.fhir.instance.model.IPrimitiveType;
import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseBinary;
import org.hl7.fhir.instance.model.api.IBaseBooleanDatatype; import org.hl7.fhir.instance.model.api.IBaseBooleanDatatype;
import org.hl7.fhir.instance.model.api.IBaseDatatype; import org.hl7.fhir.instance.model.api.IBaseDatatype;
import org.hl7.fhir.instance.model.api.IBaseDecimalDatatype; import org.hl7.fhir.instance.model.api.IBaseDecimalDatatype;
@ -88,13 +89,12 @@ import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.Tag; import ca.uhn.fhir.model.api.Tag;
import ca.uhn.fhir.model.api.TagList; import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.api.annotation.Child; import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
import ca.uhn.fhir.model.base.composite.BaseContainedDt; import ca.uhn.fhir.model.base.composite.BaseContainedDt;
import ca.uhn.fhir.model.base.composite.BaseNarrativeDt; import ca.uhn.fhir.model.base.composite.BaseNarrativeDt;
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt; import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
import ca.uhn.fhir.model.base.resource.BaseBinary;
import ca.uhn.fhir.model.primitive.DecimalDt; import ca.uhn.fhir.model.primitive.DecimalDt;
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.IntegerDt; import ca.uhn.fhir.model.primitive.IntegerDt;
import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.XhtmlDt; import ca.uhn.fhir.model.primitive.XhtmlDt;
@ -102,10 +102,6 @@ import ca.uhn.fhir.narrative.INarrativeGenerator;
import ca.uhn.fhir.util.ElementUtil; import ca.uhn.fhir.util.ElementUtil;
import ca.uhn.fhir.util.UrlUtil; import ca.uhn.fhir.util.UrlUtil;
/**
* This class is the FHIR JSON parser/encoder. Users should not interact with this
* class directly, but should use {@link FhirContext#newJsonParser()} to get an instance.
*/
public class JsonParser extends BaseParser implements IParser { public class JsonParser extends BaseParser implements IParser {
private static final Set<String> BUNDLE_TEXTNODE_CHILDREN_DSTU1; private static final Set<String> BUNDLE_TEXTNODE_CHILDREN_DSTU1;
@ -302,9 +298,9 @@ public class JsonParser extends BaseParser implements IParser {
// IResource nextResource = nextEntry.getResource(); // IResource nextResource = nextEntry.getResource();
} }
if (nextEntry.getTransactionMethod().isEmpty() == false || nextEntry.getLinkSearch().isEmpty() == false) { if (nextEntry.getTransactionOperation().isEmpty() == false || nextEntry.getLinkSearch().isEmpty() == false) {
theEventWriter.writeStartObject("transaction"); theEventWriter.writeStartObject("transaction");
writeOptionalTagWithTextNode(theEventWriter, "method", nextEntry.getTransactionMethod().getValue()); writeOptionalTagWithTextNode(theEventWriter, "operation", nextEntry.getTransactionOperation().getValue());
writeOptionalTagWithTextNode(theEventWriter, "url", nextEntry.getLinkSearch().getValue()); writeOptionalTagWithTextNode(theEventWriter, "url", nextEntry.getLinkSearch().getValue());
theEventWriter.writeEnd(); theEventWriter.writeEnd();
} }
@ -456,7 +452,7 @@ public class JsonParser extends BaseParser implements IParser {
case RESOURCE: case RESOURCE:
IBaseResource resource = (IBaseResource) theNextValue; IBaseResource resource = (IBaseResource) theNextValue;
RuntimeResourceDefinition def = myContext.getResourceDefinition(resource); RuntimeResourceDefinition def = myContext.getResourceDefinition(resource);
encodeResourceToJsonStreamWriter(def, resource, theWriter, theChildName, false); encodeResourceToJsonStreamWriter(def, resource, theWriter, theChildName, true);
break; break;
case UNDECL_EXT: case UNDECL_EXT:
default: default:
@ -631,12 +627,12 @@ public class JsonParser extends BaseParser implements IParser {
} }
private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theEventWriter, String theObjectNameOrNull, private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theEventWriter, String theObjectNameOrNull,
boolean theContainedResource) throws IOException { boolean theIsSubElementWithinResource) throws IOException {
String resourceId = null; String resourceId = null;
if (theResource instanceof IResource) { if (theResource instanceof IResource) {
IResource res = (IResource) theResource; IResource res = (IResource) theResource;
if (StringUtils.isNotBlank(res.getId().getIdPart())) { if (StringUtils.isNotBlank(res.getId().getIdPart())) {
if (theContainedResource) { if (theIsSubElementWithinResource) {
resourceId = res.getId().getIdPart(); resourceId = res.getId().getIdPart();
} else if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) { } else if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
resourceId = res.getId().getIdPart(); resourceId = res.getId().getIdPart();
@ -644,17 +640,17 @@ public class JsonParser extends BaseParser implements IParser {
} }
} else if (theResource instanceof IAnyResource) { } else if (theResource instanceof IAnyResource) {
IAnyResource res = (IAnyResource) theResource; IAnyResource res = (IAnyResource) theResource;
if (theContainedResource && StringUtils.isNotBlank(res.getId())) { if (theIsSubElementWithinResource && StringUtils.isNotBlank(res.getId())) {
resourceId = res.getId(); resourceId = res.getId();
} }
} }
encodeResourceToJsonStreamWriter(theResDef, theResource, theEventWriter, theObjectNameOrNull, theContainedResource, resourceId); encodeResourceToJsonStreamWriter(theResDef, theResource, theEventWriter, theObjectNameOrNull, theIsSubElementWithinResource, resourceId);
} }
private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theEventWriter, String theObjectNameOrNull, private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theEventWriter, String theObjectNameOrNull,
boolean theContainedResource, String theResourceId) throws IOException { boolean theIsSubElementWithinResource, String theResourceId) throws IOException {
if (!theContainedResource) { if (!theIsSubElementWithinResource) {
super.containResourcesForEncoding(theResource); super.containResourcesForEncoding(theResource);
} }
@ -673,64 +669,55 @@ public class JsonParser extends BaseParser implements IParser {
if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1) && theResource instanceof IResource) { if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1) && theResource instanceof IResource) {
IResource resource = (IResource) theResource; IResource resource = (IResource) theResource;
//Object securityLabelRawObj =
InstantDt updated = (InstantDt) resource.getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED); List<BaseCodingDt> securityLabels = (List<BaseCodingDt>) resource.getResourceMetadata().get(ResourceMetadataKeyEnum.SECURITY_LABELS);
IdDt resourceId = resource.getId(); if (!ElementUtil.isEmpty(resource.getId().getVersionIdPart(), ResourceMetadataKeyEnum.UPDATED.get(resource))
String versionIdPart = resourceId.getVersionIdPart(); || (securityLabels != null && !securityLabels.isEmpty())) {
if (isBlank(versionIdPart)) {
versionIdPart = ResourceMetadataKeyEnum.VERSION.get(resource);
}
List<BaseCodingDt> securityLabels = extractMetadataListNotNull(resource, ResourceMetadataKeyEnum.SECURITY_LABELS);
List<IdDt> profiles = extractMetadataListNotNull(resource, ResourceMetadataKeyEnum.PROFILES);
TagList tags = ResourceMetadataKeyEnum.TAG_LIST.get(resource);
if (ElementUtil.isEmpty(versionIdPart, updated, securityLabels, profiles) == false) {
theEventWriter.writeStartObject("meta"); theEventWriter.writeStartObject("meta");
writeOptionalTagWithTextNode(theEventWriter, "versionId", resource.getId().getVersionIdPart()); writeOptionalTagWithTextNode(theEventWriter, "versionId", resource.getId().getVersionIdPart());
writeOptionalTagWithTextNode(theEventWriter, "lastUpdated", ResourceMetadataKeyEnum.UPDATED.get(resource)); writeOptionalTagWithTextNode(theEventWriter, "lastUpdated", ResourceMetadataKeyEnum.UPDATED.get(resource));
if (profiles != null && profiles.isEmpty()==false) {
theEventWriter.writeStartArray("profile");
for (IdDt profile : profiles) {
if (profile != null && isNotBlank(profile.getValue())) {
theEventWriter.write(profile.getValue());
}
}
theEventWriter.writeEnd();
}
if (securityLabels.isEmpty()==false) { if (securityLabels != null) {
theEventWriter.writeStartArray("security"); if (!securityLabels.isEmpty()) {
for (BaseCodingDt securityLabel : securityLabels) { theEventWriter.writeStartArray("security");
theEventWriter.writeStartObject();
BaseRuntimeElementCompositeDefinition<?> def = (BaseRuntimeElementCompositeDefinition<?>) myContext.getElementDefinition(securityLabel.getClass()); for (BaseCodingDt securityLabel : securityLabels) {
encodeCompositeElementChildrenToStreamWriter(resDef, resource, securityLabel, theEventWriter, def.getChildren(), theContainedResource); theEventWriter.writeStartObject();
theEventWriter.writeEnd();
UriDt system = securityLabel.getSystemElement();
if (system != null && !system.isEmpty())
writeOptionalTagWithTextNode(theEventWriter, "system", system.getValueAsString());
CodeDt code = securityLabel.getCodeElement();
if (code != null && !code.isEmpty())
writeOptionalTagWithTextNode(theEventWriter, "code", code.getValueAsString());
StringDt display = securityLabel.getDisplayElement();
if (display != null && !display.isEmpty())
writeOptionalTagWithTextNode(theEventWriter, "display", display.getValueAsString());
/*todo: handle version
StringDt version = securityLabel.getVersion();
if (version != null && ! version.isEmpty())
writeOptionalTagWithTextNode(theEventWriter, "version", version.getValueAsString());
*/
theEventWriter.writeEnd(); //end the individual security label
}
theEventWriter.writeEnd(); //end security labels array
} }
theEventWriter.writeEnd();
}
if (tags != null && tags.isEmpty()==false) {
theEventWriter.writeStartArray("tag");
for (Tag tag : tags) {
theEventWriter.writeStartObject();
writeOptionalTagWithTextNode(theEventWriter, "system", tag.getScheme());
writeOptionalTagWithTextNode(theEventWriter, "code", tag.getTerm());
writeOptionalTagWithTextNode(theEventWriter, "display", tag.getLabel());
theEventWriter.writeEnd();
}
theEventWriter.writeEnd();
} }
theEventWriter.writeEnd(); //end meta theEventWriter.writeEnd(); //end meta
} }
} }
if (theResource instanceof IBaseBinary) { if (theResource instanceof BaseBinary) {
IBaseBinary bin = (IBaseBinary) theResource; BaseBinary bin = (BaseBinary) theResource;
theEventWriter.write("contentType", bin.getContentType()); theEventWriter.write("contentType", bin.getContentType());
theEventWriter.write("content", bin.getContentAsBase64()); theEventWriter.write("content", bin.getContentAsBase64());
} else { } else {
encodeCompositeElementToStreamWriter(theResDef, theResource, theResource, theEventWriter, resDef, theContainedResource); encodeCompositeElementToStreamWriter(theResDef, theResource, theResource, theEventWriter, resDef, theIsSubElementWithinResource);
} }
theEventWriter.writeEnd(); theEventWriter.writeEnd();

View File

@ -1674,7 +1674,7 @@ class ParserState<T> {
private class SecurityLabelElementStateHapi extends ElementCompositeState { private class SecurityLabelElementStateHapi extends ElementCompositeState {
public SecurityLabelElementStateHapi(ParserState<T>.PreResourceState thePreResourceState,BaseRuntimeElementCompositeDefinition<?> theDef, BaseCodingDt codingDt) { public SecurityLabelElementStateHapi(ParserState<T>.PreResourceState thePreResourceState, BaseRuntimeElementCompositeDefinition<?> theDef, BaseCodingDt codingDt) {
super(thePreResourceState, theDef, codingDt); super(thePreResourceState, theDef, codingDt);
} }
@ -1716,7 +1716,7 @@ class ParserState<T> {
securityLabels = new ArrayList<BaseCodingDt>(); securityLabels = new ArrayList<BaseCodingDt>();
myMap.put(ResourceMetadataKeyEnum.SECURITY_LABELS, securityLabels); myMap.put(ResourceMetadataKeyEnum.SECURITY_LABELS, securityLabels);
} }
BaseCodingDt securityLabel= myContext.getVersion().newCodingDt(); BaseCodingDt securityLabel = myContext.getVersion().newCodingDt();
BaseRuntimeElementCompositeDefinition<?> codinfDef = (BaseRuntimeElementCompositeDefinition<?>) myContext.getElementDefinition(securityLabel.getClass()); BaseRuntimeElementCompositeDefinition<?> codinfDef = (BaseRuntimeElementCompositeDefinition<?>) myContext.getElementDefinition(securityLabel.getClass());
push(new SecurityLabelElementStateHapi(getPreResourceState(), codinfDef, securityLabel)); push(new SecurityLabelElementStateHapi(getPreResourceState(), codinfDef, securityLabel));
securityLabels.add(securityLabel); securityLabels.add(securityLabel);

View File

@ -20,17 +20,17 @@ package ca.uhn.fhir.parser;
* #L% * #L%
*/ */
import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.*;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.IOException; import java.io.IOException;
import java.io.Reader; import java.io.Reader;
import java.io.StringWriter; import java.io.StringWriter;
import java.io.Writer; import java.io.Writer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.ListIterator;
import javax.xml.namespace.QName; import javax.xml.namespace.QName;
import javax.xml.stream.FactoryConfigurationError; import javax.xml.stream.FactoryConfigurationError;
@ -46,12 +46,13 @@ import javax.xml.stream.events.Namespace;
import javax.xml.stream.events.StartElement; import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent; import javax.xml.stream.events.XMLEvent;
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
import ca.uhn.fhir.model.primitive.*;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.IBase; import org.hl7.fhir.instance.model.IBase;
import org.hl7.fhir.instance.model.IBaseResource; import org.hl7.fhir.instance.model.IBaseResource;
import org.hl7.fhir.instance.model.IPrimitiveType; import org.hl7.fhir.instance.model.IPrimitiveType;
import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseBinary;
import org.hl7.fhir.instance.model.api.IBaseDatatype; import org.hl7.fhir.instance.model.api.IBaseDatatype;
import org.hl7.fhir.instance.model.api.IBaseExtension; import org.hl7.fhir.instance.model.api.IBaseExtension;
import org.hl7.fhir.instance.model.api.IBaseHasExtensions; import org.hl7.fhir.instance.model.api.IBaseHasExtensions;
@ -72,32 +73,25 @@ import ca.uhn.fhir.context.RuntimeChildUndeclaredExtensionDefinition;
import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.Bundle; import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.BundleEntry; import ca.uhn.fhir.model.api.BundleEntry;
import ca.uhn.fhir.model.api.ExtensionDt;
import ca.uhn.fhir.model.api.IElement;
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions; import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.Tag; import ca.uhn.fhir.model.api.Tag;
import ca.uhn.fhir.model.api.TagList; import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
import ca.uhn.fhir.model.base.composite.BaseContainedDt; import ca.uhn.fhir.model.base.composite.BaseContainedDt;
import ca.uhn.fhir.model.base.composite.BaseNarrativeDt; import ca.uhn.fhir.model.base.composite.BaseNarrativeDt;
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt; import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.base.resource.BaseBinary;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.XhtmlDt;
import ca.uhn.fhir.narrative.INarrativeGenerator; import ca.uhn.fhir.narrative.INarrativeGenerator;
import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.method.BaseMethodBinding;
import ca.uhn.fhir.util.ElementUtil;
import ca.uhn.fhir.util.NonPrettyPrintWriterWrapper; import ca.uhn.fhir.util.NonPrettyPrintWriterWrapper;
import ca.uhn.fhir.util.PrettyPrintWriterWrapper; import ca.uhn.fhir.util.PrettyPrintWriterWrapper;
import ca.uhn.fhir.util.XmlUtil; import ca.uhn.fhir.util.XmlUtil;
/**
* This class is the FHIR XML parser/encoder. Users should not interact with this
* class directly, but should use {@link FhirContext#newXmlParser()} to get an instance.
*/
public class XmlParser extends BaseParser implements IParser { public class XmlParser extends BaseParser implements IParser {
static final String ATOM_NS = "http://www.w3.org/2005/Atom"; static final String ATOM_NS = "http://www.w3.org/2005/Atom";
static final String FHIR_NS = "http://hl7.org/fhir"; static final String FHIR_NS = "http://hl7.org/fhir";
static final String OPENSEARCH_NS = "http://a9.com/-/spec/opensearch/1.1/"; static final String OPENSEARCH_NS = "http://a9.com/-/spec/opensearch/1.1/";
@ -251,12 +245,12 @@ public class XmlParser extends BaseParser implements IParser {
writeTagWithTextNode(eventWriter, "title", theBundle.getTitle()); writeTagWithTextNode(eventWriter, "title", theBundle.getTitle());
writeTagWithTextNode(eventWriter, "id", theBundle.getBundleId()); writeTagWithTextNode(eventWriter, "id", theBundle.getBundleId());
writeAtomLink(eventWriter, Constants.LINK_SELF, theBundle.getLinkSelf()); writeAtomLink(eventWriter, "self", theBundle.getLinkSelf());
writeAtomLink(eventWriter, Constants.LINK_FIRST, theBundle.getLinkFirst()); writeAtomLink(eventWriter, "first", theBundle.getLinkFirst());
writeAtomLink(eventWriter, Constants.LINK_PREVIOUS, theBundle.getLinkPrevious()); writeAtomLink(eventWriter, "previous", theBundle.getLinkPrevious());
writeAtomLink(eventWriter, Constants.LINK_NEXT, theBundle.getLinkNext()); writeAtomLink(eventWriter, "next", theBundle.getLinkNext());
writeAtomLink(eventWriter, Constants.LINK_LAST, theBundle.getLinkLast()); writeAtomLink(eventWriter, "last", theBundle.getLinkLast());
writeAtomLink(eventWriter, Constants.LINK_FHIR_BASE, theBundle.getLinkBase()); writeAtomLink(eventWriter, "fhir-base", theBundle.getLinkBase());
if (theBundle.getTotalResults().getValue() != null) { if (theBundle.getTotalResults().getValue() != null) {
eventWriter.writeStartElement("os", "totalResults", OPENSEARCH_NS); eventWriter.writeStartElement("os", "totalResults", OPENSEARCH_NS);
@ -418,9 +412,9 @@ public class XmlParser extends BaseParser implements IParser {
// IResource nextResource = nextEntry.getResource(); // IResource nextResource = nextEntry.getResource();
} }
if (nextEntry.getTransactionMethod().isEmpty() == false || nextEntry.getLinkSearch().isEmpty() == false) { if (nextEntry.getTransactionOperation().isEmpty() == false || nextEntry.getLinkSearch().isEmpty() == false) {
theEventWriter.writeStartElement("transaction"); theEventWriter.writeStartElement("transaction");
writeOptionalTagWithValue(theEventWriter, "method", nextEntry.getTransactionMethod().getValue()); writeOptionalTagWithValue(theEventWriter, "operation", nextEntry.getTransactionOperation().getValue());
writeOptionalTagWithValue(theEventWriter, "url", nextEntry.getLinkSearch().getValue()); writeOptionalTagWithValue(theEventWriter, "url", nextEntry.getLinkSearch().getValue());
theEventWriter.writeEndElement(); theEventWriter.writeEndElement();
} }
@ -442,7 +436,7 @@ public class XmlParser extends BaseParser implements IParser {
} }
private void encodeChildElementToStreamWriter(IBaseResource theResource, XMLStreamWriter theEventWriter, IBase nextValue, String childName, BaseRuntimeElementDefinition<?> childDef, private void encodeChildElementToStreamWriter(IBaseResource theResource, XMLStreamWriter theEventWriter, IBase nextValue, String childName, BaseRuntimeElementDefinition<?> childDef,
String theExtensionUrl, boolean theIncludedResource) throws XMLStreamException, DataFormatException { String theExtensionUrl, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
if (nextValue.isEmpty()) { if (nextValue.isEmpty()) {
if (childDef.getChildType() == ChildTypeEnum.CONTAINED_RESOURCES && getContainedResources().isEmpty() == false && theIncludedResource == false) { if (childDef.getChildType() == ChildTypeEnum.CONTAINED_RESOURCES && getContainedResources().isEmpty() == false && theIncludedResource == false) {
// We still want to go in.. // We still want to go in..
@ -521,12 +515,12 @@ public class XmlParser extends BaseParser implements IParser {
} }
private void encodeCompositeElementChildrenToStreamWriter(IBaseResource theResource, IBase theElement, XMLStreamWriter theEventWriter, List<? extends BaseRuntimeChildDefinition> children, private void encodeCompositeElementChildrenToStreamWriter(IBaseResource theResource, IBase theElement, XMLStreamWriter theEventWriter, List<? extends BaseRuntimeChildDefinition> children,
boolean theIncludedResource) throws XMLStreamException, DataFormatException { boolean theIncludedResource) throws XMLStreamException, DataFormatException {
for (BaseRuntimeChildDefinition nextChild : children) { for (BaseRuntimeChildDefinition nextChild : children) {
if (nextChild.getElementName().equals("extension") || nextChild.getElementName().equals("modifierExtension")) { if (nextChild.getElementName().equals("extension") || nextChild.getElementName().equals("modifierExtension")) {
continue; continue;
} }
if (nextChild instanceof RuntimeChildNarrativeDefinition && !theIncludedResource) { if (nextChild instanceof RuntimeChildNarrativeDefinition && !theIncludedResource) {
INarrativeGenerator gen = myContext.getNarrativeGenerator(); INarrativeGenerator gen = myContext.getNarrativeGenerator();
if (theResource instanceof IResource) { if (theResource instanceof IResource) {
@ -581,7 +575,7 @@ public class XmlParser extends BaseParser implements IParser {
// This is called for the Query resource in DSTU1 only // This is called for the Query resource in DSTU1 only
extensionUrl = ((IBaseExtension<?>) nextValue).getUrl(); extensionUrl = ((IBaseExtension<?>) nextValue).getUrl();
encodeChildElementToStreamWriter(theResource, theEventWriter, nextValue, childName, childDef, extensionUrl, theIncludedResource); encodeChildElementToStreamWriter(theResource, theEventWriter, nextValue, childName, childDef, extensionUrl, theIncludedResource);
} else if (extensionUrl != null && childName.equals("extension") == false) { } else if (extensionUrl != null && childName.equals("extension") == false) {
RuntimeChildDeclaredExtensionDefinition extDef = (RuntimeChildDeclaredExtensionDefinition) nextChild; RuntimeChildDeclaredExtensionDefinition extDef = (RuntimeChildDeclaredExtensionDefinition) nextChild;
if (extDef.isModifier()) { if (extDef.isModifier()) {
@ -603,7 +597,7 @@ public class XmlParser extends BaseParser implements IParser {
} }
private void encodeCompositeElementToStreamWriter(IBaseResource theResource, IBase theElement, XMLStreamWriter theEventWriter, BaseRuntimeElementCompositeDefinition<?> theElementDefinition, private void encodeCompositeElementToStreamWriter(IBaseResource theResource, IBase theElement, XMLStreamWriter theEventWriter, BaseRuntimeElementCompositeDefinition<?> theElementDefinition,
boolean theIncludedResource) throws XMLStreamException, DataFormatException { boolean theIncludedResource) throws XMLStreamException, DataFormatException {
encodeExtensionsIfPresent(theResource, theEventWriter, theElement, theIncludedResource); encodeExtensionsIfPresent(theResource, theEventWriter, theElement, theIncludedResource);
encodeCompositeElementChildrenToStreamWriter(theResource, theElement, theEventWriter, theElementDefinition.getExtensions(), theIncludedResource); encodeCompositeElementChildrenToStreamWriter(theResource, theElement, theEventWriter, theElementDefinition.getExtensions(), theIncludedResource);
encodeCompositeElementChildrenToStreamWriter(theResource, theElement, theEventWriter, theElementDefinition.getChildren(), theIncludedResource); encodeCompositeElementChildrenToStreamWriter(theResource, theElement, theEventWriter, theElementDefinition.getChildren(), theIncludedResource);
@ -651,7 +645,7 @@ public class XmlParser extends BaseParser implements IParser {
} }
private void encodeResourceToStreamWriterInDstu2Format(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theElement, XMLStreamWriter theEventWriter, private void encodeResourceToStreamWriterInDstu2Format(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theElement, XMLStreamWriter theEventWriter,
BaseRuntimeElementCompositeDefinition<?> resDef, boolean theIncludedResource) throws XMLStreamException, DataFormatException { BaseRuntimeElementCompositeDefinition<?> resDef, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
/* /*
* DSTU2 requires extensions to come in a specific spot within the encoded content - This is a bit of a messy way to make that happen, but hopefully this won't matter as much once we use the * DSTU2 requires extensions to come in a specific spot within the encoded content - This is a bit of a messy way to make that happen, but hopefully this won't matter as much once we use the
* HL7 structures * HL7 structures
@ -739,55 +733,69 @@ public class XmlParser extends BaseParser implements IParser {
// HL7.org Structures // HL7.org Structures
encodeCompositeElementToStreamWriter(theResource, theResource, theEventWriter, resDef, theContainedResource); encodeCompositeElementToStreamWriter(theResource, theResource, theEventWriter, resDef, theContainedResource);
} else { } else {
if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) { if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
// DSTU2+ // DSTU2+
IResource resource = (IResource) theResource; IResource resource = (IResource) theResource;
writeOptionalTagWithValue(theEventWriter, "id", theResourceId); writeOptionalTagWithValue(theEventWriter, "id", theResourceId);
InstantDt updated = (InstantDt) resource.getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED); InstantDt updated = (InstantDt) resource.getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED);
//Object securityLabelRawObj = resource.getResourceMetadata().get(ResourceMetadataKeyEnum.SECURITY_LABELS);
List<BaseCodingDt> securityLabels = (List<BaseCodingDt>) resource.getResourceMetadata().get(ResourceMetadataKeyEnum.SECURITY_LABELS);
IdDt resourceId = resource.getId(); IdDt resourceId = resource.getId();
String versionIdPart = resourceId.getVersionIdPart(); if (resourceId != null && isNotBlank(resourceId.getVersionIdPart())
if (isBlank(versionIdPart)) { || (updated != null && !updated.isEmpty())
versionIdPart = ResourceMetadataKeyEnum.VERSION.get(resource); || (securityLabels != null && !securityLabels.isEmpty())) {
}
List<BaseCodingDt> securityLabels = extractMetadataListNotNull(resource, ResourceMetadataKeyEnum.SECURITY_LABELS);
List<IdDt> profiles = extractMetadataListNotNull(resource, ResourceMetadataKeyEnum.PROFILES);
TagList tags = ResourceMetadataKeyEnum.TAG_LIST.get(resource);
if (ElementUtil.isEmpty(versionIdPart, updated, securityLabels, profiles) == false) {
theEventWriter.writeStartElement("meta"); theEventWriter.writeStartElement("meta");
String versionIdPart = resourceId.getVersionIdPart();
if (isBlank(versionIdPart)) {
versionIdPart = ResourceMetadataKeyEnum.VERSION.get(resource);
}
writeOptionalTagWithValue(theEventWriter, "versionId", versionIdPart); writeOptionalTagWithValue(theEventWriter, "versionId", versionIdPart);
if (updated != null) { if (updated != null) {
writeOptionalTagWithValue(theEventWriter, "lastUpdated", updated.getValueAsString()); writeOptionalTagWithValue(theEventWriter, "lastUpdated", updated.getValueAsString());
} }
for (IdDt profile : profiles) {
theEventWriter.writeStartElement("profile"); if (securityLabels != null) {
theEventWriter.writeAttribute("value", profile.getValue());
theEventWriter.writeEndElement(); if (!securityLabels.isEmpty()) {
}
for (BaseCodingDt securityLabel : securityLabels) { for (BaseCodingDt securityLabel : securityLabels) {
theEventWriter.writeStartElement("security"); theEventWriter.writeStartElement("security");
BaseRuntimeElementCompositeDefinition<?> def = (BaseRuntimeElementCompositeDefinition<?>) myContext.getElementDefinition(securityLabel.getClass());
encodeCompositeElementChildrenToStreamWriter(resource, securityLabel, theEventWriter, def.getChildren(), theContainedResource); UriDt system = securityLabel.getSystemElement();
theEventWriter.writeEndElement(); if (system != null && !system.isEmpty())
} writeOptionalTagWithValue(theEventWriter, "system", system.getValueAsString());
if (tags != null) {
for (Tag tag : tags) { CodeDt code = securityLabel.getCodeElement();
theEventWriter.writeStartElement("tag"); if (code != null && !code.isEmpty())
writeOptionalTagWithValue(theEventWriter, "system", tag.getScheme()); writeOptionalTagWithValue(theEventWriter, "code", code.getValueAsString());
writeOptionalTagWithValue(theEventWriter, "code", tag.getTerm());
writeOptionalTagWithValue(theEventWriter, "display", tag.getLabel()); StringDt display = securityLabel.getDisplayElement();
theEventWriter.writeEndElement(); if (display != null && !display.isEmpty())
writeOptionalTagWithValue(theEventWriter, "display", display.getValueAsString());
/*todo: handle version
StringDt version = securityLabel.getVersion();
if (version != null && ! version.isEmpty())
writeOptionalTagWithValue(theEventWriter, "version", version.getValueAsString());
*/
theEventWriter.writeEndElement();
}
} }
} }
theEventWriter.writeEndElement(); theEventWriter.writeEndElement();
} }
if (theResource instanceof IBaseBinary) { if (theResource instanceof BaseBinary) {
IBaseBinary bin = (IBaseBinary) theResource; BaseBinary bin = (BaseBinary) theResource;
writeOptionalTagWithValue(theEventWriter, "contentType", bin.getContentType()); writeOptionalTagWithValue(theEventWriter, "contentType", bin.getContentType());
writeOptionalTagWithValue(theEventWriter, "content", bin.getContentAsBase64()); writeOptionalTagWithValue(theEventWriter, "content", bin.getContentAsBase64());
} else { } else {
@ -801,8 +809,8 @@ public class XmlParser extends BaseParser implements IParser {
theEventWriter.writeAttribute("id", theResourceId); theEventWriter.writeAttribute("id", theResourceId);
} }
if (theResource instanceof IBaseBinary) { if (theResource instanceof BaseBinary) {
IBaseBinary bin = (IBaseBinary) theResource; BaseBinary bin = (BaseBinary) theResource;
if (bin.getContentType() != null) { if (bin.getContentType() != null) {
theEventWriter.writeAttribute("contentType", bin.getContentType()); theEventWriter.writeAttribute("contentType", bin.getContentType());
} }

View File

@ -135,7 +135,7 @@ public class MethodOutcome {
} }
/** /**
* This will be set to {@link Boolean#TRUE} for instance of MethodOutcome which are * 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. * returned to client instances, if the server has responded with an HTTP 201 Created.
*/ */
public Boolean getCreated() { public Boolean getCreated() {

View File

@ -280,9 +280,9 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
} else { } else {
retVal = parser.parseResource(requestReader); retVal = parser.parseResource(requestReader);
} }
retVal.setId(theRequest.getId()); retVal.setId(theRequest.getId());
return retVal; return retVal;
} }

View File

@ -39,9 +39,11 @@
</bean> </bean>
</property> </property>
</bean> </bean>
<bean id="myTxManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <bean id="myTxManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="myEntityManagerFactory" /> <property name="entityManagerFactory" ref="myEntityManagerFactory" />
</bean> </bean>
<tx:annotation-driven transaction-manager="myTxManager" /> <tx:annotation-driven transaction-manager="myTxManager" />
</beans> </beans>

View File

@ -9,6 +9,7 @@ import javax.servlet.http.HttpServletRequest;
import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2; import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.web.context.ContextLoaderListener; import org.springframework.web.context.ContextLoaderListener;
@ -167,10 +168,15 @@ public class TestRestfulServer extends RestfulServer {
* Do some fancy logging to create a nice access log that has details * Do some fancy logging to create a nice access log that has details
* about each incoming request. * about each incoming request.
*/ */
LoggingInterceptor loggingInterceptor = new LoggingInterceptor(); List<IServerInterceptor> interceptorBeans = myAppCtx.getBean("myServerInterceptors", List.class);
for (IServerInterceptor interceptor : interceptorBeans)
this.registerInterceptor(interceptor);
/*LoggingInterceptor loggingInterceptor = new LoggingInterceptor();
loggingInterceptor.setLoggerName("fhirtest.access"); loggingInterceptor.setLoggerName("fhirtest.access");
loggingInterceptor.setMessageFormat("Path[${servletPath}] Source[${requestHeader.x-forwarded-for}] Operation[${operationType} ${idOrResourceName}] UA[${requestHeader.user-agent}] Params[${requestParameters}]"); loggingInterceptor.setMessageFormat("Path[${servletPath}] Source[${requestHeader.x-forwarded-for}] Operation[${operationType} ${idOrResourceName}] UA[${requestHeader.user-agent}] Params[${requestParameters}]");
this.registerInterceptor(loggingInterceptor); this.registerInterceptor(loggingInterceptor);
*/
} }

View File

@ -21,4 +21,13 @@
</bean> </bean>
<tx:annotation-driven transaction-manager="myTxManager" /> <tx:annotation-driven transaction-manager="myTxManager" />
<util:list id="myServerInterceptors">
<ref bean="myLoggingInterceptor"/>
</util:list>
<bean id="myLoggingInterceptor" class="ca.uhn.fhir.rest.server.interceptor.LoggingInterceptor">
<property name="loggerName" value="fhirtest.access"/>
<property name="messageFormat"
value="Path[${servletPath}] Source[${requestHeader.x-forwarded-for}] Operation[${operationType} ${idOrResourceName}] UA[${requestHeader.user-agent}] Params[${requestParameters}]"/>
</bean>
</beans> </beans>

View File

@ -131,7 +131,7 @@ public class GenericClientTest {
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class); ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse); when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getAllHeaders()).thenReturn(new Header[] { new BasicHeader(Constants.HEADER_LOCATION, "/Patient/44/_history/22") }); 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().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"))); when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8")));
@ -140,14 +140,14 @@ public class GenericClientTest {
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 201, "OK")); when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 201, "OK"));
MethodOutcome resp = client.create().resource(ourCtx.newXmlParser().encodeResourceToString(p1)).execute(); MethodOutcome resp = client.create().resource(ourCtx.newXmlParser().encodeResourceToString(p1)).execute();
assertTrue(resp.getCreated()); assertTrue(resp.getCreated());
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK")); when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
resp = client.create().resource(ourCtx.newXmlParser().encodeResourceToString(p1)).execute(); resp = client.create().resource(ourCtx.newXmlParser().encodeResourceToString(p1)).execute();
assertNull(resp.getCreated()); assertNull(resp.getCreated());
} }
@Test @Test
public void testCreateWithStringAutoDetectsEncoding() throws Exception { public void testCreateWithStringAutoDetectsEncoding() throws Exception {

View File

@ -105,7 +105,7 @@ public class FhirDstu2 implements IFhirVersion {
public Class<? extends BaseContainedDt> getContainedType() { public Class<? extends BaseContainedDt> getContainedType() {
return ContainedDt.class; return ContainedDt.class;
} }
@Override @Override
public BaseCodingDt newCodingDt() { public BaseCodingDt newCodingDt() {
return new CodingDt(); return new CodingDt();

View File

@ -165,8 +165,10 @@
<h4>Search - Using HTTP POST</h4> <h4>Search - Using HTTP POST</h4>
<p> <p>
The FHIR specification allows the use of an HTTP POST to transmit a search to a server instead of using The FHIR specification allows the use of an HTTP POST to transmit a search to a server instead of
an HTTP GET. With this style of search, the search parameters are included in the request body instead 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 the request URL, which can be useful if you need to transmit a search with a large number
of parameters. of parameters.
</p> </p>
@ -176,7 +178,7 @@
case the client automatically switches to POST. case the client automatically switches to POST.
</p> </p>
<p> <p>
An alternate form of the search URL (using a URL ending with <code>_search</code>) was also 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 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. for using this form in order to interoperate with servers which use it.
</p> </p>
@ -211,18 +213,18 @@
<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> <h4>Conditional Creates</h4>
<p> <p>
FHIR also specifies a type of update called "conditional create", where FHIR also specifies a type of update called "conditional create", where
a set of search parameters are provided and a new resource is only a set of search parameters are provided and a new resource is only
created if no existing resource matches those parameters. See the created if no existing resource matches those parameters. See the
FHIR specification for more information on conditional creation. FHIR specification for more information on conditional creation.
</p> </p>
<macro name="snippet"> <macro name="snippet">
<param name="id" value="updateConditional" /> <param name="id" value="updateConditional"/>
<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> </subsection>
@ -276,7 +278,7 @@
</macro> </macro>
<h4>Conditional Deletes</h4> <h4>Conditional Deletes</h4>
<p> <p>
Conditional deletions are also possible, which is a form where Conditional deletions are also possible, which is a form where
instead of deleting a resource using its logical ID, you specify instead of deleting a resource using its logical ID, you specify
a set of search criteria and a single resource is deleted if a set of search criteria and a single resource is deleted if
it matches that criteria. Note that this is not a mechanism it matches that criteria. Note that this is not a mechanism
@ -284,9 +286,9 @@
on conditional deletes and how they are used. on conditional deletes and how they are used.
</p> </p>
<macro name="snippet"> <macro name="snippet">
<param name="id" value="deleteConditional" /> <param name="id" value="deleteConditional"/>
<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> </subsection>
@ -305,19 +307,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 Updates</h4> <h4>Conditional Updates</h4>
<p> <p>
FHIR also specifies a type of update called "conditional updates", where 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 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 search parameters is provided. If a single resource matches that set of
parameters, that resource is updated. See the FHIR specification for parameters, that resource is updated. See the FHIR specification for
information on how conditional updates work. information on how conditional updates work.
</p> </p>
<macro name="snippet"> <macro name="snippet">
<param name="id" value="updateConditional" /> <param name="id" value="updateConditional"/>
<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>ETags and Resource Contention</h4> <h4>ETags and Resource Contention</h4>