Add transaction operation support, and refactor IdDt into smarter datatype with URL awareness

This commit is contained in:
jamesagnew 2014-05-22 18:38:29 -04:00
parent 30065fdfae
commit 78d712de25
26 changed files with 669 additions and 440 deletions

View File

@ -21,6 +21,9 @@
<action type="add">
Parsers (XML/JSON) now support deleted entries in bundles
</action>
<action type="add">
Transaction method now supported in servers
</action>
</release>
</body>
</document>

View File

@ -23,7 +23,6 @@ package ca.uhn.fhir.model.api;
import java.io.IOException;
import java.io.Reader;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.CloseableHttpResponse;
@ -31,7 +30,6 @@ import org.apache.http.client.methods.HttpGet;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.client.BaseClient;
import ca.uhn.fhir.rest.client.api.IRestfulClient;
@ -40,34 +38,23 @@ public abstract class BaseResourceReference extends BaseElement {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseResourceReference.class);
private IResource myResource;
private String myResourceId;
private Class<? extends IResource> myResourceType;
/**
* Constructor
*/
* @param theResource
*/
public BaseResourceReference() {
// nothing
}
/**
* Constructor
*/
public BaseResourceReference(Class<? extends IResource> theResourceType, IdDt theResourceId) {
myResourceType = theResourceType;
myResourceId = theResourceId.getValue();
}
/**
* Constructor
*
* @param theResource The loaded resource itself
*/
public BaseResourceReference(Class<? extends IResource> theResourceType, String theResourceId) {
myResourceType = theResourceType;
myResourceId = theResourceId;
}
public BaseResourceReference(IResource theResource) {
myResource=theResource;
myResource = theResource;
setResourceId(theResource.getId());
}
/**
@ -79,7 +66,18 @@ public abstract class BaseResourceReference extends BaseElement {
* version specific. Internal fragment references (start with '#') refer to contained resources
* </p>
*/
public abstract StringDt getReference();
public abstract IdDt getResourceId();
/**
* Sets the resource ID
*
* <p>
* <b>Definition:</b> A reference to a location at which the other resource is found. The reference may a relative reference, in which case it is relative to the service base URL, or an absolute
* URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be
* version specific. Internal fragment references (start with '#') refer to contained resources
* </p>
*/
public abstract void setResourceId(IdDt theResourceId);
/**
* Gets the actual loaded and parsed resource instance, <b>if it is already present</b>. This
@ -94,21 +92,10 @@ public abstract class BaseResourceReference extends BaseElement {
return myResource;
}
public String getResourceId() {
return myResourceId;
}
public Class<? extends IResource> getResourceType() {
return myResourceType;
}
public String getResourceUrl() {
return getReference().getValue();
}
@Override
protected boolean isBaseEmpty() {
return super.isBaseEmpty() && myResource == null && myResourceType == null && StringUtils.isBlank(myResourceId);
return super.isBaseEmpty() && myResource == null;
}
/**
@ -120,12 +107,18 @@ public abstract class BaseResourceReference extends BaseElement {
return myResource;
}
ourLog.debug("Loading resource at URL: {}", getResourceUrl());
IdDt resourceId = getResourceId();
if (resourceId == null) {
throw new IllegalStateException("Reference has no resource ID defined");
}
String resourceUrl = resourceId.getValue();
ourLog.debug("Loading resource at URL: {}", resourceUrl);
HttpClient httpClient = theClient.getHttpClient();
FhirContext context = theClient.getFhirContext();
String resourceUrl = getResourceUrl();
if (!resourceUrl.startsWith("http")) {
resourceUrl = theClient.getServerBase() + resourceUrl;
}
@ -152,12 +145,4 @@ public abstract class BaseResourceReference extends BaseElement {
myResource = theResource;
}
public void setResourceId(String theResourceId) {
myResourceId = theResourceId;
}
public void setResourceType(Class<? extends IResource> theResourceType) {
myResourceType = theResourceType;
}
}

View File

@ -35,7 +35,6 @@ public class BundleEntry extends BaseBundle {
*****************************************************/
//@formatter:on
private TagList myCategories;
private boolean myDeleted;
private InstantDt myDeletedAt;
private StringDt myLinkSelf;
private InstantDt myPublished;
@ -62,11 +61,11 @@ public class BundleEntry extends BaseBundle {
}
/**
* Gets the date/time that thius entry was deleted.
* Gets the date/time that thius entry was deleted.
*/
public InstantDt getDeletedAt() {
if (myDeletedAt==null) {
myDeletedAt=new InstantDt();
if (myDeletedAt == null) {
myDeletedAt = new InstantDt();
}
return myDeletedAt;
}
@ -110,10 +109,6 @@ public class BundleEntry extends BaseBundle {
return myUpdated;
}
public boolean isDeleted() {
return myDeleted;
}
@Override
public boolean isEmpty() {
//@formatter:off
@ -122,12 +117,8 @@ public class BundleEntry extends BaseBundle {
//@formatter:on
}
public void setDeleted(boolean theDeleted) {
myDeleted = theDeleted;
}
/**
* Sets the date/time that this entry was deleted.
* Sets the date/time that this entry was deleted.
*/
public void setDeleted(InstantDt theDeletedAt) {
myDeletedAt = theDeletedAt;

View File

@ -70,7 +70,22 @@ public enum ResourceMetadataKeyEnum {
* <p>
* Values for this key are of type <b>{@link IdDt}</b>
* </p>
*
* @deprecated The {@link IResource#getId()} resource ID will now be populated with the version ID via the {@link IdDt#getUnqualifiedVersionId()} method
*/
VERSION_ID;
@Deprecated
VERSION_ID,
/**
* If present and populated with a date/time (as an instance of {@link InstantDt}),
* this value is an indication that the resource is in the deleted state. This key
* is only used in a limited number of scenarios, such as POSTing transaction bundles
* to a server, or returning resource history.
* <p>
* Values for this key are of type <b>{@link InstantDt}</b>
* </p>
*/
DELETED_AT;
}

View File

@ -1,19 +1,3 @@
package ca.uhn.fhir.model.dstu.composite;
/*
@ -49,22 +33,19 @@ import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.StringDt;
/**
* HAPI/FHIR <b>ResourceReferenceDt</b> Datatype
* (A reference from one resource to another)
*
* HAPI/FHIR <b>ResourceReferenceDt</b> Datatype (A reference from one resource to another)
*
* <p>
* <b>Definition:</b>
* A reference from one resource to another
* </p>
*
* <b>Definition:</b> A reference from one resource to another
* </p>
*
* <p>
* <b>Requirements:</b>
*
* </p>
* </p>
*/
@DatatypeDef(name="ResourceReferenceDt")
public class ResourceReferenceDt
extends BaseResourceReference implements ICompositeDatatype {
@DatatypeDef(name = "ResourceReferenceDt")
public class ResourceReferenceDt extends BaseResourceReference implements ICompositeDatatype {
/**
* Constructor
@ -74,79 +55,74 @@ public class ResourceReferenceDt
}
/**
* Constructor which creates a normal resource reference
*
* @param theResourceType The resource type
* @param theResourceId The resource ID
*/
public ResourceReferenceDt(Class<? extends IResource> theResourceType, String theResourceId) {
super(theResourceType, theResourceId);
}
/**
* Constructor which creates a normal resource reference
*
* @param theResourceType The resource type
* @param theResourceId The resource ID
*/
public ResourceReferenceDt(Class<? extends IResource> theResourceType, IdDt theResourceId) {
super(theResourceType, theResourceId);
}
/**
* Constructor which creates a resource reference containing the actual
* resource in question.
* Constructor which creates a resource reference containing the actual resource in question.
* <p>
* <b>
* When using this in a server:</b> Generally if this is serialized, it will be serialized
* as a contained resource, so this should not be used if the intent is not to actually
* supply the referenced resource. This is not a hard-and-fast rule however, as the
* server can be configured to not serialized this resource, or to load an ID and contain
* even if this constructor is not used.
* <b> When using this in a server:</b> Generally if this is serialized, it will be serialized as a contained
* resource, so this should not be used if the intent is not to actually supply the referenced resource. This is not
* a hard-and-fast rule however, as the server can be configured to not serialized this resource, or to load an ID
* and contain even if this constructor is not used.
* </p>
*
* @param theResource The resource instance
* @param theResource
* The resource instance
*/
public ResourceReferenceDt(IResource theResource) {
super(theResource);
}
@Child(name="reference", type=StringDt.class, order=0, min=0, max=1)
@Description(
shortDefinition="Relative, internal or absolute URL reference",
formalDefinition="A reference to a location at which the other resource is found. The reference may a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources"
)
/**
* Constructor which accepts a reference directly (this can be an ID, a partial/relative URL or a complete/absolute
* URL)
*
* @param theId
* The reference itself
*/
public ResourceReferenceDt(String theId) {
setResourceId(new IdDt(theId));
}
/**
* Constructor which accepts a reference directly (this can be an ID, a partial/relative URL or a complete/absolute
* URL)
*
* @param theId
* The reference itself
*/
public ResourceReferenceDt(IdDt theResourceId) {
setResourceId(theResourceId);
}
@Child(name = "reference", type = StringDt.class, order = 0, min = 0, max = 1)
@Description(shortDefinition = "Relative, internal or absolute URL reference", formalDefinition = "A reference to a location at which the other resource is found. The reference may a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources")
private StringDt myReference;
@Child(name="display", type=StringDt.class, order=1, min=0, max=1)
@Description(
shortDefinition="Text alternative for the resource",
formalDefinition="Plain text narrative that identifies the resource in addition to the resource reference"
)
@Child(name = "display", type = StringDt.class, order = 1, min = 0, max = 1)
@Description(shortDefinition = "Text alternative for the resource", formalDefinition = "Plain text narrative that identifies the resource in addition to the resource reference")
private StringDt myDisplay;
@Override
public boolean isEmpty() {
return super.isBaseEmpty() && ca.uhn.fhir.util.ElementUtil.isEmpty( myReference, myDisplay);
return super.isBaseEmpty() && ca.uhn.fhir.util.ElementUtil.isEmpty(myReference, myDisplay);
}
@Override
public <T extends IElement> List<T> getAllPopulatedChildElementsOfType(Class<T> theType) {
return ca.uhn.fhir.util.ElementUtil.allPopulatedChildElements(theType, myReference, myDisplay);
}
/**
* Gets the value(s) for <b>reference</b> (Relative, internal or absolute URL reference).
* creating it if it does
* not exist. Will not return <code>null</code>.
*
* <p>
* <b>Definition:</b>
* A reference to a location at which the other resource is found. The reference may a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources
* </p>
* Gets the value(s) for <b>reference</b> (Relative, internal or absolute URL reference). creating it if it does not
* exist. Will not return <code>null</code>.
*
* <p>
* <b>Definition:</b> A reference to a location at which the other resource is found. The reference may a relative
* reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location
* where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR
* RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#')
* refer to contained resources
* </p>
*/
public StringDt getReference() {
public StringDt getReference() {
if (myReference == null) {
myReference = new StringDt();
}
@ -155,41 +131,44 @@ public class ResourceReferenceDt
/**
* Sets the value(s) for <b>reference</b> (Relative, internal or absolute URL reference)
*
* <p>
* <b>Definition:</b>
* A reference to a location at which the other resource is found. The reference may a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources
* </p>
*
* <p>
* <b>Definition:</b> A reference to a location at which the other resource is found. The reference may a relative
* reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location
* where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR
* RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#')
* refer to contained resources
* </p>
*/
public ResourceReferenceDt setReference(StringDt theValue) {
myReference = theValue;
return this;
}
/**
/**
* Sets the value for <b>reference</b> (Relative, internal or absolute URL reference)
*
* <p>
* <b>Definition:</b>
* A reference to a location at which the other resource is found. The reference may a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources
* </p>
*
* <p>
* <b>Definition:</b> A reference to a location at which the other resource is found. The reference may a relative
* reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location
* where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR
* RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#')
* refer to contained resources
* </p>
*/
public ResourceReferenceDt setReference( String theString) {
return setReference(new StringDt(theString));
public ResourceReferenceDt setReference(String theString) {
return setReference(new StringDt(theString));
}
/**
* Gets the value(s) for <b>display</b> (Text alternative for the resource).
* creating it if it does
* not exist. Will not return <code>null</code>.
*
* <p>
* <b>Definition:</b>
* Plain text narrative that identifies the resource in addition to the resource reference
* </p>
* Gets the value(s) for <b>display</b> (Text alternative for the resource). creating it if it does not exist. Will
* not return <code>null</code>.
*
* <p>
* <b>Definition:</b> Plain text narrative that identifies the resource in addition to the resource reference
* </p>
*/
public StringDt getDisplay() {
public StringDt getDisplay() {
if (myDisplay == null) {
myDisplay = new StringDt();
}
@ -198,32 +177,36 @@ public class ResourceReferenceDt
/**
* Sets the value(s) for <b>display</b> (Text alternative for the resource)
*
* <p>
* <b>Definition:</b>
* Plain text narrative that identifies the resource in addition to the resource reference
* </p>
*
* <p>
* <b>Definition:</b> Plain text narrative that identifies the resource in addition to the resource reference
* </p>
*/
public ResourceReferenceDt setDisplay(StringDt theValue) {
myDisplay = theValue;
return this;
}
/**
/**
* Sets the value for <b>display</b> (Text alternative for the resource)
*
* <p>
* <b>Definition:</b>
* Plain text narrative that identifies the resource in addition to the resource reference
* </p>
*
* <p>
* <b>Definition:</b> Plain text narrative that identifies the resource in addition to the resource reference
* </p>
*/
public ResourceReferenceDt setDisplay( String theString) {
myDisplay = new StringDt(theString);
return this;
public ResourceReferenceDt setDisplay(String theString) {
myDisplay = new StringDt(theString);
return this;
}
@Override
public IdDt getResourceId() {
return new IdDt(myReference.getValue());
}
@Override
public void setResourceId(IdDt theResourceId) {
myReference = new StringDt(theResourceId.getValue());
}
}

View File

@ -22,17 +22,20 @@ package ca.uhn.fhir.model.primitive;
import java.math.BigDecimal;
import org.apache.commons.lang3.StringUtils;
import ca.uhn.fhir.model.api.BasePrimitive;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
import ca.uhn.fhir.model.api.annotation.SimpleSetter;
import ca.uhn.fhir.parser.DataFormatException;
/**
* Represents the FHIR ID type. This is the actual resource ID, meaning the ID that will be used in RESTful URLs, Resource References, etc. to represent a specific instance of a resource.
* Represents the FHIR ID type. This is the actual resource ID, meaning the ID that will be used in RESTful URLs,
* Resource References, etc. to represent a specific instance of a resource.
*
* <p>
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or any other combination of lowercase letters, numerals, "-" and ".", with a length
* limit of 36 characters.
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or any
* other combination of lowercase letters, numerals, "-" and ".", with a length limit of 36 characters.
* </p>
* <p>
* regex: [a-z0-9\-\.]{1,36}
@ -41,7 +44,10 @@ import ca.uhn.fhir.parser.DataFormatException;
@DatatypeDef(name = "id")
public class IdDt extends BasePrimitive<String> {
private String myUnqualifiedId;
private String myValue;
private String myUnqualifiedVersionId;
private String myResourceType;
/**
* Create a new empty ID
@ -51,7 +57,8 @@ public class IdDt extends BasePrimitive<String> {
}
/**
* Create a new ID, using a BigDecimal input. Uses {@link BigDecimal#toPlainString()} to generate the string representation.
* Create a new ID, using a BigDecimal input. Uses {@link BigDecimal#toPlainString()} to generate the string
* representation.
*/
public IdDt(BigDecimal thePid) {
if (thePid != null) {
@ -69,12 +76,12 @@ public class IdDt extends BasePrimitive<String> {
}
/**
* Create a new ID using a string. This String may contain a simple ID (e.g. "1234")
* or it may contain a complete URL (http://example.com/fhir/Patient/1234).
* Create a new ID using a string. This String may contain a simple ID (e.g. "1234") or it may contain a complete
* URL (http://example.com/fhir/Patient/1234).
*
* <p>
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or any other combination of lowercase letters, numerals, "-" and ".", with a length
* limit of 36 characters.
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or
* any other combination of lowercase letters, numerals, "-" and ".", with a length limit of 36 characters.
* </p>
* <p>
* regex: [a-z0-9\-\.]{1,36}
@ -85,12 +92,8 @@ public class IdDt extends BasePrimitive<String> {
setValue(theValue);
}
/**
* Returns the value of this ID as a big decimal, or <code>null</code> if the value is null
* Returns the unqualified portion of this ID as a big decimal, or <code>null</code> if the value is null
*
* @throws NumberFormatException
* If the value is not a valid BigDecimal
@ -103,7 +106,7 @@ public class IdDt extends BasePrimitive<String> {
}
/**
* Returns the value of this ID as a {@link Long}, or <code>null</code> if the value is null
* Returns the unqualified portion of this ID as a {@link Long}, or <code>null</code> if the value is null
*
* @throws NumberFormatException
* If the value is not a valid Long
@ -114,15 +117,34 @@ public class IdDt extends BasePrimitive<String> {
}
return Long.parseLong(getValueAsString());
}
/**
* Returns a reference to <code>this</code> IdDt. It is generally not neccesary to use this method but it is provided for consistency with the rest of the API.
* Returns a reference to <code>this</code> IdDt. It is generally not neccesary to use this method but it is
* provided for consistency with the rest of the API.
*/
@Override
public IdDt getId() {
return this;
}
public String getResourceType() {
return myResourceType;
}
public String getUnqualifiedId() {
return myUnqualifiedId;
}
public String getUnqualifiedVersionId() {
return myUnqualifiedVersionId;
}
/**
* Returns the value of this ID. Note that this value may be a fully qualified URL, a relative/partial URL, or
* a simple ID. Use {@link #getUnqualifiedId()} to get just the ID portion.
*
* @see #getUnqualifiedId()
*/
@Override
public String getValue() {
return myValue;
@ -134,7 +156,8 @@ public class IdDt extends BasePrimitive<String> {
}
/**
* Copies the value from the given IdDt to <code>this</code> IdDt. It is generally not neccesary to use this method but it is provided for consistency with the rest of the API.
* Copies the value from the given IdDt to <code>this</code> IdDt. It is generally not neccesary to use this method
* but it is provided for consistency with the rest of the API.
*/
@Override
public void setId(IdDt theId) {
@ -145,8 +168,8 @@ public class IdDt extends BasePrimitive<String> {
* Set the value
*
* <p>
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or any other combination of lowercase letters, numerals, "-" and ".", with a length
* limit of 36 characters.
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or
* any other combination of lowercase letters, numerals, "-" and ".", with a length limit of 36 characters.
* </p>
* <p>
* regex: [a-z0-9\-\.]{1,36}
@ -156,14 +179,44 @@ public class IdDt extends BasePrimitive<String> {
public void setValue(String theValue) throws DataFormatException {
// TODO: add validation
myValue = theValue;
if (StringUtils.isBlank(theValue)) {
myValue = null;
myUnqualifiedId = null;
myUnqualifiedVersionId = null;
myResourceType = null;
} else {
int vidIndex = theValue.indexOf("/_history/");
int idIndex;
if (vidIndex != -1) {
myUnqualifiedVersionId = theValue.substring(vidIndex + "/_history/".length());
idIndex = theValue.lastIndexOf('/', vidIndex - 1);
myUnqualifiedId = theValue.substring(idIndex + 1, vidIndex);
} else {
idIndex = theValue.lastIndexOf('/');
myUnqualifiedId = theValue.substring(idIndex + 1);
myUnqualifiedVersionId = null;
}
if (idIndex <= 0) {
myResourceType = null;
}else {
int typeIndex = theValue.lastIndexOf('/', idIndex - 1);
if (typeIndex == -1) {
myResourceType = theValue.substring(0,idIndex);
} else {
myResourceType = theValue.substring(typeIndex+1, idIndex);
}
}
}
}
/**
* Set the value
*
* <p>
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or any other combination of lowercase letters, numerals, "-" and ".", with a length
* limit of 36 characters.
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or
* any other combination of lowercase letters, numerals, "-" and ".", with a length limit of 36 characters.
* </p>
* <p>
* regex: [a-z0-9\-\.]{1,36}

View File

@ -205,7 +205,7 @@ public class JsonParser extends BaseParser implements IParser {
writeAuthor(nextEntry, eventWriter);
IResource resource = nextEntry.getResource();
if (resource != null) {
if (resource != null && !resource.isEmpty()) {
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(resource);
encodeResourceToJsonStreamWriter(resDef, resource, eventWriter, "content");
}
@ -264,25 +264,26 @@ public class JsonParser extends BaseParser implements IParser {
break;
}
case RESOURCE_REF: {
ResourceReferenceDt value = (ResourceReferenceDt) theValue;
ResourceReferenceDt referenceDt = (ResourceReferenceDt) theValue;
IdDt value = referenceDt.getResourceId();
if (theChildName != null) {
theWriter.writeStartObject(theChildName);
} else {
theWriter.writeStartObject();
}
String reference = value.getReference().getValue();
String reference = value.getValue();
if (StringUtils.isBlank(reference)) {
if (value.getResourceType() != null && StringUtils.isNotBlank(value.getResourceId())) {
reference = myContext.getResourceDefinition(value.getResourceType()).getName() + '/' + value.getResourceId();
if (value.getResourceType() != null && StringUtils.isNotBlank(value.getUnqualifiedId())) {
reference = myContext.getResourceDefinition(value.getResourceType()).getName() + '/' + value.getUnqualifiedId();
}
}
if (StringUtils.isNotBlank(reference)) {
theWriter.write("resource", reference);
}
if (value.getDisplay().isEmpty() == false) {
theWriter.write("display", value.getDisplay().getValueAsString());
if (referenceDt.getDisplay().isEmpty() == false) {
theWriter.write("display", referenceDt.getDisplay().getValueAsString());
}
theWriter.writeEnd();
break;
@ -836,7 +837,7 @@ public class JsonParser extends BaseParser implements IParser {
}
private boolean writeAtomLink(JsonGenerator theEventWriter, String theRel, StringDt theLink, boolean theStarted) {
boolean retVal = false;
boolean retVal = theStarted;
if (isNotBlank(theLink.getValue())) {
if (theStarted==false) {
theEventWriter.writeStartArray("link");

View File

@ -120,9 +120,8 @@ class ParserState<T> {
}
/**
* Invoked after any new XML event is individually processed, containing a
* copy of the XML event. This is basically intended for embedded XHTML
* content
* Invoked after any new XML event is individually processed, containing a copy of the XML event. This is basically
* intended for embedded XHTML content
*/
public void xmlEvent(XMLEvent theNextEvent) {
myState.xmlEvent(theNextEvent);
@ -215,8 +214,8 @@ class ParserState<T> {
myInstance.setScheme(theValue);
} else if ("value".equals(theName)) {
/*
* This handles XML parsing, which is odd for this quasi-resource type,
* since the tag has three values instead of one like everything else.
* This handles XML parsing, which is odd for this quasi-resource type, since the tag has three values
* instead of one like everything else.
*/
switch (myCatState) {
case STATE_LABEL:
@ -261,10 +260,32 @@ class ParserState<T> {
}
private void putPlacerResourceInDeletedEntry(BundleEntry entry) {
IdDt id = null;
if (entry.getLinkSelf() != null && entry.getLinkSelf().isEmpty() == false) {
id = new IdDt(entry.getLinkSelf().getValue());
} else {
id = entry.getId();
}
IResource resource = entry.getResource();
if (resource == null && id != null && isNotBlank(id.getResourceType())) {
resource = myContext.getResourceDefinition(id.getResourceType()).newInstance();
resource.setId(id);
entry.setResource(resource);
}
if (resource != null) {
resource.getResourceMetadata().put(ResourceMetadataKeyEnum.DELETED_AT, entry.getDeletedAt());
resource.getResourceMetadata().put(ResourceMetadataKeyEnum.VERSION_ID, id);
}
}
public class AtomEntryState extends BaseState {
private BundleEntry myEntry;
private Class<? extends IResource> myResourceType;
private boolean myDeleted;
public AtomEntryState(Bundle theInstance, Class<? extends IResource> theResourceType) {
super(null);
@ -281,6 +302,10 @@ class ParserState<T> {
public void endingElement() throws DataFormatException {
populateResourceMetadata();
pop();
if (myDeleted) {
putPlacerResourceInDeletedEntry(myEntry);
}
}
@Override
@ -305,7 +330,7 @@ class ParserState<T> {
push(new AtomCategoryState(myEntry.addCategory()));
} else if ("deleted".equals(theLocalPart) && myJsonMode) {
// JSON and XML deleted entries are completely different for some reason
myEntry.setDeleted(true);
myDeleted = true;
push(new AtomDeletedJsonWhenState(myEntry.getDeletedAt()));
} else {
throw new DataFormatException("Unexpected element in entry: " + theLocalPart);
@ -314,6 +339,7 @@ class ParserState<T> {
// TODO: handle category
}
@SuppressWarnings("deprecation")
private void populateResourceMetadata() {
if (myEntry.getResource() == null) {
return;
@ -335,59 +361,21 @@ class ParserState<T> {
}
if (!myEntry.getLinkSelf().isEmpty()) {
String linkSelfValue = myEntry.getLinkSelf().getValue();
/*
* Find resource ID if it is there
*/
String resNameLc = myContext.getResourceDefinition(myEntry.getResource()).getName().toLowerCase();
String subStrId = "/" + resNameLc + "/";
int idIdx = linkSelfValue.toLowerCase().lastIndexOf(subStrId);
if (idIdx != -1) {
int endIndex = linkSelfValue.indexOf('/', idIdx + subStrId.length());
String id;
if (endIndex == -1) {
id = linkSelfValue.substring(idIdx + subStrId.length());
} else {
id = linkSelfValue.substring(idIdx + subStrId.length(), endIndex);
}
myEntry.getResource().setId(new IdDt(id));
}
/*
* Find resource version ID if it is there
*/
String subStrVid = "/" + Constants.PARAM_HISTORY + "/";
int startIndex = linkSelfValue.indexOf(subStrVid);
if (startIndex > 0) {
startIndex = startIndex + subStrVid.length();
int endIndex = linkSelfValue.indexOf('?', startIndex);
if (endIndex == -1) {
endIndex = linkSelfValue.length();
}
String versionId = linkSelfValue.substring(startIndex, endIndex);
if (isNotBlank(versionId)) {
int idx = versionId.indexOf('/');
if (idx != -1) {
// Just in case
ourLog.warn("Bundle entry link-self contains path information beyond version (this will be ignored): {}", versionId);
versionId = versionId.substring(0, idx);
}
metadata.put(ResourceMetadataKeyEnum.VERSION_ID, new IdDt(versionId));
}
IdDt id = new IdDt(linkSelfValue);
myEntry.getResource().setId(id);
if (isNotBlank(id.getUnqualifiedVersionId())) {
metadata.put(ResourceMetadataKeyEnum.VERSION_ID, id);
}
}
}
}
public class AtomDeletedEntryState extends AtomEntryState {
public AtomDeletedEntryState(Bundle theInstance, Class<? extends IResource> theResourceType) {
super(theInstance, theResourceType);
getEntry().setDeleted(true);
}
@Override
@ -399,9 +387,13 @@ class ParserState<T> {
}
}
@Override
public void endingElement() throws DataFormatException {
putPlacerResourceInDeletedEntry(getEntry());
super.endingElement();
}
}
private class AtomLinkState extends BaseState {
@ -506,7 +498,6 @@ class ParserState<T> {
}
private class AtomDeletedJsonWhenState extends BaseState {
private String myData;
@ -529,7 +520,6 @@ class ParserState<T> {
throw new DataFormatException("Unexpected nested element in atom tag: " + theLocalPart);
}
@Override
public void attributeValue(String theName, String theValue) throws DataFormatException {
myData = theValue;
@ -541,7 +531,7 @@ class ParserState<T> {
}
}
private class AtomState extends BaseState {
private Bundle myInstance;
@ -577,7 +567,7 @@ class ParserState<T> {
} else if ("author".equals(theLocalPart)) {
push(new AtomAuthorState(myInstance));
} else if ("deleted-entry".equals(theLocalPart) && verifyNamespace(XmlParser.TOMBSTONES_NS, theNamespaceURI)) {
push(new AtomDeletedEntryState(myInstance,myResourceType));
push(new AtomDeletedEntryState(myInstance, myResourceType));
} else {
if (theNamespaceURI != null) {
throw new DataFormatException("Unexpected element: {" + theNamespaceURI + "}" + theLocalPart);
@ -740,7 +730,7 @@ class ParserState<T> {
case RESOURCE_REF: {
ResourceReferenceDt newChildInstance = new ResourceReferenceDt();
myDefinition.getMutator().addValue(myParentInstance, newChildInstance);
ResourceReferenceState newState = new ResourceReferenceState(getPreResourceState(), (RuntimeResourceReferenceDefinition)target, newChildInstance);
ResourceReferenceState newState = new ResourceReferenceState(getPreResourceState(), (RuntimeResourceReferenceDefinition) target, newChildInstance);
push(newState);
return;
}
@ -1221,25 +1211,25 @@ class ParserState<T> {
case INITIAL:
throw new DataFormatException("Unexpected attribute: " + theValue);
case REFERENCE:
int lastSlash = theValue.lastIndexOf('/');
if (lastSlash==-1) {
myInstance.setResourceId(theValue);
} else if (lastSlash==0) {
myInstance.setResourceId(theValue.substring(1));
}else {
int secondLastSlash=theValue.lastIndexOf('/', lastSlash-1);
String resourceTypeName;
if (secondLastSlash==-1) {
resourceTypeName=theValue.substring(0,lastSlash);
}else {
resourceTypeName=theValue.substring(secondLastSlash+1,lastSlash);
}
myInstance.setResourceId(theValue.substring(lastSlash+1));
RuntimeResourceDefinition def = myContext.getResourceDefinition(resourceTypeName);
if(def!=null) {
myInstance.setResourceType(def.getImplementingClass());
}
}
// int lastSlash = theValue.lastIndexOf('/');
// if (lastSlash==-1) {
// myInstance.setResourceId(theValue);
// } else if (lastSlash==0) {
// myInstance.setResourceId(theValue.substring(1));
// }else {
// int secondLastSlash=theValue.lastIndexOf('/', lastSlash-1);
// String resourceTypeName;
// if (secondLastSlash==-1) {
// resourceTypeName=theValue.substring(0,lastSlash);
// }else {
// resourceTypeName=theValue.substring(secondLastSlash+1,lastSlash);
// }
// myInstance.setResourceId(theValue.substring(lastSlash+1));
// RuntimeResourceDefinition def = myContext.getResourceDefinition(resourceTypeName);
// if(def!=null) {
// myInstance.setResourceType(def.getImplementingClass());
// }
// }
myInstance.setReference(theValue);
break;
}

View File

@ -176,7 +176,7 @@ public class XmlParser extends BaseParser implements IParser {
IResource resource = nextEntry.getResource();
if (resource != null) {
if (resource != null && !resource.isEmpty()) {
eventWriter.writeStartElement("content");
eventWriter.writeAttribute("type", "text/xml");
encodeResourceToXmlStreamWriter(resource, eventWriter, false);
@ -506,11 +506,11 @@ public class XmlParser extends BaseParser implements IParser {
private void encodeResourceReferenceToStreamWriter(XMLStreamWriter theEventWriter, ResourceReferenceDt theRef) throws XMLStreamException {
String reference = theRef.getReference().getValue();
if (StringUtils.isBlank(reference)) {
if (theRef.getResourceType() != null && StringUtils.isNotBlank(theRef.getResourceId())) {
reference = myContext.getResourceDefinition(theRef.getResourceType()).getName() + '/' + theRef.getResourceId();
}
}
// if (StringUtils.isBlank(reference)) {
// if (theRef.getResourceType() != null && StringUtils.isNotBlank(theRef.getResourceId())) {
// reference = myContext.getResourceDefinition(theRef.getResourceType()).getName() + '/' + theRef.getResourceId();
// }
// }
if (!(theRef.getDisplay().isEmpty())) {
theEventWriter.writeStartElement("display");

View File

@ -0,0 +1,25 @@
package ca.uhn.fhir.rest.method;
/*
* #%L
* HAPI FHIR Library
* %%
* Copyright (C) 2014 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%
*/
public class MethodUtil {
}

View File

@ -110,7 +110,7 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding {
public List<IResource> invokeServer(Request theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
theMethodParams[myIdIndex] = theRequest.getId();
if (myVersionIdIndex != null) {
theMethodParams[myVersionIdIndex] = theRequest.getVersionId();
theMethodParams[myVersionIdIndex] = new IdDt(theRequest.getVersionId().getUnqualifiedVersionId());
}
Object response = invokeServerMethod(theMethodParams);
@ -140,11 +140,11 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding {
}
public static GetClientInvocation createVReadInvocation(IdDt theId, IdDt vid, String resourceName) {
return new GetClientInvocation(resourceName, theId.getValue(), Constants.URL_TOKEN_HISTORY, vid.getValue());
return new GetClientInvocation(resourceName, theId.getUnqualifiedId(), Constants.URL_TOKEN_HISTORY, vid.getUnqualifiedId());
}
public static GetClientInvocation createReadInvocation(IdDt theId, String resourceName) {
return new GetClientInvocation(resourceName, theId.getValue());
return new GetClientInvocation(resourceName, theId.getUnqualifiedId());
}
@Override

View File

@ -1,5 +1,25 @@
package ca.uhn.fhir.rest.method;
/*
* #%L
* HAPI FHIR Library
* %%
* Copyright (C) 2014 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 static org.apache.commons.lang3.StringUtils.*;
import java.lang.reflect.Method;

View File

@ -21,6 +21,7 @@ package ca.uhn.fhir.rest.param;
*/
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@ -28,6 +29,7 @@ import java.util.Map;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.BundleEntry;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.rest.annotation.TransactionParam;
import ca.uhn.fhir.rest.client.BaseClientInvocation;
@ -49,7 +51,15 @@ public class TransactionParameter implements IParameter {
@Override
public Object translateQueryParametersIntoServerArgument(Request theRequest, Object theRequestContents) throws InternalErrorException, InvalidRequestException {
Bundle resource = (Bundle) theRequestContents;
return resource;
ArrayList<IResource> retVal = new ArrayList<IResource>();
for (BundleEntry next : resource.getEntries()) {
if (next.getResource() != null) {
retVal.add(next.getResource());
}
}
return retVal;
}
@Override

View File

@ -51,7 +51,6 @@ import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.provider.ServerConformanceProvider;
import ca.uhn.fhir.rest.server.provider.ServerProfileProvider;
import ca.uhn.fhir.util.VersionUtil;
@ -539,7 +538,10 @@ public class RestfulServer extends HttpServlet {
if (nextString.equals(Constants.PARAM_HISTORY)) {
if (tok.hasMoreTokens()) {
String versionString = tok.nextToken();
versionId = new IdDt(versionString);
if (id == null) {
throw new InvalidRequestException("Don't know how to handle request path: " + requestPath);
}
versionId = new IdDt(resourceName + "/" + id.getUnqualifiedId() + "/_history/" + versionString);
} else {
operation = Constants.PARAM_HISTORY;
}
@ -568,11 +570,7 @@ public class RestfulServer extends HttpServlet {
if (theRequestType == RequestType.PUT && versionId == null) {
String contentLocation = theRequest.getHeader("Content-Location");
if (contentLocation != null) {
int idx = contentLocation.indexOf("/_history/");
if (idx != -1) {
String versionIdString = contentLocation.substring(idx + "/_history/".length());
versionId = new IdDt(versionIdString);
}
versionId = new IdDt(contentLocation);
}
}

View File

@ -32,6 +32,7 @@ import ca.uhn.fhir.model.dstu.valueset.IssueSeverityEnum;
import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum;
import ca.uhn.fhir.model.primitive.DecimalDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.annotation.AddTags;
@ -50,6 +51,8 @@ import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.annotation.Since;
import ca.uhn.fhir.rest.annotation.Sort;
import ca.uhn.fhir.rest.annotation.Transaction;
import ca.uhn.fhir.rest.annotation.TransactionParam;
import ca.uhn.fhir.rest.annotation.Update;
import ca.uhn.fhir.rest.annotation.Validate;
import ca.uhn.fhir.rest.annotation.VersionIdParam;
@ -742,6 +745,31 @@ public class TagMethodProvider
}
//END SNIPPET: tagMethodProvider
//START SNIPPET: transaction
@Transaction
public List<IResource> transaction(@TransactionParam List<IResource> theResources) {
// theResources will contain a complete bundle of all resources to persist
// in a single transaction
for (IResource next : theResources) {
InstantDt deleted = (InstantDt) next.getResourceMetadata().get(ResourceMetadataKeyEnum.DELETED_AT);
if (deleted != null && deleted.isEmpty() == false) {
// delete this resource
} else {
// create or update this resource
}
}
ArrayList<IResource> retVal = new ArrayList<IResource>();
/*
* According to the specification, a bundle must be returned. This bundle will contain
* all of the created/updated/deleted resources, including their new/updated identities.
*
* Implementing this will depend on your specific application
*/
return retVal;
}
//END SNIPPET: transaction
}

View File

@ -1090,9 +1090,31 @@
<section name="System Level - Transaction">
<p>
Not yet implemented - Get in touch if you would like to help!
The
<a href="http://hl7.org/implement/standards/fhir/http.html#transaction">transaction</a>
action is among the most challenging parts of the FHIR specification to implement. It allows the
user to submit a bundle containing a number of resources to be created/updated/deleted as a single
atomic transaction.
</p>
<p>
HAPI provides a skeleton for implementing this action, although most of the effort
will depend on the underlying implementation. The following example shows
how to define a <i>transaction</i> method.
</p>
<macro name="snippet">
<param name="id" value="transaction" />
<param name="file" value="src/site/example/java/example/RestfulPatientResourceProviderMore.java" />
</macro>
<p>
Example URL to invoke this method:
<br />
<code>POST http://fhir.example.com/</code><br/>
<i>(note that the content of this POST will be a bundle)</i>
</p>
<a name="system_search" />
</section>

View File

@ -18,79 +18,8 @@ public class ResourceReferenceDtTest {
@Test
public void testParseValueAbsolute() {
Patient patient = new Patient();
ResourceReferenceDt rr = new ResourceReferenceDt();
rr.setReference("http://foo/fhir/Organization/123");
patient.setManagingOrganization(rr);
Patient actual = parseAndEncode(patient);
rr = actual.getManagingOrganization();
assertEquals(Organization.class, rr.getResourceType());
assertEquals("123", rr.getResourceId());
}
@Test
public void testParseValueMissingType1() {
Patient patient = new Patient();
ResourceReferenceDt rr = new ResourceReferenceDt();
rr.setReference("/123");
patient.setManagingOrganization(rr);
Patient actual = parseAndEncode(patient);
rr = actual.getManagingOrganization();
assertEquals(null, rr.getResourceType());
assertEquals("123", rr.getResourceId());
}
@Test
public void testParseValueMissingType2() {
Patient patient = new Patient();
ResourceReferenceDt rr = new ResourceReferenceDt();
rr.setReference("123");
patient.setManagingOrganization(rr);
Patient actual = parseAndEncode(patient);
rr = actual.getManagingOrganization();
assertEquals(null, rr.getResourceType());
assertEquals("123", rr.getResourceId());
}
@Test
public void testParseValueRelative1() {
Patient patient = new Patient();
ResourceReferenceDt rr = new ResourceReferenceDt();
rr.setReference("Organization/123");
patient.setManagingOrganization(rr);
Patient actual = parseAndEncode(patient);
rr = actual.getManagingOrganization();
assertEquals(Organization.class, rr.getResourceType());
assertEquals("123", rr.getResourceId());
}
@Test
public void testParseValueRelative2() {
Patient patient = new Patient();
ResourceReferenceDt rr = new ResourceReferenceDt();
rr.setReference("/Organization/123");
patient.setManagingOrganization(rr);
Patient actual = parseAndEncode(patient);
rr = actual.getManagingOrganization();
assertEquals(Organization.class, rr.getResourceType());
assertEquals("123", rr.getResourceId());
}
private Patient parseAndEncode(Patient patient) {
String encoded = ourCtx.newXmlParser().encodeResourceToString(patient);
ourLog.info("\n" + encoded);
return ourCtx.newXmlParser().parseResource(Patient.class, encoded);
}
@BeforeClass
public static void beforeClass() {

View File

@ -0,0 +1,130 @@
package ca.uhn.fhir.model.primitive;
import static org.junit.Assert.assertEquals;
import org.junit.BeforeClass;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu.resource.Patient;
public class IdDtTest {
private static FhirContext ourCtx;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(IdDtTest.class);
@Test
public void testParseValueAbsolute() {
Patient patient = new Patient();
IdDt rr = new IdDt();
rr.setValue("http://foo/fhir/Organization/123");
patient.setManagingOrganization(new ResourceReferenceDt(rr));
Patient actual = parseAndEncode(patient);
ResourceReferenceDt ref = actual.getManagingOrganization();
assertEquals("Organization", ref.getResourceId().getResourceType());
assertEquals("123", ref.getResourceId().getUnqualifiedId());
}
@Test
public void testParseValueAbsoluteWithVersion() {
Patient patient = new Patient();
IdDt rr = new IdDt();
rr.setValue("http://foo/fhir/Organization/123/_history/999");
patient.setManagingOrganization(new ResourceReferenceDt(rr));
Patient actual = parseAndEncode(patient);
ResourceReferenceDt ref = actual.getManagingOrganization();
assertEquals("Organization", ref.getResourceId().getResourceType());
assertEquals("123", ref.getResourceId().getUnqualifiedId());
assertEquals("999", ref.getResourceId().getUnqualifiedVersionId());
}
@Test
public void testParseValueWithVersion() {
Patient patient = new Patient();
IdDt rr = new IdDt();
rr.setValue("/123/_history/999");
patient.setManagingOrganization(new ResourceReferenceDt(rr));
Patient actual = parseAndEncode(patient);
ResourceReferenceDt ref = actual.getManagingOrganization();
assertEquals(null, ref.getResourceId().getResourceType());
assertEquals("123", ref.getResourceId().getUnqualifiedId());
assertEquals("999", ref.getResourceId().getUnqualifiedVersionId());
}
@Test
public void testParseValueMissingType1() {
Patient patient = new Patient();
IdDt rr = new IdDt();
rr.setValue("/123");
patient.setManagingOrganization(new ResourceReferenceDt(rr));
Patient actual = parseAndEncode(patient);
ResourceReferenceDt ref = actual.getManagingOrganization();
assertEquals(null, ref.getResourceId().getResourceType());
assertEquals("123", ref.getResourceId().getUnqualifiedId());
}
@Test
public void testParseValueMissingType2() {
Patient patient = new Patient();
IdDt rr = new IdDt();
rr.setValue("123");
patient.setManagingOrganization(new ResourceReferenceDt(rr));
Patient actual = parseAndEncode(patient);
ResourceReferenceDt ref = actual.getManagingOrganization();
assertEquals(null, ref.getResourceId().getResourceType());
assertEquals("123", ref.getResourceId().getUnqualifiedId());
}
@Test
public void testParseValueRelative1() {
Patient patient = new Patient();
IdDt rr = new IdDt();
rr.setValue("Organization/123");
patient.setManagingOrganization(new ResourceReferenceDt(rr));
Patient actual = parseAndEncode(patient);
ResourceReferenceDt ref = actual.getManagingOrganization();
assertEquals("Organization", ref.getResourceId().getResourceType());
assertEquals("123", ref.getResourceId().getUnqualifiedId());
}
@Test
public void testParseValueRelative2() {
Patient patient = new Patient();
IdDt rr = new IdDt();
rr.setValue("/Organization/123");
patient.setManagingOrganization(new ResourceReferenceDt(rr));
Patient actual = parseAndEncode(patient);
ResourceReferenceDt ref = actual.getManagingOrganization();
assertEquals("Organization", ref.getResourceId().getResourceType());
assertEquals("123", ref.getResourceId().getUnqualifiedId());
}
private Patient parseAndEncode(Patient patient) {
String encoded = ourCtx.newXmlParser().encodeResourceToString(patient);
ourLog.info("\n" + encoded);
return ourCtx.newXmlParser().parseResource(Patient.class, encoded);
}
@BeforeClass
public static void beforeClass() {
ourCtx = new FhirContext();
}
}

View File

@ -1,10 +1,7 @@
package ca.uhn.fhir.parser;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.*;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ -28,6 +25,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.BundleEntry;
import ca.uhn.fhir.model.api.ExtensionDt;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.Extension;
@ -49,6 +47,7 @@ import ca.uhn.fhir.model.dstu.resource.ValueSet.DefineConcept;
import ca.uhn.fhir.model.dstu.valueset.AddressUseEnum;
import ca.uhn.fhir.model.dstu.valueset.NarrativeStatusEnum;
import ca.uhn.fhir.model.primitive.DecimalDt;
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;
@ -152,8 +151,7 @@ public class JsonParserTest {
assertEquals("term", b.getEntries().get(0).getCategories().get(0).getTerm());
assertEquals("label", b.getEntries().get(0).getCategories().get(0).getLabel());
assertEquals("scheme", b.getEntries().get(0).getCategories().get(0).getScheme());
assertNotNull(b.getEntries().get(0).getResource());
assertEquals(Patient.class, b.getEntries().get(0).getResource().getClass());
assertNull(b.getEntries().get(0).getResource());
}
@ -221,7 +219,7 @@ public class JsonParserTest {
MyPatientWithOneDeclaredExtension patient = new MyPatientWithOneDeclaredExtension();
patient.addAddress().setUse(AddressUseEnum.HOME);
patient.setFoo(new ResourceReferenceDt(Organization.class, "123"));
patient.setFoo(new ResourceReferenceDt("Organization/123"));
String val = parser.encodeResourceToString(patient);
ourLog.info(val);
@ -230,7 +228,7 @@ public class JsonParserTest {
MyPatientWithOneDeclaredExtension actual = parser.parseResource(MyPatientWithOneDeclaredExtension.class, val);
assertEquals(AddressUseEnum.HOME, patient.getAddressFirstRep().getUse().getValueAsEnum());
ResourceReferenceDt ref = actual.getFoo();
assertEquals("Organization/123", ref.getResourceUrl());
assertEquals("Organization/123", ref.getResourceId().getValue());
}
@ -257,7 +255,7 @@ public class JsonParserTest {
Patient patient = new Patient();
patient.addAddress().setUse(AddressUseEnum.HOME);
patient.addUndeclaredExtension(false, "urn:foo", new ResourceReferenceDt(Organization.class, "123"));
patient.addUndeclaredExtension(false, "urn:foo", new ResourceReferenceDt("Organization/123"));
String val = parser.encodeResourceToString(patient);
ourLog.info(val);
@ -268,7 +266,7 @@ public class JsonParserTest {
List<ExtensionDt> ext = actual.getUndeclaredExtensionsByUrl("urn:foo");
assertEquals(1, ext.size());
ResourceReferenceDt ref = (ResourceReferenceDt) ext.get(0).getValue();
assertEquals("Organization/123", ref.getResourceUrl());
assertEquals("Organization/123", ref.getReference().getValue());
}
@ -297,7 +295,7 @@ public class JsonParserTest {
String str = p.encodeResourceToString(patient);
assertThat(str, IsNot.not(StringContains.containsString("managingOrganization")));
patient.setManagingOrganization(new ResourceReferenceDt(Organization.class, "123"));
patient.setManagingOrganization(new ResourceReferenceDt("Organization/123"));
str = p.encodeResourceToString(patient);
assertThat(str, StringContains.containsString("\"managingOrganization\":{\"resource\":\"Organization/123\"}"));
@ -529,9 +527,11 @@ public class JsonParserTest {
Bundle bundle = ourCtx.newJsonParser().parseBundle(bundleString);
BundleEntry entry = bundle.getEntries().get(0);
assertTrue(entry.isDeleted());
assertEquals("2012-05-29T23:45:32+00:00", entry.getDeletedAt().getValueAsString());
assertEquals("http://fhir.furore.com/fhir/Patient/1/_history/2", entry.getLinkSelf().getValue());
assertEquals("1", entry.getResource().getId().getUnqualifiedId());
assertEquals("2", entry.getResource().getId().getUnqualifiedVersionId());
assertEquals(new InstantDt("2012-05-29T23:45:32+00:00"), entry.getResource().getResourceMetadata().get(ResourceMetadataKeyEnum.DELETED_AT));
// Now encode

View File

@ -3,6 +3,7 @@ package ca.uhn.fhir.parser;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.eq;
@ -13,6 +14,8 @@ import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.io.IOUtils;
@ -20,6 +23,7 @@ import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.XMLUnit;
import org.hamcrest.core.IsNot;
import org.hamcrest.core.StringContains;
import org.hamcrest.text.StringContainsInOrder;
import org.junit.BeforeClass;
import org.junit.Test;
import org.xml.sax.SAXException;
@ -52,6 +56,7 @@ import ca.uhn.fhir.model.dstu.valueset.NarrativeStatusEnum;
import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.DecimalDt;
import ca.uhn.fhir.model.primitive.IdDt;
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;
@ -142,7 +147,7 @@ public class XmlParserTest {
Patient patient = new Patient();
patient.addAddress().setUse(AddressUseEnum.HOME);
patient.addUndeclaredExtension(false, "urn:foo", new ResourceReferenceDt(Organization.class, "123"));
patient.addUndeclaredExtension(false, "urn:foo", new ResourceReferenceDt("Organization/123"));
String val = parser.encodeResourceToString(patient);
ourLog.info(val);
@ -153,7 +158,7 @@ public class XmlParserTest {
List<ExtensionDt> ext = actual.getUndeclaredExtensionsByUrl("urn:foo");
assertEquals(1, ext.size());
ResourceReferenceDt ref = (ResourceReferenceDt) ext.get(0).getValue();
assertEquals("Organization/123", ref.getResourceUrl());
assertEquals("Organization/123", ref.getReference().getValue());
}
@ -163,7 +168,7 @@ public class XmlParserTest {
MyPatientWithOneDeclaredExtension patient = new MyPatientWithOneDeclaredExtension();
patient.addAddress().setUse(AddressUseEnum.HOME);
patient.setFoo(new ResourceReferenceDt(Organization.class, "123"));
patient.setFoo(new ResourceReferenceDt("Organization/123"));
String val = parser.encodeResourceToString(patient);
ourLog.info(val);
@ -172,7 +177,7 @@ public class XmlParserTest {
MyPatientWithOneDeclaredExtension actual = parser.parseResource(MyPatientWithOneDeclaredExtension.class, val);
assertEquals(AddressUseEnum.HOME, patient.getAddressFirstRep().getUse().getValueAsEnum());
ResourceReferenceDt ref = actual.getFoo();
assertEquals("Organization/123", ref.getResourceUrl());
assertEquals("Organization/123", ref.getReference().getValue());
}
@ -246,11 +251,41 @@ public class XmlParserTest {
assertEquals("term", b.getEntries().get(0).getCategories().get(0).getTerm());
assertEquals("label", b.getEntries().get(0).getCategories().get(0).getLabel());
assertEquals("scheme", b.getEntries().get(0).getCategories().get(0).getScheme());
assertNotNull(b.getEntries().get(0).getResource());
assertEquals(Patient.class, b.getEntries().get(0).getResource().getClass());
assertNull(b.getEntries().get(0).getResource());
}
@Test
public void testEncodeBundle() {
Bundle b= new Bundle();
Patient p1 = new Patient();
p1.addName().addFamily("Family1");
BundleEntry entry = b.addEntry();
entry.getId().setValue("1");
entry.setResource(p1);
Patient p2 = new Patient();
p2.addName().addFamily("Family2");
entry = b.addEntry();
entry.getId().setValue("2");
entry.setResource(p2);
BundleEntry deletedEntry = b.addEntry();
deletedEntry.setId(new IdDt("Patient/3"));
deletedEntry.setDeleted(InstantDt.withCurrentTime());
String bundleString = ourCtx.newXmlParser().setPrettyPrint(true).encodeBundleToString(b);
ourLog.info(bundleString);
List<String> strings = new ArrayList<String>();
strings.addAll(Arrays.asList("<entry>", "<id>1</id>", "</entry>"));
strings.addAll(Arrays.asList("<entry>", "<id>2</id>", "</entry>"));
strings.addAll(Arrays.asList("<at:deleted-entry", "ref=\"Patient/3", "/>"));
assertThat(bundleString, StringContainsInOrder.stringContainsInOrder(strings));
}
@Test
public void testEncodeContainedResources() {
@ -298,7 +333,7 @@ public class XmlParserTest {
String str = p.encodeResourceToString(patient);
assertThat(str, IsNot.not(StringContains.containsString("managingOrganization")));
patient.setManagingOrganization(new ResourceReferenceDt(Organization.class, "123"));
patient.setManagingOrganization(new ResourceReferenceDt("Organization/123"));
str = p.encodeResourceToString(patient);
assertThat(str, StringContains.containsString("<managingOrganization><reference value=\"Organization/123\"/></managingOrganization>"));
@ -362,7 +397,7 @@ public class XmlParserTest {
@Test
public void testLoadAndEncodeDeclaredExtensions() throws ConfigurationException, DataFormatException, SAXException, IOException {
IParser p = ourCtx.newXmlParser();
IParser p = new FhirContext(ResourceWithExtensionsA.class).newXmlParser();
//@formatter:off
String msg = "<ResourceWithExtensionsA xmlns=\"http://hl7.org/fhir\">\n" +
@ -662,13 +697,14 @@ public class XmlParserTest {
assertEquals("label", tl.get(0).getLabel());
assertEquals("http://foo", tl.get(0).getScheme());
assertEquals(new IdDt("256a5231-a2bb-49bd-9fea-f349d428b70d"), resource.getId());
assertEquals("256a5231-a2bb-49bd-9fea-f349d428b70d", resource.getId().getUnqualifiedId());
msg = msg.replace("<link href=\"http://hl7.org/implement/standards/fhir/valueset/256a5231-a2bb-49bd-9fea-f349d428b70d\" rel=\"self\"/>", "<link href=\"http://hl7.org/implement/standards/fhir/valueset/256a5231-a2bb-49bd-9fea-f349d428b70d/_history/12345\" rel=\"self\"/>");
entry = p.parseBundle(msg).getEntries().get(0);
resource = (ValueSet) entry.getResource();
assertEquals(new IdDt("256a5231-a2bb-49bd-9fea-f349d428b70d"), resource.getId());
assertEquals(new IdDt("12345"), resource.getResourceMetadata().get(ResourceMetadataKeyEnum.VERSION_ID));
assertEquals("256a5231-a2bb-49bd-9fea-f349d428b70d", resource.getId().getUnqualifiedId());
assertEquals("12345", resource.getId().getUnqualifiedVersionId());
assertEquals("12345", ((IdDt)resource.getResourceMetadata().get(ResourceMetadataKeyEnum.VERSION_ID)).getUnqualifiedVersionId());
}
@ -691,10 +727,13 @@ public class XmlParserTest {
Bundle bundle = p.parseBundle(msg);
BundleEntry entry = bundle.getEntries().get(0);
assertTrue(entry.isDeleted());
assertEquals("http://foo/Patient/1", entry.getId().getValue());
assertEquals("2013-02-10T04:11:24.435+00:00", entry.getDeletedAt().getValueAsString());
assertEquals("http://foo/Patient/1/_history/2", entry.getLinkSelf().getValue());
assertEquals("1", entry.getResource().getId().getUnqualifiedId());
assertEquals("2", entry.getResource().getId().getUnqualifiedVersionId());
assertEquals("2", ((IdDt)entry.getResource().getResourceMetadata().get(ResourceMetadataKeyEnum.VERSION_ID)).getUnqualifiedVersionId());
assertEquals(new InstantDt("2013-02-10T04:11:24.435+00:00"), entry.getResource().getResourceMetadata().get(ResourceMetadataKeyEnum.DELETED_AT));
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeBundleToString(bundle));
@ -714,7 +753,7 @@ public class XmlParserTest {
assertEquals("http://spark.furore.com/fhir/_snapshot?id=327d6bb9-83b0-4929-aa91-6dd9c41e587b&start=0&_count=20", bundle.getLinkSelf().getValue());
assertEquals("Patient resource with id 3216379", bundle.getEntries().get(0).getTitle().getValue());
assertEquals("http://spark.furore.com/fhir/Patient/3216379", bundle.getEntries().get(0).getId().getValue());
assertEquals("3216379", bundle.getEntries().get(0).getResource().getId().getValue());
assertEquals("3216379", bundle.getEntries().get(0).getResource().getId().getUnqualifiedId());
}

View File

@ -220,6 +220,7 @@ public class ResfulServerMethodTest {
status = ourClient.execute(httpGet);
responseContent = IOUtils.toString(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
ourLog.info(responseContent);
bundle = ourCtx.newJsonParser().parseBundle(responseContent);
entry0 = bundle.getEntries().get(0);
assertEquals("http://localhost:" + ourPort + "/Patient/1?_format=json", entry0.getLinkSelf().getValue());

View File

@ -235,13 +235,13 @@ public class TagsServerTest {
@AddTags(type = Patient.class)
public void addTagsPatient(@IdParam IdDt theId, @VersionIdParam IdDt theVersion, @TagListParam TagList theTagList) {
ourLastOutcome = "add" + theId.getValue() + theVersion.getValue();
ourLastOutcome = "add" + theId.getUnqualifiedId() + theVersion.getUnqualifiedVersionId();
ourLastTagList=theTagList;
}
@AddTags(type = Patient.class)
public void addTagsPatient(@IdParam IdDt theId, @TagListParam TagList theTagList) {
ourLastOutcome = "add" + theId.getValue();
ourLastOutcome = "add" + theId.getUnqualifiedId();
ourLastTagList=theTagList;
}
@ -264,7 +264,7 @@ public class TagsServerTest {
@GetTags(type = Patient.class)
public TagList getAllTagsPatientId(@IdParam IdDt theId) {
TagList tagList = new TagList();
tagList.add(new Tag("Patient" + theId.getValue(), "DogLabel", (String) null));
tagList.add(new Tag("Patient" + theId.getUnqualifiedId(), "DogLabel", (String) null));
tagList.add(new Tag("AllCat", "CatLabel", "http://cats"));
return tagList;
}
@ -272,20 +272,20 @@ public class TagsServerTest {
@GetTags(type = Patient.class)
public TagList getAllTagsPatientIdVersion(@IdParam IdDt theId, @VersionIdParam IdDt theVersion) {
TagList tagList = new TagList();
tagList.add(new Tag("Patient" + theId.getValue() + theVersion.getValue(), "DogLabel", (String) null));
tagList.add(new Tag("Patient" + theId.getUnqualifiedId() + theVersion.getUnqualifiedVersionId(), "DogLabel", (String) null));
tagList.add(new Tag("AllCat", "CatLabel", "http://cats"));
return tagList;
}
@DeleteTags(type = Patient.class)
public void RemoveTagsPatient(@IdParam IdDt theId, @VersionIdParam IdDt theVersion, @TagListParam TagList theTagList) {
ourLastOutcome = "Remove" + theId.getValue() + theVersion.getValue();
ourLastOutcome = "Remove" + theId.getUnqualifiedId() + theVersion.getUnqualifiedVersionId();
ourLastTagList=theTagList;
}
@DeleteTags(type = Patient.class)
public void RemoveTagsPatient(@IdParam IdDt theId, @TagListParam TagList theTagList) {
ourLastOutcome = "Remove" + theId.getValue();
ourLastOutcome = "Remove" + theId.getUnqualifiedId();
ourLastTagList=theTagList;
}

View File

@ -44,21 +44,26 @@ public class TransactionTest {
@Test
public void testTransaction() throws Exception {
Bundle b= new Bundle();
Bundle b = new Bundle();
Patient p1 = new Patient();
p1.getId().setValue("1");
b.addEntry().setResource(p1);
p1.addName().addFamily("Family1");
BundleEntry entry = b.addEntry();
entry.getId().setValue("1");
entry.setResource(p1);
Patient p2 = new Patient();
p2.getId().setValue("2");
b.addEntry().setResource(p2);
p2.addName().addFamily("Family2");
entry = b.addEntry();
entry.getId().setValue("2");
entry.setResource(p2);
BundleEntry deletedEntry = b.addEntry();
deletedEntry.setId(new IdDt("3"));
deletedEntry.setDeleted(new InstantDt());
deletedEntry.setId(new IdDt("Patient/3"));
deletedEntry.setDeleted(InstantDt.withCurrentTime());
String bundleString = ourCtx.newXmlParser().encodeBundleToString(b);
String bundleString = ourCtx.newXmlParser().setPrettyPrint(true).encodeBundleToString(b);
ourLog.info(bundleString);
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/");
httpPost.setEntity(new StringEntity(bundleString, ContentType.create(Constants.CT_ATOM_XML, "UTF-8")));

View File

@ -88,7 +88,7 @@ public class CompleteResourceProviderTest {
Patient p1 = new Patient();
p1.addIdentifier().setSystem("urn:system").setValue("testSearchByResourceChain01");
p1.addName().addFamily("testSearchByResourceChainFamily01").addGiven("testSearchByResourceChainGiven01");
p1.setManagingOrganization(new ResourceReferenceDt(Organization.class, o1id));
p1.setManagingOrganization(new ResourceReferenceDt("Organization/o1id"));
IdDt p1Id = ourClient.create(p1).getId();
//@formatter:off
@ -107,7 +107,7 @@ public class CompleteResourceProviderTest {
Patient p1 = new Patient();
p1.addIdentifier().setSystem("urn:system").setValue("testSearchByResourceChain01");
p1.addName().addFamily("testSearchByResourceChainFamily01").addGiven("testSearchByResourceChainGiven01");
p1.setManagingOrganization(new ResourceReferenceDt(Organization.class, "132312323"));
p1.setManagingOrganization(new ResourceReferenceDt("Organization/132312323"));
try {
ourClient.create(p1).getId();

View File

@ -90,7 +90,7 @@ public class TinderClientMojo extends AbstractMojo {
ProfileParser pp = new ProfileParser();
int index = 0;
for (RestResource nextResource : rest.getResource()) {
if (StringUtils.isBlank(nextResource.getProfile().getResourceUrl())) {
if (StringUtils.isBlank(nextResource.getProfile().getReference().getValue())) {
continue;
}
@ -99,7 +99,7 @@ public class TinderClientMojo extends AbstractMojo {
Profile profile;
try {
ourLog.info("Loading Profile: {}", nextResource.getProfile().getResourceUrl());
ourLog.info("Loading Profile: {}", nextResource.getProfile().getReference().getValue());
profile = (Profile) nextResource.getProfile().loadResource(client);
} catch (IOException e) {
throw new MojoFailureException("Failed to load resource profile: " + nextResource.getProfile().getReference().getValue(), e);
@ -107,7 +107,7 @@ public class TinderClientMojo extends AbstractMojo {
BaseRootType resourceModel;
try {
resourceModel = pp.parseSingleProfile(profile, nextResource.getProfile().getResourceUrl());
resourceModel = pp.parseSingleProfile(profile, nextResource.getProfile().getReference().getValue());
} catch (Exception e) {
throw new MojoFailureException("Failed to load resource profile: " + nextResource.getProfile().getReference().getValue(), e);
}

View File

@ -6,6 +6,7 @@ import java.math.BigDecimal;
import org.apache.commons.lang3.StringUtils;
import java.util.*;
import ca.uhn.fhir.model.api.*;
import ca.uhn.fhir.model.primitive.*;
import ca.uhn.fhir.model.api.annotation.*;
#foreach ( $import in $imports )
@ -223,42 +224,42 @@ public class ${className}
#end
#if ( ${className} == "ResourceReferenceDt" )
/**
* Constructor which creates a normal resource reference
*
* @param theResourceType The resource type
* @param theResourceId The resource ID
*/
public ResourceReferenceDt(Class<? extends IResource> theResourceType, String theResourceId) {
super(theResourceType, theResourceId);
}
/**
* Constructor which creates a normal resource reference
*
* @param theResourceType The resource type
* @param theResourceId The resource ID
*/
public ResourceReferenceDt(Class<? extends IResource> theResourceType, ca.uhn.fhir.model.primitive.IdDt theResourceId) {
super(theResourceType, theResourceId);
}
/**
* Constructor which creates a resource reference containing the actual
* resource in question.
* Constructor which creates a resource reference containing the actual resource in question.
* <p>
* <b>
* When using this in a server:</b> Generally if this is serialized, it will be serialized
* as a contained resource, so this should not be used if the intent is not to actually
* supply the referenced resource. This is not a hard-and-fast rule however, as the
* server can be configured to not serialized this resource, or to load an ID and contain
* even if this constructor is not used.
* <b> When using this in a server:</b> Generally if this is serialized, it will be serialized as a contained
* resource, so this should not be used if the intent is not to actually supply the referenced resource. This is not
* a hard-and-fast rule however, as the server can be configured to not serialized this resource, or to load an ID
* and contain even if this constructor is not used.
* </p>
*
* @param theResource The resource instance
* @param theResource
* The resource instance
*/
public ResourceReferenceDt(IResource theResource) {
super(theResource);
}
/**
* Constructor which accepts a reference directly (this can be an ID, a partial/relative URL or a complete/absolute
* URL)
*
* @param theId
* The reference itself
*/
public ResourceReferenceDt(String theId) {
setResourceId(new IdDt(theId));
}
/**
* Constructor which accepts a reference directly (this can be an ID, a partial/relative URL or a complete/absolute
* URL)
*
* @param theId
* The reference itself
*/
public ResourceReferenceDt(IdDt theResourceId) {
setResourceId(theResourceId);
}
#end
#childExtensionFields( $childExtensionTypes )