From 78d712de250394b19515d6b41b5534e3e2593973 Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Thu, 22 May 2014 18:38:29 -0400 Subject: [PATCH] Add transaction operation support, and refactor IdDt into smarter datatype with URL awareness --- hapi-fhir-base/src/changes/changes.xml | 3 + .../fhir/model/api/BaseResourceReference.java | 69 +++--- .../ca/uhn/fhir/model/api/BundleEntry.java | 17 +- .../model/api/ResourceMetadataKeyEnum.java | 17 +- .../dstu/composite/ResourceReferenceDt.java | 219 ++++++++---------- .../ca/uhn/fhir/model/primitive/IdDt.java | 95 ++++++-- .../java/ca/uhn/fhir/parser/JsonParser.java | 17 +- .../java/ca/uhn/fhir/parser/ParserState.java | 140 ++++++----- .../java/ca/uhn/fhir/parser/XmlParser.java | 12 +- .../ca/uhn/fhir/rest/method/MethodUtil.java | 25 ++ .../fhir/rest/method/ReadMethodBinding.java | 6 +- .../rest/method/TransactionMethodBinding.java | 20 ++ .../fhir/rest/param/TransactionParameter.java | 12 +- .../uhn/fhir/rest/server/RestfulServer.java | 12 +- .../RestfulPatientResourceProviderMore.java | 28 +++ .../src/site/xdoc/doc_rest_operations.xml | 24 +- .../composite/ResourceReferenceDtTest.java | 71 ------ .../ca/uhn/fhir/model/primitive/IdDtTest.java | 130 +++++++++++ .../ca/uhn/fhir/parser/JsonParserTest.java | 24 +- .../ca/uhn/fhir/parser/XmlParserTest.java | 65 ++++-- .../rest/server/ResfulServerMethodTest.java | 1 + .../uhn/fhir/rest/server/TagsServerTest.java | 12 +- .../uhn/fhir/rest/server/TransactionTest.java | 21 +- .../test/CompleteResourceProviderTest.java | 4 +- .../ca/uhn/fhir/tinder/TinderClientMojo.java | 6 +- .../src/main/resources/vm/dt_composite.vm | 59 ++--- 26 files changed, 669 insertions(+), 440 deletions(-) create mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java create mode 100644 hapi-fhir-base/src/test/java/ca/uhn/fhir/model/primitive/IdDtTest.java diff --git a/hapi-fhir-base/src/changes/changes.xml b/hapi-fhir-base/src/changes/changes.xml index 2987d6f068f..1dcb88ba90d 100644 --- a/hapi-fhir-base/src/changes/changes.xml +++ b/hapi-fhir-base/src/changes/changes.xml @@ -21,6 +21,9 @@ Parsers (XML/JSON) now support deleted entries in bundles + + Transaction method now supported in servers + diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BaseResourceReference.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BaseResourceReference.java index 8295ea113f8..ef8c5ad7509 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BaseResourceReference.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BaseResourceReference.java @@ -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 myResourceType; /** * Constructor - */ + * @param theResource + */ public BaseResourceReference() { // nothing } - - /** - * Constructor - */ - public BaseResourceReference(Class theResourceType, IdDt theResourceId) { - myResourceType = theResourceType; - myResourceId = theResourceId.getValue(); - } /** * Constructor + * + * @param theResource The loaded resource itself */ - public BaseResourceReference(Class 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 *

*/ - public abstract StringDt getReference(); + public abstract IdDt getResourceId(); + + /** + * Sets the resource ID + * + *

+ * Definition: 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 + *

+ */ + public abstract void setResourceId(IdDt theResourceId); /** * Gets the actual loaded and parsed resource instance, if it is already present. This @@ -94,21 +92,10 @@ public abstract class BaseResourceReference extends BaseElement { return myResource; } - public String getResourceId() { - return myResourceId; - } - - public Class 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 theResourceType) { - myResourceType = theResourceType; - } - } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BundleEntry.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BundleEntry.java index 7cdfcdb7cb6..316e426b7e6 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BundleEntry.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BundleEntry.java @@ -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; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ResourceMetadataKeyEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ResourceMetadataKeyEnum.java index 0de503b1df7..5d68955d42f 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ResourceMetadataKeyEnum.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ResourceMetadataKeyEnum.java @@ -70,7 +70,22 @@ public enum ResourceMetadataKeyEnum { *

* Values for this key are of type {@link IdDt} *

+ * + * @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. + *

+ * Values for this key are of type {@link InstantDt} + *

+ */ + DELETED_AT; } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/dstu/composite/ResourceReferenceDt.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/dstu/composite/ResourceReferenceDt.java index fe8572188bb..a059d550237 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/dstu/composite/ResourceReferenceDt.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/dstu/composite/ResourceReferenceDt.java @@ -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 ResourceReferenceDt Datatype - * (A reference from one resource to another) - * + * HAPI/FHIR ResourceReferenceDt Datatype (A reference from one resource to another) + * *

- * Definition: - * A reference from one resource to another - *

- * + * Definition: A reference from one resource to another + *

+ * *

* Requirements: * - *

+ *

*/ -@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 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 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. *

- * - * When using this in a server: 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. + * When using this in a server: 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. *

* - * @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 List getAllPopulatedChildElementsOfType(Class theType) { return ca.uhn.fhir.util.ElementUtil.allPopulatedChildElements(theType, myReference, myDisplay); } /** - * Gets the value(s) for reference (Relative, internal or absolute URL reference). - * creating it if it does - * not exist. Will not return null. - * - *

- * Definition: - * 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 - *

+ * Gets the value(s) for reference (Relative, internal or absolute URL reference). creating it if it does not + * exist. Will not return null. + * + *

+ * Definition: 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 + *

*/ - public StringDt getReference() { + public StringDt getReference() { if (myReference == null) { myReference = new StringDt(); } @@ -155,41 +131,44 @@ public class ResourceReferenceDt /** * Sets the value(s) for reference (Relative, internal or absolute URL reference) - * - *

- * Definition: - * 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 - *

+ * + *

+ * Definition: 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 + *

*/ public ResourceReferenceDt setReference(StringDt theValue) { myReference = theValue; return this; } - /** + /** * Sets the value for reference (Relative, internal or absolute URL reference) - * - *

- * Definition: - * 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 - *

+ * + *

+ * Definition: 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 + *

*/ - 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 display (Text alternative for the resource). - * creating it if it does - * not exist. Will not return null. - * - *

- * Definition: - * Plain text narrative that identifies the resource in addition to the resource reference - *

+ * Gets the value(s) for display (Text alternative for the resource). creating it if it does not exist. Will + * not return null. + * + *

+ * Definition: Plain text narrative that identifies the resource in addition to the resource reference + *

*/ - public StringDt getDisplay() { + public StringDt getDisplay() { if (myDisplay == null) { myDisplay = new StringDt(); } @@ -198,32 +177,36 @@ public class ResourceReferenceDt /** * Sets the value(s) for display (Text alternative for the resource) - * - *

- * Definition: - * Plain text narrative that identifies the resource in addition to the resource reference - *

+ * + *

+ * Definition: Plain text narrative that identifies the resource in addition to the resource reference + *

*/ public ResourceReferenceDt setDisplay(StringDt theValue) { myDisplay = theValue; return this; } - /** + /** * Sets the value for display (Text alternative for the resource) - * - *

- * Definition: - * Plain text narrative that identifies the resource in addition to the resource reference - *

+ * + *

+ * Definition: Plain text narrative that identifies the resource in addition to the resource reference + *

*/ - 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()); + } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/IdDt.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/IdDt.java index 2a135de882b..120ee539f62 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/IdDt.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/IdDt.java @@ -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. * *

- * Description: 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. + * Description: 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. *

*

* regex: [a-z0-9\-\.]{1,36} @@ -41,7 +44,10 @@ import ca.uhn.fhir.parser.DataFormatException; @DatatypeDef(name = "id") public class IdDt extends BasePrimitive { + 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 { } /** - * 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 { } /** - * 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). * *

- * Description: 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. + * Description: 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. *

*

* regex: [a-z0-9\-\.]{1,36} @@ -85,12 +92,8 @@ public class IdDt extends BasePrimitive { setValue(theValue); } - - - - /** - * Returns the value of this ID as a big decimal, or null if the value is null + * Returns the unqualified portion of this ID as a big decimal, or null if the value is null * * @throws NumberFormatException * If the value is not a valid BigDecimal @@ -103,7 +106,7 @@ public class IdDt extends BasePrimitive { } /** - * Returns the value of this ID as a {@link Long}, or null if the value is null + * Returns the unqualified portion of this ID as a {@link Long}, or null if the value is null * * @throws NumberFormatException * If the value is not a valid Long @@ -114,15 +117,34 @@ public class IdDt extends BasePrimitive { } return Long.parseLong(getValueAsString()); } - + /** - * Returns a reference to this 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 this 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 { } /** - * Copies the value from the given IdDt to this 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 this 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 { * Set the value * *

- * Description: 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. + * Description: 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. *

*

* regex: [a-z0-9\-\.]{1,36} @@ -156,14 +179,44 @@ public class IdDt extends BasePrimitive { 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 * *

- * Description: 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. + * Description: 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. *

*

* regex: [a-z0-9\-\.]{1,36} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java index b2368ed5572..d463790697e 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java @@ -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"); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java index 67caec04eea..90a29319dd4 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java @@ -120,9 +120,8 @@ class ParserState { } /** - * 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 { 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 { } + 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 myResourceType; + private boolean myDeleted; public AtomEntryState(Bundle theInstance, Class theResourceType) { super(null); @@ -281,6 +302,10 @@ class ParserState { public void endingElement() throws DataFormatException { populateResourceMetadata(); pop(); + + if (myDeleted) { + putPlacerResourceInDeletedEntry(myEntry); + } } @Override @@ -305,7 +330,7 @@ class ParserState { 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 { // TODO: handle category } + @SuppressWarnings("deprecation") private void populateResourceMetadata() { if (myEntry.getResource() == null) { return; @@ -335,59 +361,21 @@ class ParserState { } 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 theResourceType) { super(theInstance, theResourceType); - - getEntry().setDeleted(true); } @Override @@ -399,9 +387,13 @@ class ParserState { } } - + @Override + public void endingElement() throws DataFormatException { + putPlacerResourceInDeletedEntry(getEntry()); + super.endingElement(); + } + } - private class AtomLinkState extends BaseState { @@ -506,7 +498,6 @@ class ParserState { } - private class AtomDeletedJsonWhenState extends BaseState { private String myData; @@ -529,7 +520,6 @@ class ParserState { 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 { } } - + private class AtomState extends BaseState { private Bundle myInstance; @@ -577,7 +567,7 @@ class ParserState { } 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 { 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 { 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; } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/XmlParser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/XmlParser.java index 65d06b9b739..a1c007d217e 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/XmlParser.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/XmlParser.java @@ -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"); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java new file mode 100644 index 00000000000..84999853c2a --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java @@ -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 { + +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ReadMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ReadMethodBinding.java index 94105e4011d..af150ef2d4a 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ReadMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ReadMethodBinding.java @@ -110,7 +110,7 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding { public List 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 diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/TransactionMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/TransactionMethodBinding.java index fb66cf76ae6..f54790ba01e 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/TransactionMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/TransactionMethodBinding.java @@ -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; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/TransactionParameter.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/TransactionParameter.java index 7a119891232..0522041282a 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/TransactionParameter.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/TransactionParameter.java @@ -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 retVal = new ArrayList(); + for (BundleEntry next : resource.getEntries()) { + if (next.getResource() != null) { + retVal.add(next.getResource()); + } + } + + return retVal; } @Override diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java index 154dac73b2f..58c95bb2023 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java @@ -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); } } diff --git a/hapi-fhir-base/src/site/example/java/example/RestfulPatientResourceProviderMore.java b/hapi-fhir-base/src/site/example/java/example/RestfulPatientResourceProviderMore.java index 094fada9769..0406caddec3 100644 --- a/hapi-fhir-base/src/site/example/java/example/RestfulPatientResourceProviderMore.java +++ b/hapi-fhir-base/src/site/example/java/example/RestfulPatientResourceProviderMore.java @@ -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 transaction(@TransactionParam List 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 retVal = new ArrayList(); + /* + * 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 + } diff --git a/hapi-fhir-base/src/site/xdoc/doc_rest_operations.xml b/hapi-fhir-base/src/site/xdoc/doc_rest_operations.xml index 74a90885f67..bfe66cbff18 100644 --- a/hapi-fhir-base/src/site/xdoc/doc_rest_operations.xml +++ b/hapi-fhir-base/src/site/xdoc/doc_rest_operations.xml @@ -1090,9 +1090,31 @@

- Not yet implemented - Get in touch if you would like to help! + The + transaction + 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.

+

+ 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 transaction method. +

+ + + + + +

+ Example URL to invoke this method: +
+ POST http://fhir.example.com/
+ (note that the content of this POST will be a bundle) +

+ +
diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/model/dstu/composite/ResourceReferenceDtTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/model/dstu/composite/ResourceReferenceDtTest.java index be7876c3614..c9aab329f40 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/model/dstu/composite/ResourceReferenceDtTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/model/dstu/composite/ResourceReferenceDtTest.java @@ -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() { diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/model/primitive/IdDtTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/model/primitive/IdDtTest.java new file mode 100644 index 00000000000..6c79b1408ae --- /dev/null +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/model/primitive/IdDtTest.java @@ -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(); + } + +} diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/parser/JsonParserTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/parser/JsonParserTest.java index 4216e637059..3e42e9079d6 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/parser/JsonParserTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/parser/JsonParserTest.java @@ -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 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 diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java index 967428c84bf..4120e1533ef 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java @@ -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 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 strings = new ArrayList(); + strings.addAll(Arrays.asList("", "1", "")); + strings.addAll(Arrays.asList("", "2", "")); + strings.addAll(Arrays.asList("")); + 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("")); @@ -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 = "\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("", ""); 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()); } diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/ResfulServerMethodTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/ResfulServerMethodTest.java index 508b2903c74..1ce487e6639 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/ResfulServerMethodTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/ResfulServerMethodTest.java @@ -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()); diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/TagsServerTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/TagsServerTest.java index 1e794171aa6..bed449f602a 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/TagsServerTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/TagsServerTest.java @@ -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; } diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/TransactionTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/TransactionTest.java index 96a24c741a8..19ad1400c5c 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/TransactionTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/TransactionTest.java @@ -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"))); diff --git a/hapi-fhir-jpaserver-test/src/test/java/ca/uhn/fhir/jpa/test/CompleteResourceProviderTest.java b/hapi-fhir-jpaserver-test/src/test/java/ca/uhn/fhir/jpa/test/CompleteResourceProviderTest.java index 66e0ed4be37..f65a46fd1d6 100644 --- a/hapi-fhir-jpaserver-test/src/test/java/ca/uhn/fhir/jpa/test/CompleteResourceProviderTest.java +++ b/hapi-fhir-jpaserver-test/src/test/java/ca/uhn/fhir/jpa/test/CompleteResourceProviderTest.java @@ -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(); diff --git a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/TinderClientMojo.java b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/TinderClientMojo.java index 68d77eff33d..082a83279b7 100644 --- a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/TinderClientMojo.java +++ b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/TinderClientMojo.java @@ -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); } diff --git a/hapi-tinder-plugin/src/main/resources/vm/dt_composite.vm b/hapi-tinder-plugin/src/main/resources/vm/dt_composite.vm index a9757b72d17..3a4842312f5 100644 --- a/hapi-tinder-plugin/src/main/resources/vm/dt_composite.vm +++ b/hapi-tinder-plugin/src/main/resources/vm/dt_composite.vm @@ -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 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 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. *

- * - * When using this in a server: 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. + * When using this in a server: 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. *

* - * @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 )