From 344e324929ce94fa13f288735ae38ca606e69c18 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Tue, 24 Jan 2017 10:47:24 -0500 Subject: [PATCH] Fix #544 Squashed commit of the following: commit 1ab9b58e9f3380cd6de3379b161e669f17cf4854 Author: James Agnew Date: Tue Jan 24 10:46:56 2017 -0500 Changes working now commit 5c752cdacee20711e7edef8aeb3217c94f72b9c5 Author: James Date: Tue Jan 24 08:43:04 2017 -0500 Get JPA working with new uuid: logic commit 127b76884a153684b2873af36683c721ac6784b1 Author: James Agnew Date: Mon Jan 23 21:11:18 2017 -0500 Fix test commit 599bbe236c81fc050f421bced67578fdcafb9371 Author: James Agnew Date: Mon Jan 23 18:42:46 2017 -0500 Work on #544 --- .../ca/uhn/fhir/model/primitive/IdDt.java | 165 +++++---- .../java/ca/uhn/fhir/parser/JsonParser.java | 2 +- .../java/ca/uhn/fhir/parser/ParserState.java | 25 +- .../java/ca/uhn/fhir/parser/XmlParser.java | 2 +- .../hl7/fhir/instance/model/api/IIdType.java | 31 ++ .../uhn/fhir/jpa/dao/FhirSystemDaoDstu1.java | 4 +- .../uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java | 7 +- .../jpa/dao/dstu3/FhirSystemDaoDstu3.java | 6 +- .../jpa/dao/dstu2/FhirSystemDaoDstu2Test.java | 4 - .../jpa/dao/dstu3/FhirSystemDaoDstu3Test.java | 133 ++++---- .../ca/uhn/fhir/model/primitive/IdDtTest.java | 81 ++++- .../ca/uhn/fhir/parser/XmlParserTest.java | 4 +- .../hl7/fhir/dstu2016may/model/IdType.java | 82 +++-- .../ca/uhn/fhir/model/IdTypeDstu2_1Test.java | 84 ++++- .../fhir/parser/JsonParserDstu2_1Test.java | 62 +--- .../uhn/fhir/parser/JsonParserDstu2Test.java | 47 ++- .../java/org/hl7/fhir/dstu3/model/IdType.java | 78 +++-- .../ca/uhn/fhir/model/IdTypeDstu3Test.java | 83 ++++- .../uhn/fhir/parser/JsonParserDstu3Test.java | 71 ++-- .../uhn/fhir/parser/XmlParserDstu3Test.java | 316 ++++++++++-------- .../test/resources/bundle_ref_by_uuid_544.xml | 174 ++++++++++ src/changes/changes.xml | 11 + 22 files changed, 953 insertions(+), 519 deletions(-) create mode 100644 hapi-fhir-structures-dstu3/src/test/resources/bundle_ref_by_uuid_544.xml 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 a942f74e5e6..6c888f6b92e 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 @@ -1,5 +1,6 @@ package ca.uhn.fhir.model.primitive; +import static org.apache.commons.lang3.StringUtils.defaultString; /* * #%L * HAPI FHIR - Core Library @@ -10,7 +11,7 @@ package ca.uhn.fhir.model.primitive; * 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 + * 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, @@ -52,7 +53,7 @@ import ca.uhn.fhir.util.UrlUtil; * regex: [a-z-Z0-9\-\.]{1,36} *

*/ -@DatatypeDef(name = "id", profileOf=StringDt.class) +@DatatypeDef(name = "id", profileOf = StringDt.class) public class IdDt extends UriDt implements IPrimitiveDatatype, IIdType { private String myBaseUrl; @@ -106,9 +107,9 @@ public class IdDt extends UriDt implements IPrimitiveDatatype, IIdType { * Constructor * * @param theResourceType - * The resource type (e.g. "Patient") + * The resource type (e.g. "Patient") * @param theIdPart - * The ID (e.g. "123") + * The ID (e.g. "123") */ public IdDt(String theResourceType, BigDecimal theIdPart) { this(theResourceType, toPlainStringWithNpeThrowIfNeeded(theIdPart)); @@ -118,9 +119,9 @@ public class IdDt extends UriDt implements IPrimitiveDatatype, IIdType { * Constructor * * @param theResourceType - * The resource type (e.g. "Patient") + * The resource type (e.g. "Patient") * @param theIdPart - * The ID (e.g. "123") + * The ID (e.g. "123") */ public IdDt(String theResourceType, Long theIdPart) { this(theResourceType, toPlainStringWithNpeThrowIfNeeded(theIdPart)); @@ -130,9 +131,9 @@ public class IdDt extends UriDt implements IPrimitiveDatatype, IIdType { * Constructor * * @param theResourceType - * The resource type (e.g. "Patient") + * The resource type (e.g. "Patient") * @param theId - * The ID (e.g. "123") + * The ID (e.g. "123") */ public IdDt(String theResourceType, String theId) { this(theResourceType, theId, null); @@ -142,11 +143,11 @@ public class IdDt extends UriDt implements IPrimitiveDatatype, IIdType { * Constructor * * @param theResourceType - * The resource type (e.g. "Patient") + * The resource type (e.g. "Patient") * @param theId - * The ID (e.g. "123") + * The ID (e.g. "123") * @param theVersionId - * The version ID ("e.g. "456") + * The version ID ("e.g. "456") */ public IdDt(String theResourceType, String theId, String theVersionId) { this(null, theResourceType, theId, theVersionId); @@ -156,13 +157,13 @@ public class IdDt extends UriDt implements IPrimitiveDatatype, IIdType { * Constructor * * @param theBaseUrl - * The server base URL (e.g. "http://example.com/fhir") + * The server base URL (e.g. "http://example.com/fhir") * @param theResourceType - * The resource type (e.g. "Patient") + * The resource type (e.g. "Patient") * @param theId - * The ID (e.g. "123") + * The ID (e.g. "123") * @param theVersionId - * The version ID ("e.g. "456") + * The version ID ("e.g. "456") */ public IdDt(String theBaseUrl, String theResourceType, String theId, String theVersionId) { myBaseUrl = theBaseUrl; @@ -202,34 +203,6 @@ public class IdDt extends UriDt implements IPrimitiveDatatype, IIdType { return getIdPartAsBigDecimal(); } - private String determineLocalPrefix(String theValue) { - if (theValue == null || theValue.isEmpty()) { - return null; - } - if (theValue.startsWith("#")) { - return "#"; - } - int lastPrefix = -1; - for (int i = 0; i < theValue.length(); i++) { - char nextChar = theValue.charAt(i); - if (nextChar == ':') { - lastPrefix = i; - } else if (!Character.isLetter(nextChar) || !Character.isLowerCase(nextChar)) { - break; - } - } - if (lastPrefix != -1) { - String candidate = theValue.substring(0, lastPrefix + 1); - if (candidate.startsWith("cid:") || candidate.startsWith("urn:")) { - return candidate; - } else { - return null; - } - } else { - return null; - } - } - @Override public boolean equals(Object theArg0) { if (!(theArg0 instanceof IdDt)) { @@ -277,7 +250,7 @@ public class IdDt extends UriDt implements IPrimitiveDatatype, IIdType { * 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 + * If the value is not a valid BigDecimal */ public BigDecimal getIdPartAsBigDecimal() { String val = getIdPart(); @@ -291,7 +264,7 @@ public class IdDt extends UriDt implements IPrimitiveDatatype, IIdType { * 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 + * If the value is not a valid Long */ @Override public Long getIdPartAsLong() { @@ -315,11 +288,11 @@ public class IdDt extends UriDt implements IPrimitiveDatatype, IIdType { @Override public String getValue() { if (super.getValue() == null && myHaveComponentParts) { - - if (determineLocalPrefix(myBaseUrl) != null && myResourceType == null && myUnqualifiedVersionId == null) { - return myBaseUrl + myUnqualifiedId; + + if (isLocal() || isUrn()) { + return myUnqualifiedId; } - + StringBuilder b = new StringBuilder(); if (isNotBlank(myBaseUrl)) { b.append(myBaseUrl); @@ -448,18 +421,23 @@ public class IdDt extends UriDt implements IPrimitiveDatatype, IIdType { } /** - * Returns true if the ID is a local reference (in other words, it begins with the '#' character) + * Returns true if the ID is a local reference (in other words, + * it begins with the '#' character) */ @Override public boolean isLocal() { - return "#".equals(myBaseUrl); + return defaultString(myUnqualifiedId).startsWith("#"); + } + + private boolean isUrn() { + return defaultString(myUnqualifiedId).startsWith("urn:"); } @Override public boolean isVersionIdPartValidLong() { return isValidLong(getVersionIdPart()); } - + /** * 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. */ @@ -468,6 +446,27 @@ public class IdDt extends UriDt implements IPrimitiveDatatype, IIdType { setValue(theId.getValue()); } + @Override + public IIdType setParts(String theBaseUrl, String theResourceType, String theIdPart, String theVersionIdPart) { + if (isNotBlank(theVersionIdPart)) { + Validate.notBlank(theResourceType, "If theVersionIdPart is populated, theResourceType and theIdPart must be populated"); + Validate.notBlank(theIdPart, "If theVersionIdPart is populated, theResourceType and theIdPart must be populated"); + } + if (isNotBlank(theBaseUrl) && isNotBlank(theIdPart)) { + Validate.notBlank(theResourceType, "If theBaseUrl is populated and theIdPart is populated, theResourceType must be populated"); + } + + setValue(null); + + myBaseUrl = theBaseUrl; + myResourceType = theResourceType; + myUnqualifiedId = theIdPart; + myUnqualifiedVersionId = StringUtils.defaultIfBlank(theVersionIdPart, null); + myHaveComponentParts = true; + + return this; + } + /** * Set the value * @@ -484,9 +483,7 @@ public class IdDt extends UriDt implements IPrimitiveDatatype, IIdType { // TODO: add validation super.setValue(theValue); myHaveComponentParts = false; - - String localPrefix = determineLocalPrefix(theValue); - + if (StringUtils.isBlank(theValue)) { myBaseUrl = null; super.setValue(null); @@ -495,14 +492,14 @@ public class IdDt extends UriDt implements IPrimitiveDatatype, IIdType { myResourceType = null; } else if (theValue.charAt(0) == '#' && theValue.length() > 1) { super.setValue(theValue); - myBaseUrl = "#"; - myUnqualifiedId = theValue.substring(1); + myBaseUrl = null; + myUnqualifiedId = theValue; myUnqualifiedVersionId = null; myResourceType = null; myHaveComponentParts = true; - } else if (localPrefix != null) { - myBaseUrl = localPrefix; - myUnqualifiedId = theValue.substring(localPrefix.length()); + } else if (theValue.startsWith("urn:")) { + myBaseUrl = null; + myUnqualifiedId = theValue; myUnqualifiedVersionId = null; myResourceType = null; myHaveComponentParts = true; @@ -567,24 +564,33 @@ public class IdDt extends UriDt implements IPrimitiveDatatype, IIdType { */ @Override public IdDt toUnqualified() { + if (isLocal() || isUrn()) { + return new IdDt(getValueAsString()); + } return new IdDt(getResourceType(), getIdPart(), getVersionIdPart()); } @Override public IdDt toUnqualifiedVersionless() { - if (isLocal()) { - return toVersionless(); + if (isLocal() || isUrn()) { + return new IdDt(getValueAsString()); } return new IdDt(getResourceType(), getIdPart()); } @Override public IdDt toVersionless() { + if (isLocal() || isUrn()) { + return new IdDt(getValueAsString()); + } return new IdDt(getBaseUrl(), getResourceType(), getIdPart(), null); } @Override public IdDt withResourceType(String theResourceName) { + if (isLocal() || isUrn()) { + return new IdDt(getValueAsString()); + } return new IdDt(theResourceName, getIdPart(), getVersionIdPart()); } @@ -593,13 +599,16 @@ public class IdDt extends UriDt implements IPrimitiveDatatype, IIdType { * because IdDt can contain either a complete URL or a partial one (or even jut a simple ID), this method may be used to translate into a complete URL. * * @param theServerBase - * The server base (e.g. "http://example.com/fhir") + * The server base (e.g. "http://example.com/fhir") * @param theResourceType - * The resource name (e.g. "Patient") + * The resource name (e.g. "Patient") * @return A fully qualified URL for this ID (e.g. "http://example.com/fhir/Patient/1") */ @Override public IdDt withServerBase(String theServerBase, String theResourceType) { + if (isLocal() || isUrn()) { + return new IdDt(getValueAsString()); + } return new IdDt(theServerBase, theResourceType, getIdPart(), getVersionIdPart()); } @@ -607,12 +616,17 @@ public class IdDt extends UriDt implements IPrimitiveDatatype, IIdType { * Creates a new instance of this ID which is identical, but refers to the specific version of this resource ID noted by theVersion. * * @param theVersion - * The actual version string, e.g. "1" + * The actual version string, e.g. "1" * @return A new instance of IdDt which is identical, but refers to the specific version of this resource ID noted by theVersion. */ + @Override public IdDt withVersion(String theVersion) { Validate.notBlank(theVersion, "Version may not be null or empty"); + if (isLocal() || isUrn()) { + return new IdDt(getValueAsString()); + } + String existingValue = getValue(); int i = existingValue.indexOf(Constants.PARAM_HISTORY); @@ -678,25 +692,4 @@ public class IdDt extends UriDt implements IPrimitiveDatatype, IIdType { return theIdPart.toString(); } - @Override - public IIdType setParts(String theBaseUrl, String theResourceType, String theIdPart, String theVersionIdPart) { - if (isNotBlank(theVersionIdPart)) { - Validate.notBlank(theResourceType, "If theVersionIdPart is populated, theResourceType and theIdPart must be populated"); - Validate.notBlank(theIdPart, "If theVersionIdPart is populated, theResourceType and theIdPart must be populated"); - } - if (isNotBlank(theBaseUrl) && isNotBlank(theIdPart)) { - Validate.notBlank(theResourceType, "If theBaseUrl is populated and theIdPart is populated, theResourceType must be populated"); - } - - setValue(null); - - myBaseUrl = theBaseUrl; - myResourceType = theResourceType; - myUnqualifiedId = theIdPart; - myUnqualifiedVersionId = StringUtils.defaultIfBlank(theVersionIdPart, null); - myHaveComponentParts = true; - - return this; - } - } 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 6f8868c4f02..782907c977d 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 @@ -347,7 +347,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser { for (BundleEntry nextEntry : theBundle.getEntries()) { theEventWriter.beginObject(); - if (nextEntry.getResource() != null && nextEntry.getResource().getId().getBaseUrl() != null) { + if (nextEntry.getResource() != null && isNotBlank(nextEntry.getResource().getIdElement().getValue()) && (nextEntry.getResource().getId().getBaseUrl() != null || nextEntry.getResource().getId().getValueAsString().startsWith("urn:"))) { writeOptionalTagWithTextNode(theEventWriter, "fullUrl", nextEntry.getResource().getId().getValue()); } 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 6fc5a4b9736..9750f68f511 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 @@ -721,7 +721,6 @@ class ParserState { myInstance = theInstance; } - @SuppressWarnings("unchecked") @Override public void wereBack() { @@ -2090,17 +2089,32 @@ class ParserState { final boolean bundle = "Bundle".equals(myContext.getResourceDefinition(myInstance).getName()); if (bundle) { + FhirTerser t = myContext.newTerser(); + + Map idToResource = new HashMap(); + if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) { + List entries = t.getValues(myInstance, "Bundle.entry", IBase.class); + for (IBase nextEntry : entries) { + IPrimitiveType fullUrl = t.getSingleValueOrNull(nextEntry, "fullUrl", IPrimitiveType.class); + if (fullUrl != null && isNotBlank(fullUrl.getValueAsString())) { + IBaseResource resource = t.getSingleValueOrNull(nextEntry, "resource", IBaseResource.class); + if (resource != null) { + idToResource.put(fullUrl.getValueAsString(), resource); + } + } + } + } + /* * Stitch together resource references */ - Map idToResource = new HashMap(); - FhirTerser t = myContext.newTerser(); List resources = t.getAllPopulatedChildElementsOfType(myInstance, IBaseResource.class); for (IBaseResource next : resources) { IIdType id = next.getIdElement(); if (id != null && id.isEmpty() == false) { String resName = myContext.getResourceDefinition(next).getName(); - idToResource.put(id.withResourceType(resName).toUnqualifiedVersionless(), next); + IIdType idType = id.withResourceType(resName).toUnqualifiedVersionless(); + idToResource.put(idType.getValueAsString(), next); } } @@ -2108,7 +2122,8 @@ class ParserState { List refs = myContext.newTerser().getAllPopulatedChildElementsOfType(next, IBaseReference.class); for (IBaseReference nextRef : refs) { if (nextRef.isEmpty() == false && nextRef.getReferenceElement() != null) { - IBaseResource target = idToResource.get(nextRef.getReferenceElement().toUnqualifiedVersionless()); + IIdType unqualifiedVersionless = nextRef.getReferenceElement().toUnqualifiedVersionless(); + IBaseResource target = idToResource.get(unqualifiedVersionless.getValueAsString()); if (target != null) { nextRef.setResource(target); } 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 a0fbff92853..19f402c0125 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 @@ -446,7 +446,7 @@ public class XmlParser extends BaseParser implements IParser { writeBundleResourceLink(theEventWriter, "alternate", nextEntry.getLinkAlternate()); - if (nextEntry.getResource() != null && nextEntry.getResource().getId().getBaseUrl() != null) { + if (nextEntry.getResource() != null && isNotBlank(nextEntry.getResource().getIdElement().getValue()) && (nextEntry.getResource().getId().getBaseUrl() != null || nextEntry.getResource().getId().getValueAsString().startsWith("urn:"))) { writeOptionalTagWithValue(theEventWriter, "fullUrl", nextEntry.getResource().getId().getValue()); } diff --git a/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IIdType.java b/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IIdType.java index 9d370db1fbe..119ddb66e4e 100644 --- a/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IIdType.java +++ b/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IIdType.java @@ -130,10 +130,41 @@ public interface IIdType extends IPrimitiveType { IIdType toVersionless(); + /** + * Returns a copy of this object, but with a different {@link #getResourceType() resource type} + * (or if this object does not have a resource type currently, returns a copy of this object with + * the given resource type). + *

+ * Note that if this object represents a local reference (e.g. #foo) or + * a URN (e.g. urn:oid:1.2.3.4) this method will simply return a copy + * of this object with no modifications. + *

+ */ IIdType withResourceType(String theResName); + /** + * Returns a copy of this object, but with a different {@link #getResourceType() resource type} + * and {@link #getBaseUrl() base URL} + * (or if this object does not have a resource type currently, returns a copy of this object with + * the given server base and resource type). + *

+ * Note that if this object represents a local reference (e.g. #foo) or + * a URN (e.g. urn:oid:1.2.3.4) this method will simply return a copy + * of this object with no modifications. + *

+ */ IIdType withServerBase(String theServerBase, String theResourceName); + /** + * Returns a copy of this object, but with a different {@link #getVersionIdPart() version ID} + * (or if this object does not have a resource type currently, returns a copy of this object with + * the given version). + *

+ * Note that if this object represents a local reference (e.g. #foo) or + * a URN (e.g. urn:oid:1.2.3.4) this method will simply return a copy + * of this object with no modifications. + *

+ */ IIdType withVersion(String theVersion); /** diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu1.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu1.java index 5dc04523221..1b34f2312b0 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu1.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu1.java @@ -164,7 +164,7 @@ public class FhirSystemDaoDstu1 extends BaseHapiFhirSystemDao, M entity = toEntity(nextResource); entity.setUpdated(updateTime); entity.setPublished(updateTime); - if (nextId.isEmpty() == false && "cid:".equals(nextId.getBaseUrl())) { + if (nextId.getIdPart() != null && nextId.getIdPart().startsWith("cid:")) { ourLog.debug("Resource in transaction has ID[{}], will replace with server assigned ID", nextId.getIdPart()); } else if (nextResouceOperationIn == BundleEntryTransactionMethodEnum.POST) { if (nextId.isEmpty() == false) { @@ -282,7 +282,7 @@ public class FhirSystemDaoDstu1 extends BaseHapiFhirSystemDao, M } private static boolean isPlaceholder(IdDt theId) { - if ("cid:".equals(theId.getBaseUrl())) { + if (theId.getIdPart() != null && theId.getIdPart().startsWith("cid:")) { return true; } return false; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java index f2edd53d016..abbbd8b2214 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java @@ -40,7 +40,6 @@ import java.util.Set; import javax.persistence.TypedQuery; import org.apache.http.NameValuePair; -import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.springframework.beans.factory.annotation.Autowired; @@ -634,8 +633,10 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao { } private static boolean isPlaceholder(IdDt theId) { - if ("urn:oid:".equals(theId.getBaseUrl()) || "urn:uuid:".equals(theId.getBaseUrl())) { - return true; + if (theId.getValue() != null) { + if (theId.getValue().startsWith("urn:oid:") || theId.getValue().startsWith("urn:uuid:")) { + return true; + } } return false; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java index ff9af462fc8..6a38958f9d1 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java @@ -627,8 +627,10 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao { } private static boolean isPlaceholder(IdType theId) { - if ("urn:oid:".equals(theId.getBaseUrl()) || "urn:uuid:".equals(theId.getBaseUrl())) { - return true; + if (theId.getValue() != null) { + if (theId.getValue().startsWith("urn:oid:") || theId.getValue().startsWith("urn:uuid:")) { + return true; + } } return false; } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirSystemDaoDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirSystemDaoDstu2Test.java index 3dfe4f5aa79..113709d8c6a 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirSystemDaoDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirSystemDaoDstu2Test.java @@ -1615,13 +1615,11 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest { res.addEntry().setResource(p1).getRequest().setMethod(HTTPVerbEnum.POST).setUrl("Patient"); Observation o1 = new Observation(); - o1.setId("cid:observation1"); o1.addIdentifier().setSystem("system").setValue("testTransactionWithRelativeOidIds02"); o1.setSubject(new ResourceReferenceDt("urn:oid:0.1.2.3")); res.addEntry().setResource(o1).getRequest().setMethod(HTTPVerbEnum.POST).setUrl("Observation"); Observation o2 = new Observation(); - o2.setId("cid:observation2"); o2.addIdentifier().setSystem("system").setValue("testTransactionWithRelativeOidIds03"); o2.setSubject(new ResourceReferenceDt("urn:oid:0.1.2.3")); res.addEntry().setResource(o2).getRequest().setMethod(HTTPVerbEnum.POST).setUrl("Observation"); @@ -1760,13 +1758,11 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest { res.addEntry().setResource(p1).getRequest().setMethod(HTTPVerbEnum.POST).setUrl("Patient"); Observation o1 = new Observation(); - o1.setId("cid:observation1"); o1.addIdentifier().setSystem("system").setValue("testTransactionWithRelativeOidIds02"); o1.setSubject(new ResourceReferenceDt("Patient/urn:oid:0.1.2.3")); res.addEntry().setResource(o1).getRequest().setMethod(HTTPVerbEnum.POST).setUrl("Observation"); Observation o2 = new Observation(); - o2.setId("cid:observation2"); o2.addIdentifier().setSystem("system").setValue("testTransactionWithRelativeOidIds03"); o2.setSubject(new ResourceReferenceDt("Patient/urn:oid:0.1.2.3")); res.addEntry().setResource(o2).getRequest().setMethod(HTTPVerbEnum.POST).setUrl("Observation"); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java index e90a587711d..fe3db761a81 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java @@ -93,7 +93,6 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { return null; } - /** * See #410 */ @@ -104,7 +103,7 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { Bundle output = mySystemDao.transaction(mySrd, bundle); ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(output)); - + IdType id = new IdType(output.getEntry().get(1).getResponse().getLocation()); MedicationRequest mo = myMedicationRequestDao.read(id); ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(mo)); @@ -113,7 +112,7 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { @Test public void testTransactionOruBundle() throws IOException { myDaoConfig.setAllowMultipleDelete(true); - + String input = IOUtils.toString(getClass().getResourceAsStream("/oruBundle.json"), StandardCharsets.UTF_8); Bundle inputBundle; @@ -135,7 +134,7 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { Observation obs1 = new Observation(); obs1.setStatus(ObservationStatus.FINAL); IIdType obs1id = myObservationDao.create(obs1).getId().toUnqualifiedVersionless(); - + Observation obs2 = new Observation(); obs2.setStatus(ObservationStatus.FINAL); IIdType obs2id = myObservationDao.create(obs2).getId().toUnqualifiedVersionless(); @@ -144,7 +143,7 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { rpt.addIdentifier().setSystem("foo").setValue("IDENTIFIER"); rpt.addResult(new Reference(obs2id)); IIdType rptId = myDiagnosticReportDao.create(rpt).getId().toUnqualifiedVersionless(); - + myObservationDao.read(obs1id); myObservationDao.read(obs2id); @@ -155,7 +154,7 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { b.addEntry().getRequest().setMethod(HTTPVerb.DELETE).setUrl("Observation?_has:DiagnosticReport:result:identifier=foo|IDENTIFIER"); b.addEntry().setResource(rpt).getRequest().setMethod(HTTPVerb.PUT).setUrl("DiagnosticReport?identifier=foo|IDENTIFIER"); mySystemDao.transaction(mySrd, b); - + myObservationDao.read(obs1id); try { myObservationDao.read(obs2id); @@ -163,11 +162,11 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { } catch (ResourceGoneException e) { // good } - + rpt = myDiagnosticReportDao.read(rptId); assertThat(rpt.getResult(), empty()); } - + @Test public void testReindexing() { Patient p = new Patient(); @@ -240,7 +239,7 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { assertEquals(Long.valueOf(2), entity.getIndexStatus()); } - + @Test public void testSystemMetaOperation() { @@ -254,7 +253,7 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { Patient patient = new Patient(); patient.addIdentifier().setSystem("urn:system").setValue(methodName); patient.addName().setFamily("Tester").addGiven("Joe"); - + patient.getMeta().addTag(null, "Dog", "Puppies"); patient.getMeta().getSecurity().add(new Coding().setSystem("seclabel:sys:1").setCode("seclabel:code:1").setDisplay("seclabel:dis:1")); patient.getMeta().getProfile().add(new IdType("http://profile/1")); @@ -265,7 +264,7 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { Patient patient = new Patient(); patient.addIdentifier().setSystem("urn:system").setValue(methodName); patient.addName().setFamily("Tester").addGiven("Joe"); - + patient.getMeta().addTag("http://foo", "Cat", "Kittens"); patient.getMeta().getSecurity().add(new Coding().setSystem("seclabel:sys:2").setCode("seclabel:code:2").setDisplay("seclabel:dis:2")); patient.getMeta().getProfile().add(new IdType("http://profile/2")); @@ -362,16 +361,14 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { assertThat(respEntry.getStatus(), startsWith("404")); } - - + @Test public void testTransactionCreateInlineMatchUrlWithNoMatches() { String methodName = "testTransactionCreateInlineMatchUrlWithNoMatches"; Bundle request = new Bundle(); myDaoConfig.setAllowInlineMatchUrlReferences(true); - - + Observation o = new Observation(); o.getCode().setText("Some Observation"); o.getSubject().setReference("Patient?identifier=urn%3Asystem%7C" + methodName); @@ -385,14 +382,13 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { } } - @Test public void testTransactionCreateInlineMatchUrlWithOneMatch() { String methodName = "testTransactionCreateInlineMatchUrlWithOneMatch"; Bundle request = new Bundle(); myDaoConfig.setAllowInlineMatchUrlReferences(true); - + Patient p = new Patient(); p.addIdentifier().setSystem("urn:system").setValue(methodName); p.setId("Patient/" + methodName); @@ -418,14 +414,14 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { assertEquals("1", o.getIdElement().getVersionIdPart()); } - + @Test public void testTransactionCreateInlineMatchUrlWithOneMatch2() { String methodName = "testTransactionCreateInlineMatchUrlWithOneMatch2"; Bundle request = new Bundle(); myDaoConfig.setAllowInlineMatchUrlReferences(true); - + Patient p = new Patient(); p.addName().addGiven("Heute"); p.addIdentifier().setSystem("urn:system").setValue(methodName); @@ -493,7 +489,7 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { Bundle request = new Bundle(); myDaoConfig.setAllowInlineMatchUrlReferences(true); - + Patient p = new Patient(); p.addIdentifier().setSystem("urn:system").setValue(methodName); myPatientDao.create(p, mySrd).getId(); @@ -514,7 +510,7 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { assertEquals("Invalid match URL \"Patient?identifier=urn%3Asystem%7CtestTransactionCreateInlineMatchUrlWithTwoMatches\" - Multiple resources match this search", e.getMessage()); } } - + @Test public void testTransactionCreateMatchUrlWithOneMatch() { String methodName = "testTransactionCreateMatchUrlWithOneMatch"; @@ -556,7 +552,7 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { assertEquals("1", o.getIdElement().getVersionIdPart()); } - + @Test public void testTransactionCreateMatchUrlWithTwoMatch() { String methodName = "testTransactionCreateMatchUrlWithTwoMatch"; @@ -651,16 +647,16 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { o1.getPartOf().setReference(o2.getId()); o2.getPartOf().setReference(o1.getId()); - + Bundle resp = mySystemDao.transaction(mySrd, request); assertEquals(BundleType.TRANSACTIONRESPONSE, resp.getTypeElement().getValue()); assertEquals(2, resp.getEntry().size()); IdType id1 = new IdType(resp.getEntry().get(0).getResponse().getLocation()); IdType id2 = new IdType(resp.getEntry().get(1).getResponse().getLocation()); - + ourLog.info("ID1: {}", id1); - + SearchParameterMap map = new SearchParameterMap(); map.add(Organization.SP_PARTOF, new ReferenceParam(id1.toUnqualifiedVersionless().getValue())); IBundleProvider res = myOrganizationDao.search(map); @@ -690,16 +686,16 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { o1.getPartOf().setReference(o1.getId()); o2.getPartOf().setReference(o2.getId()); - + resp = mySystemDao.transaction(mySrd, request); assertEquals(BundleType.TRANSACTIONRESPONSE, resp.getTypeElement().getValue()); assertEquals(2, resp.getEntry().size()); id1 = new IdType(resp.getEntry().get(0).getResponse().getLocation()); id2 = new IdType(resp.getEntry().get(1).getResponse().getLocation()); - + ourLog.info("ID1: {}", id1); - + map = new SearchParameterMap(); map.add(Organization.SP_PARTOF, new ReferenceParam(id1.toUnqualifiedVersionless().getValue())); res = myOrganizationDao.search(map); @@ -749,7 +745,7 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { assertEquals(Patient.class, details.getResource().getClass()); } - + @Test public void testTransactionCreateWithDuplicateMatchUrl01() { String methodName = "testTransactionCreateWithDuplicateMatchUrl01"; @@ -768,7 +764,8 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { mySystemDao.transaction(mySrd, request); fail(); } catch (InvalidRequestException e) { - assertEquals(e.getMessage(), "Unable to process Transaction - Request would cause multiple resources to match URL: \"Patient?identifier=urn%3Asystem%7CtestTransactionCreateWithDuplicateMatchUrl01\". Does transaction request contain duplicates?"); + assertEquals(e.getMessage(), + "Unable to process Transaction - Request would cause multiple resources to match URL: \"Patient?identifier=urn%3Asystem%7CtestTransactionCreateWithDuplicateMatchUrl01\". Does transaction request contain duplicates?"); } } @@ -790,7 +787,8 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { mySystemDao.transaction(mySrd, request); fail(); } catch (InvalidRequestException e) { - assertEquals(e.getMessage(), "Unable to process Transaction - Request would cause multiple resources to match URL: \"Patient?identifier=urn%3Asystem%7CtestTransactionCreateWithDuplicateMatchUrl02\". Does transaction request contain duplicates?"); + assertEquals(e.getMessage(), + "Unable to process Transaction - Request would cause multiple resources to match URL: \"Patient?identifier=urn%3Asystem%7CtestTransactionCreateWithDuplicateMatchUrl02\". Does transaction request contain duplicates?"); } } @@ -878,7 +876,7 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { request.addEntry().setResource(p).getRequest().setMethod(HTTPVerb.PUT).setUrl("http://localhost/server/base/Patient/" + methodName); mySystemDao.transaction(mySrd, request); - + myPatientDao.read(new IdType("Patient/" + methodName), mySrd); } @@ -897,7 +895,7 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { request.addEntry().setResource(p).getRequest().setMethod(HTTPVerb.PUT).setUrl("Patient/" + methodName); mySystemDao.transaction(mySrd, request); - + myObservationDao.read(new IdType("Observation/a" + methodName), mySrd); myPatientDao.read(new IdType("Patient/" + methodName), mySrd); } @@ -1145,7 +1143,6 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { } } - @Test public void testTransactionDeleteMatchUrlWithZeroMatch() { String methodName = "testTransactionDeleteMatchUrlWithZeroMatch"; @@ -1157,7 +1154,7 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { Bundle resp = mySystemDao.transaction(mySrd, request); assertEquals(1, resp.getEntry().size()); assertEquals("204 No Content", resp.getEntry().get(0).getResponse().getStatus()); - + // fail(); // } catch (ResourceNotFoundException e) { // assertThat(e.getMessage(), containsString("resource matching URL \"Patient?")); @@ -1194,14 +1191,14 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { public void testTransactionDoesNotAllowDanglingTemporaryIds() throws Exception { String input = IOUtils.toString(getClass().getResourceAsStream("/cdr-bundle.json"), StandardCharsets.UTF_8); Bundle bundle = myFhirCtx.newJsonParser().parseResource(Bundle.class, input); - + BundleEntryComponent entry = bundle.addEntry(); Patient p = new Patient(); p.getManagingOrganization().setReference("urn:uuid:30ce60cf-f7cb-4196-961f-cadafa8b7ff5"); entry.setResource(p); entry.getRequest().setMethod(HTTPVerb.POST); entry.getRequest().setUrl("Patient"); - + try { mySystemDao.transaction(mySrd, bundle); fail(); @@ -1215,13 +1212,13 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { String input = IOUtils.toString(getClass().getResourceAsStream("/cdr-bundle.json"), StandardCharsets.UTF_8); Bundle bundle = myFhirCtx.newJsonParser().parseResource(Bundle.class, input); mySystemDao.transaction(mySrd, bundle); - + IBundleProvider history = mySystemDao.history(null, null, null); Bundle list = toBundle(history); ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(list)); - + assertEquals(6, list.getEntry().size()); - + Patient p = find(list, Patient.class, 0); assertTrue(p.getIdElement().isIdPartValidLong()); assertTrue(p.getGeneralPractitionerFirstRep().getReferenceElement().isIdPartValidLong()); @@ -1268,7 +1265,7 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { String input = IOUtils.toString(getClass().getResourceAsStream("/transaction-bundle.xml"), StandardCharsets.UTF_8); Bundle bundle = myFhirCtx.newXmlParser().parseResource(Bundle.class, input); Bundle response = mySystemDao.transaction(mySrd, bundle); - + ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(response)); assertEquals("201 Created", response.getEntry().get(0).getResponse().getStatus()); assertThat(response.getEntry().get(0).getResponse().getLocation(), matchesPattern("Practitioner/[0-9]+/_history/1")); @@ -1276,10 +1273,10 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { /* * Now a second time */ - + bundle = myFhirCtx.newXmlParser().parseResource(Bundle.class, input); response = mySystemDao.transaction(mySrd, bundle); - + ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(response)); assertEquals("200 OK", response.getEntry().get(0).getResponse().getStatus()); assertThat(response.getEntry().get(0).getResponse().getLocation(), matchesPattern("Practitioner/[0-9]+/_history/1")); @@ -1312,7 +1309,7 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { @Test public void testTransactionOrdering() { String methodName = "testTransactionOrdering"; - + //@formatter:off /* * Transaction Order, per the spec: @@ -1745,11 +1742,11 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { "\"status\":\"201 Created\"," + "\"location\":\"Questionnaire/54127-6/_history/1\",")); //@formatter:on - + /* * Upload again to update */ - + resp = mySystemDao.transaction(mySrd, bundle); encoded = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(resp); @@ -1771,13 +1768,13 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { Patient patient = new Patient(); patient.addIdentifier().setSystem("http://www.ghh.org/identifiers").setValue("condreftestpatid1"); myPatientDao.create(patient, mySrd); - + String input = IOUtils.toString(getClass().getResourceAsStream("/simone-conditional-url.xml"), StandardCharsets.UTF_8); Bundle bundle = myFhirCtx.newXmlParser().parseResource(Bundle.class, input); - + Bundle response = mySystemDao.transaction(mySrd, bundle); ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(response)); - + } @Test @@ -1794,14 +1791,14 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { String input = IOUtils.toString(getClass().getResourceAsStream("/simone-conditional-url.xml"), StandardCharsets.UTF_8); Bundle bundle = myFhirCtx.newXmlParser().parseResource(Bundle.class, input); - + try { mySystemDao.transaction(mySrd, bundle); fail(); } catch (PreconditionFailedException e) { assertEquals("Invalid match URL \"Patient?identifier=http://www.ghh.org/identifiers|condreftestpatid1\" - Multiple resources match this search", e.getMessage()); } - + } @Test @@ -1810,14 +1807,14 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { String input = IOUtils.toString(getClass().getResourceAsStream("/simone-conditional-url.xml"), StandardCharsets.UTF_8); Bundle bundle = myFhirCtx.newXmlParser().parseResource(Bundle.class, input); - + try { mySystemDao.transaction(mySrd, bundle); fail(); } catch (ResourceNotFoundException e) { assertEquals("Invalid match URL \"Patient?identifier=http://www.ghh.org/identifiers|condreftestpatid1\" - No resources match this search", e.getMessage()); } - + } @Test @@ -1841,7 +1838,7 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { Patient p = new Patient(); p.addName().setFamily("family"); final IIdType id = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless(); - + Bundle inputBundle = new Bundle(); //@formatter:off @@ -1856,7 +1853,7 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { .setMethod(HTTPVerb.POST) .setUrl("Patient"); //@formatter:on - + //@formatter:off Appointment app1 = new Appointment(); app1.addParticipant().getActor().setReference(id.getValue()); @@ -1882,11 +1879,11 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { Bundle outputBundle = mySystemDao.transaction(mySrd, inputBundle); ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(outputBundle)); - + assertEquals(3, outputBundle.getEntry().size()); IdDt id0 = new IdDt(outputBundle.getEntry().get(0).getResponse().getLocation()); IdDt id2 = new IdDt(outputBundle.getEntry().get(2).getResponse().getLocation()); - + app2 = myAppointmentDao.read(id2, mySrd); assertEquals("NO REF", app2.getParticipant().get(0).getActor().getDisplay()); assertEquals(null, app2.getParticipant().get(0).getActor().getReference()); @@ -1947,6 +1944,24 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { assertNotEquals(medOrderId1, medOrderId2); } + @Test + public void testTransactionWIthInvalidPlaceholder() throws Exception { + Bundle res = new Bundle(); + res.setType(BundleType.TRANSACTION); + + Observation o1 = new Observation(); + o1.setId("cid:observation1"); + o1.addIdentifier().setSystem("system").setValue("testTransactionWithRelativeOidIds02"); + res.addEntry().setResource(o1).getRequest().setMethod(HTTPVerb.POST).setUrl("Observation"); + + try { + mySystemDao.transaction(mySrd, res); + fail(); + } catch (InvalidRequestException e) { + assertEquals("Invalid placeholder ID found: cid:observation1 - Must be of the form 'urn:uuid:[uuid]' or 'urn:oid:[oid]'", e.getMessage()); + } + } + @Test public void testTransactionWithRelativeOidIds() throws Exception { Bundle res = new Bundle(); @@ -1958,13 +1973,11 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { res.addEntry().setResource(p1).getRequest().setMethod(HTTPVerb.POST).setUrl("Patient"); Observation o1 = new Observation(); - o1.setId("cid:observation1"); o1.addIdentifier().setSystem("system").setValue("testTransactionWithRelativeOidIds02"); o1.setSubject(new Reference("urn:oid:0.1.2.3")); res.addEntry().setResource(o1).getRequest().setMethod(HTTPVerb.POST).setUrl("Observation"); Observation o2 = new Observation(); - o2.setId("cid:observation2"); o2.addIdentifier().setSystem("system").setValue("testTransactionWithRelativeOidIds03"); o2.setSubject(new Reference("urn:oid:0.1.2.3")); res.addEntry().setResource(o2).getRequest().setMethod(HTTPVerb.POST).setUrl("Observation"); @@ -2103,13 +2116,11 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { res.addEntry().setResource(p1).getRequest().setMethod(HTTPVerb.POST).setUrl("Patient"); Observation o1 = new Observation(); - o1.setId("cid:observation1"); o1.addIdentifier().setSystem("system").setValue("testTransactionWithRelativeOidIds02"); o1.setSubject(new Reference("Patient/urn:oid:0.1.2.3")); res.addEntry().setResource(o1).getRequest().setMethod(HTTPVerb.POST).setUrl("Observation"); Observation o2 = new Observation(); - o2.setId("cid:observation2"); o2.addIdentifier().setSystem("system").setValue("testTransactionWithRelativeOidIds03"); o2.setSubject(new Reference("Patient/urn:oid:0.1.2.3")); res.addEntry().setResource(o2).getRequest().setMethod(HTTPVerb.POST).setUrl("Observation"); diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/model/primitive/IdDtTest.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/model/primitive/IdDtTest.java index 100cec3ed74..ea55c68199d 100644 --- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/model/primitive/IdDtTest.java +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/model/primitive/IdDtTest.java @@ -20,6 +20,71 @@ public class IdDtTest { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(IdDtTest.class); + @Test + public void testUuid() { + IdDt id = new IdDt("urn:uuid:1234-5678"); + assertEquals("urn:uuid:1234-5678", id.getValueAsString()); + assertEquals("urn:uuid:1234-5678", id.getIdPart()); + assertEquals("urn:uuid:1234-5678", id.toUnqualified().getValueAsString()); + assertEquals("urn:uuid:1234-5678", id.toUnqualifiedVersionless().getValueAsString()); + assertEquals(null, id.getVersionIdPart()); + assertEquals(null, id.getResourceType()); + assertEquals(null, id.getBaseUrl()); + + assertEquals("urn:uuid:1234-5678", id.withResourceType("Patient").getValue()); + assertEquals("urn:uuid:1234-5678", id.withServerBase("http://foo", "Patient").getValue()); + assertEquals("urn:uuid:1234-5678", id.withVersion("2").getValue()); + } + + @Test + public void testOid() { + IdDt id = new IdDt("urn:oid:1.2.3.4"); + assertEquals("urn:oid:1.2.3.4", id.getValueAsString()); + assertEquals("urn:oid:1.2.3.4", id.getIdPart()); + assertEquals("urn:oid:1.2.3.4", id.toUnqualified().getValueAsString()); + assertEquals("urn:oid:1.2.3.4", id.toUnqualifiedVersionless().getValueAsString()); + assertEquals(null, id.getVersionIdPart()); + assertEquals(null, id.getResourceType()); + assertEquals(null, id.getBaseUrl()); + + assertEquals("urn:oid:1.2.3.4", id.withResourceType("Patient").getValue()); + assertEquals("urn:oid:1.2.3.4", id.withServerBase("http://foo", "Patient").getValue()); + assertEquals("urn:oid:1.2.3.4", id.withVersion("2").getValue()); + } + + @Test + public void testLocal() { + IdDt id = new IdDt("#foo"); + assertEquals("#foo", id.getValueAsString()); + assertEquals("#foo", id.getIdPart()); + assertEquals("#foo", id.toUnqualified().getValueAsString()); + assertEquals("#foo", id.toUnqualifiedVersionless().getValueAsString()); + assertEquals(null, id.getVersionIdPart()); + assertEquals(null, id.getResourceType()); + assertEquals(null, id.getBaseUrl()); + + assertEquals("#foo", id.withResourceType("Patient").getValue()); + assertEquals("#foo", id.withServerBase("http://foo", "Patient").getValue()); + assertEquals("#foo", id.withVersion("2").getValue()); + } + + @Test + public void testNormal() { + IdDt id = new IdDt("foo"); + assertEquals("foo", id.getValueAsString()); + assertEquals("foo", id.getIdPart()); + assertEquals("foo", id.toUnqualified().getValueAsString()); + assertEquals("foo", id.toUnqualifiedVersionless().getValueAsString()); + assertEquals(null, id.getVersionIdPart()); + assertEquals(null, id.getResourceType()); + assertEquals(null, id.getBaseUrl()); + + assertEquals("Patient/foo", id.withResourceType("Patient").getValue()); + assertEquals("http://foo/Patient/foo", id.withServerBase("http://foo", "Patient").getValue()); + assertEquals("foo/_history/2", id.withVersion("2").getValue()); + } + + @Test public void testDetectIsIdPartValid() { assertTrue(new IdDt("0").isIdPartValid()); @@ -60,16 +125,16 @@ public class IdDtTest { @Test public void testDetectLocalBase() { assertEquals("urn:uuid:180f219f-97a8-486d-99d9-ed631fe4fc57", new IdDt("urn:uuid:180f219f-97a8-486d-99d9-ed631fe4fc57").getValue()); - assertEquals("urn:uuid:", new IdDt("urn:uuid:180f219f-97a8-486d-99d9-ed631fe4fc57").getBaseUrl()); - assertEquals("180f219f-97a8-486d-99d9-ed631fe4fc57", new IdDt("urn:uuid:180f219f-97a8-486d-99d9-ed631fe4fc57").getIdPart()); + assertEquals(null, new IdDt("urn:uuid:180f219f-97a8-486d-99d9-ed631fe4fc57").getBaseUrl()); + assertEquals("urn:uuid:180f219f-97a8-486d-99d9-ed631fe4fc57", new IdDt("urn:uuid:180f219f-97a8-486d-99d9-ed631fe4fc57").getIdPart()); assertEquals("cid:180f219f-97a8-486d-99d9-ed631fe4fc57", new IdDt("cid:180f219f-97a8-486d-99d9-ed631fe4fc57").getValue()); - assertEquals("cid:", new IdDt("cid:180f219f-97a8-486d-99d9-ed631fe4fc57").getBaseUrl()); - assertEquals("180f219f-97a8-486d-99d9-ed631fe4fc57", new IdDt("cid:180f219f-97a8-486d-99d9-ed631fe4fc57").getIdPart()); + assertEquals(null, new IdDt("cid:180f219f-97a8-486d-99d9-ed631fe4fc57").getBaseUrl()); + assertEquals("cid:180f219f-97a8-486d-99d9-ed631fe4fc57", new IdDt("cid:180f219f-97a8-486d-99d9-ed631fe4fc57").getIdPart()); assertEquals("#180f219f-97a8-486d-99d9-ed631fe4fc57", new IdDt("#180f219f-97a8-486d-99d9-ed631fe4fc57").getValue()); - assertEquals("#", new IdDt("#180f219f-97a8-486d-99d9-ed631fe4fc57").getBaseUrl()); - assertEquals("180f219f-97a8-486d-99d9-ed631fe4fc57", new IdDt("#180f219f-97a8-486d-99d9-ed631fe4fc57").getIdPart()); + assertEquals(null, new IdDt("#180f219f-97a8-486d-99d9-ed631fe4fc57").getBaseUrl()); + assertEquals("#180f219f-97a8-486d-99d9-ed631fe4fc57", new IdDt("#180f219f-97a8-486d-99d9-ed631fe4fc57").getIdPart()); } @@ -80,10 +145,10 @@ public class IdDtTest { public void testComplicatedLocal() { IdDt id = new IdDt("#Patient/cid:Patient-72/_history/1"); assertTrue(id.isLocal()); - assertEquals("#", id.getBaseUrl()); + assertEquals(null, id.getBaseUrl()); assertNull(id.getResourceType()); assertNull(id.getVersionIdPart()); - assertEquals("Patient/cid:Patient-72/_history/1", id.getIdPart()); + assertEquals("#Patient/cid:Patient-72/_history/1", id.getIdPart()); } diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java index edd620a8cf3..12d87c133b6 100644 --- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java @@ -561,8 +561,8 @@ public class XmlParserTest { // Re-parse the bundle patient = (Patient) xmlParser.parseResource(xmlParser.encodeResourceToString(patient)); assertEquals("#1", patient.getManagingOrganization().getReference().getValue()); - assertEquals("#", patient.getManagingOrganization().getReference().getBaseUrl()); - assertEquals("1", patient.getManagingOrganization().getReference().getIdPart()); + assertEquals(null, patient.getManagingOrganization().getReference().getBaseUrl()); + assertEquals("#1", patient.getManagingOrganization().getReference().getIdPart()); assertNotNull(patient.getManagingOrganization().getResource()); org = (Organization) patient.getManagingOrganization().getResource(); diff --git a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/model/IdType.java b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/model/IdType.java index 8204c31a3f5..8a6df569886 100644 --- a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/model/IdType.java +++ b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/model/IdType.java @@ -1,5 +1,7 @@ package org.hl7.fhir.dstu2016may.model; +import static org.apache.commons.lang3.StringUtils.defaultString; + /* Copyright (c) 2011+, HL7, Inc. All rights reserved. @@ -242,34 +244,6 @@ public final class IdType extends UriType implements IPrimitiveType, IId return new IdType(getValue()); } - private String determineLocalPrefix(String theValue) { - if (theValue == null || theValue.isEmpty()) { - return null; - } - if (theValue.startsWith("#")) { - return "#"; - } - int lastPrefix = -1; - for (int i = 0; i < theValue.length(); i++) { - char nextChar = theValue.charAt(i); - if (nextChar == ':') { - lastPrefix = i; - } else if (!Character.isLetter(nextChar) || !Character.isLowerCase(nextChar)) { - break; - } - } - if (lastPrefix != -1) { - String candidate = theValue.substring(0, lastPrefix + 1); - if (candidate.startsWith("cid:") || candidate.startsWith("urn:")) { - return candidate; - } else { - return null; - } - } else { - return null; - } - } - @Override public boolean equals(Object theArg0) { if (!(theArg0 instanceof IdType)) { @@ -368,8 +342,8 @@ public final class IdType extends UriType implements IPrimitiveType, IId String retVal = super.getValue(); if (retVal == null && myHaveComponentParts) { - if (determineLocalPrefix(myBaseUrl) != null && myResourceType == null && myUnqualifiedVersionId == null) { - return myBaseUrl + myUnqualifiedId; + if (isLocal() || isUrn()) { + return myUnqualifiedId; } StringBuilder b = new StringBuilder(); @@ -510,7 +484,11 @@ public final class IdType extends UriType implements IPrimitiveType, IId */ @Override public boolean isLocal() { - return "#".equals(myBaseUrl); + return defaultString(myUnqualifiedId).startsWith("#"); + } + + private boolean isUrn() { + return defaultString(myUnqualifiedId).startsWith("urn:"); } @Override @@ -536,8 +514,6 @@ public final class IdType extends UriType implements IPrimitiveType, IId super.setValue(theValue); myHaveComponentParts = false; - String localPrefix = determineLocalPrefix(theValue); - if (StringUtils.isBlank(theValue)) { myBaseUrl = null; super.setValue(null); @@ -545,15 +521,18 @@ public final class IdType extends UriType implements IPrimitiveType, IId myUnqualifiedVersionId = null; myResourceType = null; } else if (theValue.charAt(0) == '#' && theValue.length() > 1) { - super.setValue(theValue); - myBaseUrl = "#"; - myUnqualifiedId = theValue.substring(1); - myUnqualifiedVersionId = null; - myResourceType = null; - myHaveComponentParts = true; - } else if (localPrefix != null) { - myBaseUrl = localPrefix; - myUnqualifiedId = theValue.substring(localPrefix.length()); + super.setValue(theValue); + myBaseUrl = null; + myUnqualifiedId = theValue; + myUnqualifiedVersionId = null; + myResourceType = null; + myHaveComponentParts = true; + } else if (theValue.startsWith("urn:")) { + myBaseUrl = null; + myUnqualifiedId = theValue; + myUnqualifiedVersionId = null; + myResourceType = null; + myHaveComponentParts = true; } else { int vidIndex = theValue.indexOf("/_history/"); int idIndex; @@ -618,21 +597,33 @@ public final class IdType extends UriType implements IPrimitiveType, IId */ @Override public IdType toUnqualified() { + if (isLocal() || isUrn()) { + return new IdType(getValueAsString()); + } return new IdType(getResourceType(), getIdPart(), getVersionIdPart()); } @Override public IdType toUnqualifiedVersionless() { + if (isLocal() || isUrn()) { + return new IdType(getValueAsString()); + } return new IdType(getResourceType(), getIdPart()); } @Override public IdType toVersionless() { + if (isLocal() || isUrn()) { + return new IdType(getValueAsString()); + } return new IdType(getBaseUrl(), getResourceType(), getIdPart(), null); } @Override public IdType withResourceType(String theResourceName) { + if (isLocal() || isUrn()) { + return new IdType(getValueAsString()); + } return new IdType(theResourceName, getIdPart(), getVersionIdPart()); } @@ -652,6 +643,9 @@ public final class IdType extends UriType implements IPrimitiveType, IId */ @Override public IdType withServerBase(String theServerBase, String theResourceType) { + if (isLocal() || isUrn()) { + return new IdType(getValueAsString()); + } return new IdType(theServerBase, theResourceType, getIdPart(), getVersionIdPart()); } @@ -667,6 +661,10 @@ public final class IdType extends UriType implements IPrimitiveType, IId public IdType withVersion(String theVersion) { Validate.notBlank(theVersion, "Version may not be null or empty"); + if (isLocal() || isUrn()) { + return new IdType(getValueAsString()); + } + String existingValue = getValue(); int i = existingValue.indexOf("_history"); diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/model/IdTypeDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/model/IdTypeDstu2_1Test.java index 97a84ec0cce..acfeec4ae3e 100644 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/model/IdTypeDstu2_1Test.java +++ b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/model/IdTypeDstu2_1Test.java @@ -28,6 +28,70 @@ public class IdTypeDstu2_1Test { TestUtil.clearAllStaticFieldsForUnitTest(); } + @Test + public void testUuid() { + IdType id = new IdType("urn:uuid:1234-5678"); + assertEquals("urn:uuid:1234-5678", id.getValueAsString()); + assertEquals("urn:uuid:1234-5678", id.getIdPart()); + assertEquals("urn:uuid:1234-5678", id.toUnqualified().getValueAsString()); + assertEquals("urn:uuid:1234-5678", id.toUnqualifiedVersionless().getValueAsString()); + assertEquals(null, id.getVersionIdPart()); + assertEquals(null, id.getResourceType()); + assertEquals(null, id.getBaseUrl()); + + assertEquals("urn:uuid:1234-5678", id.withResourceType("Patient").getValue()); + assertEquals("urn:uuid:1234-5678", id.withServerBase("http://foo", "Patient").getValue()); + assertEquals("urn:uuid:1234-5678", id.withVersion("2").getValue()); + } + + @Test + public void testOid() { + IdType id = new IdType("urn:oid:1.2.3.4"); + assertEquals("urn:oid:1.2.3.4", id.getValueAsString()); + assertEquals("urn:oid:1.2.3.4", id.getIdPart()); + assertEquals("urn:oid:1.2.3.4", id.toUnqualified().getValueAsString()); + assertEquals("urn:oid:1.2.3.4", id.toUnqualifiedVersionless().getValueAsString()); + assertEquals(null, id.getVersionIdPart()); + assertEquals(null, id.getResourceType()); + assertEquals(null, id.getBaseUrl()); + + assertEquals("urn:oid:1.2.3.4", id.withResourceType("Patient").getValue()); + assertEquals("urn:oid:1.2.3.4", id.withServerBase("http://foo", "Patient").getValue()); + assertEquals("urn:oid:1.2.3.4", id.withVersion("2").getValue()); + } + + @Test + public void testLocal() { + IdType id = new IdType("#foo"); + assertEquals("#foo", id.getValueAsString()); + assertEquals("#foo", id.getIdPart()); + assertEquals("#foo", id.toUnqualified().getValueAsString()); + assertEquals("#foo", id.toUnqualifiedVersionless().getValueAsString()); + assertEquals(null, id.getVersionIdPart()); + assertEquals(null, id.getResourceType()); + assertEquals(null, id.getBaseUrl()); + + assertEquals("#foo", id.withResourceType("Patient").getValue()); + assertEquals("#foo", id.withServerBase("http://foo", "Patient").getValue()); + assertEquals("#foo", id.withVersion("2").getValue()); + } + + @Test + public void testNormal() { + IdType id = new IdType("foo"); + assertEquals("foo", id.getValueAsString()); + assertEquals("foo", id.getIdPart()); + assertEquals("foo", id.toUnqualified().getValueAsString()); + assertEquals("foo", id.toUnqualifiedVersionless().getValueAsString()); + assertEquals(null, id.getVersionIdPart()); + assertEquals(null, id.getResourceType()); + assertEquals(null, id.getBaseUrl()); + + assertEquals("Patient/foo", id.withResourceType("Patient").getValue()); + assertEquals("http://foo/Patient/foo", id.withServerBase("http://foo", "Patient").getValue()); + assertEquals("foo/_history/2", id.withVersion("2").getValue()); + } + @Test public void testDetectLocal() { @@ -55,16 +119,16 @@ public class IdTypeDstu2_1Test { @Test public void testDetectLocalBase() { assertEquals("urn:uuid:180f219f-97a8-486d-99d9-ed631fe4fc57", new IdType("urn:uuid:180f219f-97a8-486d-99d9-ed631fe4fc57").getValue()); - assertEquals("urn:uuid:", new IdType("urn:uuid:180f219f-97a8-486d-99d9-ed631fe4fc57").getBaseUrl()); - assertEquals("180f219f-97a8-486d-99d9-ed631fe4fc57", new IdType("urn:uuid:180f219f-97a8-486d-99d9-ed631fe4fc57").getIdPart()); + assertEquals(null, new IdType("urn:uuid:180f219f-97a8-486d-99d9-ed631fe4fc57").getBaseUrl()); + assertEquals("urn:uuid:180f219f-97a8-486d-99d9-ed631fe4fc57", new IdType("urn:uuid:180f219f-97a8-486d-99d9-ed631fe4fc57").getIdPart()); assertEquals("cid:180f219f-97a8-486d-99d9-ed631fe4fc57", new IdType("cid:180f219f-97a8-486d-99d9-ed631fe4fc57").getValue()); - assertEquals("cid:", new IdType("cid:180f219f-97a8-486d-99d9-ed631fe4fc57").getBaseUrl()); - assertEquals("180f219f-97a8-486d-99d9-ed631fe4fc57", new IdType("cid:180f219f-97a8-486d-99d9-ed631fe4fc57").getIdPart()); + assertEquals(null, new IdType("cid:180f219f-97a8-486d-99d9-ed631fe4fc57").getBaseUrl()); + assertEquals("cid:180f219f-97a8-486d-99d9-ed631fe4fc57", new IdType("cid:180f219f-97a8-486d-99d9-ed631fe4fc57").getIdPart()); assertEquals("#180f219f-97a8-486d-99d9-ed631fe4fc57", new IdType("#180f219f-97a8-486d-99d9-ed631fe4fc57").getValue()); - assertEquals("#", new IdType("#180f219f-97a8-486d-99d9-ed631fe4fc57").getBaseUrl()); - assertEquals("180f219f-97a8-486d-99d9-ed631fe4fc57", new IdType("#180f219f-97a8-486d-99d9-ed631fe4fc57").getIdPart()); + assertEquals(null, new IdType("#180f219f-97a8-486d-99d9-ed631fe4fc57").getBaseUrl()); + assertEquals("#180f219f-97a8-486d-99d9-ed631fe4fc57", new IdType("#180f219f-97a8-486d-99d9-ed631fe4fc57").getIdPart()); } @@ -75,20 +139,20 @@ public class IdTypeDstu2_1Test { public void testComplicatedLocal() { IdType id = new IdType("#Patient/cid:Patient-72/_history/1"); assertTrue(id.isLocal()); - assertEquals("#", id.getBaseUrl()); + assertEquals(null, id.getBaseUrl()); assertNull(id.getResourceType()); assertNull(id.getVersionIdPart()); - assertEquals("Patient/cid:Patient-72/_history/1", id.getIdPart()); + assertEquals("#Patient/cid:Patient-72/_history/1", id.getIdPart()); IdType id2 = new IdType("#Patient/cid:Patient-72/_history/1"); assertEquals(id, id2); id2 = id2.toUnqualified(); - assertFalse(id2.isLocal()); + assertTrue(id2.isLocal()); assertNull(id2.getBaseUrl()); assertNull(id2.getResourceType()); assertNull(id2.getVersionIdPart()); - assertEquals("Patient/cid:Patient-72/_history/1", id2.getIdPart()); + assertEquals("#Patient/cid:Patient-72/_history/1", id2.getIdPart()); } diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/parser/JsonParserDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/parser/JsonParserDstu2_1Test.java index abdb0d5f290..3a903e512a5 100644 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/parser/JsonParserDstu2_1Test.java +++ b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/parser/JsonParserDstu2_1Test.java @@ -1,6 +1,5 @@ package ca.uhn.fhir.parser; -import static org.apache.commons.lang3.StringUtils.countMatches; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; @@ -15,79 +14,31 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Matchers.eq; import static org.mockito.Matchers.isNull; -import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import java.io.IOException; import java.math.BigDecimal; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.HashSet; -import java.util.List; -import java.util.UUID; +import java.util.*; import org.apache.commons.io.IOUtils; import org.hamcrest.Matchers; import org.hamcrest.core.StringContains; +import org.hl7.fhir.dstu2016may.model.*; import org.hl7.fhir.dstu2016may.model.Address.AddressUse; import org.hl7.fhir.dstu2016may.model.Address.AddressUseEnumFactory; -import org.hl7.fhir.dstu2016may.model.Attachment; -import org.hl7.fhir.dstu2016may.model.AuditEvent; -import org.hl7.fhir.dstu2016may.model.Basic; -import org.hl7.fhir.dstu2016may.model.Binary; -import org.hl7.fhir.dstu2016may.model.Bundle; import org.hl7.fhir.dstu2016may.model.Bundle.BundleEntryComponent; import org.hl7.fhir.dstu2016may.model.Bundle.BundleType; -import org.hl7.fhir.dstu2016may.model.Claim; -import org.hl7.fhir.dstu2016may.model.Coding; -import org.hl7.fhir.dstu2016may.model.Communication; -import org.hl7.fhir.dstu2016may.model.Condition; import org.hl7.fhir.dstu2016may.model.Condition.ConditionVerificationStatus; -import org.hl7.fhir.dstu2016may.model.Conformance; import org.hl7.fhir.dstu2016may.model.Conformance.UnknownContentCode; -import org.hl7.fhir.dstu2016may.model.Coverage; -import org.hl7.fhir.dstu2016may.model.DateTimeType; -import org.hl7.fhir.dstu2016may.model.DateType; -import org.hl7.fhir.dstu2016may.model.DecimalType; -import org.hl7.fhir.dstu2016may.model.DiagnosticReport; -import org.hl7.fhir.dstu2016may.model.EnumFactory; import org.hl7.fhir.dstu2016may.model.Enumeration; import org.hl7.fhir.dstu2016may.model.Enumerations.AdministrativeGender; -import org.hl7.fhir.dstu2016may.model.ExplanationOfBenefit; -import org.hl7.fhir.dstu2016may.model.Extension; -import org.hl7.fhir.dstu2016may.model.HumanName; -import org.hl7.fhir.dstu2016may.model.IdType; import org.hl7.fhir.dstu2016may.model.Identifier.IdentifierUse; -import org.hl7.fhir.dstu2016may.model.Linkage; -import org.hl7.fhir.dstu2016may.model.Medication; -import org.hl7.fhir.dstu2016may.model.MedicationOrder; -import org.hl7.fhir.dstu2016may.model.Observation; import org.hl7.fhir.dstu2016may.model.Observation.ObservationStatus; -import org.hl7.fhir.dstu2016may.model.Parameters; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.hl7.fhir.dstu2016may.model.PrimitiveType; -import org.hl7.fhir.dstu2016may.model.Quantity; -import org.hl7.fhir.dstu2016may.model.QuestionnaireResponse; -import org.hl7.fhir.dstu2016may.model.Reference; -import org.hl7.fhir.dstu2016may.model.RelatedPerson; -import org.hl7.fhir.dstu2016may.model.SampledData; -import org.hl7.fhir.dstu2016may.model.SimpleQuantity; -import org.hl7.fhir.dstu2016may.model.StringType; -import org.hl7.fhir.dstu2016may.model.UriType; -import org.hl7.fhir.dstu2016may.model.ValueSet; -import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.utilities.xhtml.XhtmlNode; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.*; import org.mockito.ArgumentCaptor; -import org.mockito.Mockito; import com.google.common.collect.Sets; @@ -96,11 +47,8 @@ import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator; import ca.uhn.fhir.parser.IParserErrorHandler.IParseLocation; import ca.uhn.fhir.parser.PatientWithExtendedContactDstu3.CustomContactComponent; import ca.uhn.fhir.parser.XmlParserDstu2_1Test.TestPatientFor327; -import ca.uhn.fhir.parser.json.JsonLikeValue.ValueType; import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.util.TestUtil; -import ca.uhn.fhir.validation.FhirValidator; -import ca.uhn.fhir.validation.ValidationResult; import net.sf.json.JSON; import net.sf.json.JSONSerializer; import net.sf.json.JsonConfig; @@ -1430,8 +1378,8 @@ public class JsonParserDstu2_1Test { ourLog.info(encoded); assertEquals("urn:uuid:180f219f-97a8-486d-99d9-ed631fe4fc57", parsed.getEntry().get(0).getResource().getIdElement().getValue()); - assertEquals("urn:uuid:", parsed.getEntry().get(0).getResource().getIdElement().getBaseUrl()); - assertEquals("180f219f-97a8-486d-99d9-ed631fe4fc57", parsed.getEntry().get(0).getResource().getIdElement().getIdPart()); + assertEquals(null, parsed.getEntry().get(0).getResource().getIdElement().getBaseUrl()); + assertEquals("urn:uuid:180f219f-97a8-486d-99d9-ed631fe4fc57", parsed.getEntry().get(0).getResource().getIdElement().getIdPart()); assertThat(encoded, not(containsString("\"id\":\"180f219f-97a8-486d-99d9-ed631fe4fc57\""))); } diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/JsonParserDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/JsonParserDstu2Test.java index 567b6c4fc89..1caed6e3f59 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/JsonParserDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/JsonParserDstu2Test.java @@ -67,6 +67,34 @@ public class JsonParserDstu2Test { private static FhirContext ourCtx = FhirContext.forDstu2(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JsonParserDstu2Test.class); + /** + * See #544 + */ + @Test + public void testBundleStitchReferencesByUuid() throws Exception { + ca.uhn.fhir.model.dstu2.resource.Bundle bundle = new ca.uhn.fhir.model.dstu2.resource.Bundle(); + + DocumentManifest dm = new DocumentManifest(); + dm.getSubject().setReference("urn:uuid:96e85cca-9797-45d6-834a-c4eb27f331d3"); + bundle.addEntry().setResource(dm); + + Patient patient = new Patient(); + patient.addName().addFamily("FAMILY"); + bundle.addEntry().setResource(patient).setFullUrl("urn:uuid:96e85cca-9797-45d6-834a-c4eb27f331d3"); + + String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle); + ourLog.info(encoded); + + bundle = ourCtx.newJsonParser().parseResource(ca.uhn.fhir.model.dstu2.resource.Bundle.class, encoded); + dm = (DocumentManifest) bundle.getEntry().get(0).getResource(); + + assertEquals("urn:uuid:96e85cca-9797-45d6-834a-c4eb27f331d3", dm.getSubject().getReference()); + + Patient subject = (Patient) dm.getSubject().getResource(); + assertNotNull(subject); + assertEquals("FAMILY", subject.getNameFirstRep().getFamilyAsSingleString()); + } + @Test public void testContainedResourceInExtensionUndeclared() { Patient p = new Patient(); @@ -89,6 +117,21 @@ public class JsonParserDstu2Test { assertEquals("ORG", o.getName()); } + @Test + public void testEncodeBundleOldStyleContainingResourceWithUuidBase() { + Patient p = new Patient(); + p.setId(IdDt.newRandomUuid()); + p.addName().addFamily("PATIENT"); + + Bundle b = new Bundle(); + b.addEntry().setResource(p); + + String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeBundleToString(b); + ourLog.info(encoded); + assertThat(encoded, stringContainsInOrder("fullUrl", p.getId().getValue(), "Patient")); + } + + /** * See #308 */ @@ -1242,8 +1285,8 @@ public class JsonParserDstu2Test { ourLog.info(encoded); assertEquals("urn:uuid:180f219f-97a8-486d-99d9-ed631fe4fc57", parsed.getEntry().get(0).getResource().getId().getValue()); - assertEquals("urn:uuid:", parsed.getEntry().get(0).getResource().getId().getBaseUrl()); - assertEquals("180f219f-97a8-486d-99d9-ed631fe4fc57", parsed.getEntry().get(0).getResource().getId().getIdPart()); + assertEquals(null, parsed.getEntry().get(0).getResource().getId().getBaseUrl()); + assertEquals("urn:uuid:180f219f-97a8-486d-99d9-ed631fe4fc57", parsed.getEntry().get(0).getResource().getId().getIdPart()); assertThat(encoded, not(containsString("\"id\":\"180f219f-97a8-486d-99d9-ed631fe4fc57\""))); } diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/IdType.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/IdType.java index e2dea1dce3f..02535643be3 100644 --- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/IdType.java +++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/IdType.java @@ -1,5 +1,7 @@ package org.hl7.fhir.dstu3.model; +import static org.apache.commons.lang3.StringUtils.defaultString; + /* Copyright (c) 2011+, HL7, Inc. All rights reserved. @@ -242,41 +244,12 @@ public final class IdType extends UriType implements IPrimitiveType, IId return new IdType(getValue()); } - private String determineLocalPrefix(String theValue) { - if (theValue == null || theValue.isEmpty()) { - return null; - } - if (theValue.startsWith("#")) { - return "#"; - } - int lastPrefix = -1; - for (int i = 0; i < theValue.length(); i++) { - char nextChar = theValue.charAt(i); - if (nextChar == ':') { - lastPrefix = i; - } else if (!Character.isLetter(nextChar) || !Character.isLowerCase(nextChar)) { - break; - } - } - if (lastPrefix != -1) { - String candidate = theValue.substring(0, lastPrefix + 1); - if (candidate.startsWith("cid:") || candidate.startsWith("urn:")) { - return candidate; - } else { - return null; - } - } else { - return null; - } - } - @Override public boolean equals(Object theArg0) { if (!(theArg0 instanceof IdType)) { return false; } - IdType id = (IdType) theArg0; - return StringUtils.equals(getValueAsString(), id.getValueAsString()); + return StringUtils.equals(getValueAsString(), ((IdType)theArg0).getValueAsString()); } /** @@ -368,8 +341,8 @@ public final class IdType extends UriType implements IPrimitiveType, IId String retVal = super.getValue(); if (retVal == null && myHaveComponentParts) { - if (determineLocalPrefix(myBaseUrl) != null && myResourceType == null && myUnqualifiedVersionId == null) { - return myBaseUrl + myUnqualifiedId; + if (isLocal() || isUrn()) { + return myUnqualifiedId; } StringBuilder b = new StringBuilder(); @@ -510,7 +483,11 @@ public final class IdType extends UriType implements IPrimitiveType, IId */ @Override public boolean isLocal() { - return "#".equals(myBaseUrl); + return defaultString(myUnqualifiedId).startsWith("#"); + } + + private boolean isUrn() { + return defaultString(myUnqualifiedId).startsWith("urn:"); } @Override @@ -536,8 +513,6 @@ public final class IdType extends UriType implements IPrimitiveType, IId super.setValue(theValue); myHaveComponentParts = false; - String localPrefix = determineLocalPrefix(theValue); - if (StringUtils.isBlank(theValue)) { myBaseUrl = null; super.setValue(null); @@ -546,14 +521,17 @@ public final class IdType extends UriType implements IPrimitiveType, IId myResourceType = null; } else if (theValue.charAt(0) == '#' && theValue.length() > 1) { super.setValue(theValue); - myBaseUrl = "#"; - myUnqualifiedId = theValue.substring(1); + myBaseUrl = null; + myUnqualifiedId = theValue; + myUnqualifiedVersionId = null; + myResourceType = null; + myHaveComponentParts = true; + } else if (theValue.startsWith("urn:")) { + myBaseUrl = null; + myUnqualifiedId = theValue; myUnqualifiedVersionId = null; myResourceType = null; myHaveComponentParts = true; - } else if (localPrefix != null) { - myBaseUrl = localPrefix; - myUnqualifiedId = theValue.substring(localPrefix.length()); } else { int vidIndex = theValue.indexOf("/_history/"); int idIndex; @@ -618,21 +596,33 @@ public final class IdType extends UriType implements IPrimitiveType, IId */ @Override public IdType toUnqualified() { + if (isLocal() || isUrn()) { + return new IdType(getValueAsString()); + } return new IdType(getResourceType(), getIdPart(), getVersionIdPart()); } @Override public IdType toUnqualifiedVersionless() { + if (isLocal() || isUrn()) { + return new IdType(getValueAsString()); + } return new IdType(getResourceType(), getIdPart()); } @Override public IdType toVersionless() { + if (isLocal() || isUrn()) { + return new IdType(getValueAsString()); + } return new IdType(getBaseUrl(), getResourceType(), getIdPart(), null); } @Override public IdType withResourceType(String theResourceName) { + if (isLocal() || isUrn()) { + return new IdType(getValueAsString()); + } return new IdType(theResourceName, getIdPart(), getVersionIdPart()); } @@ -652,6 +642,9 @@ public final class IdType extends UriType implements IPrimitiveType, IId */ @Override public IdType withServerBase(String theServerBase, String theResourceType) { + if (isLocal() || isUrn()) { + return new IdType(getValueAsString()); + } return new IdType(theServerBase, theResourceType, getIdPart(), getVersionIdPart()); } @@ -664,9 +657,14 @@ public final class IdType extends UriType implements IPrimitiveType, IId * @return A new instance of IdType which is identical, but refers to the * specific version of this resource ID noted by theVersion. */ + @Override public IdType withVersion(String theVersion) { Validate.notBlank(theVersion, "Version may not be null or empty"); + if (isLocal() || isUrn()) { + return new IdType(getValueAsString()); + } + String existingValue = getValue(); int i = existingValue.indexOf("_history"); diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/model/IdTypeDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/model/IdTypeDstu3Test.java index 37162e19ad9..f21a09e6850 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/model/IdTypeDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/model/IdTypeDstu3Test.java @@ -28,6 +28,69 @@ public class IdTypeDstu3Test { TestUtil.clearAllStaticFieldsForUnitTest(); } + @Test + public void testUuid() { + IdType id = new IdType("urn:uuid:1234-5678"); + assertEquals("urn:uuid:1234-5678", id.getValueAsString()); + assertEquals("urn:uuid:1234-5678", id.getIdPart()); + assertEquals("urn:uuid:1234-5678", id.toUnqualified().getValueAsString()); + assertEquals("urn:uuid:1234-5678", id.toUnqualifiedVersionless().getValueAsString()); + assertEquals(null, id.getVersionIdPart()); + assertEquals(null, id.getResourceType()); + assertEquals(null, id.getBaseUrl()); + + assertEquals("urn:uuid:1234-5678", id.withResourceType("Patient").getValue()); + assertEquals("urn:uuid:1234-5678", id.withServerBase("http://foo", "Patient").getValue()); + assertEquals("urn:uuid:1234-5678", id.withVersion("2").getValue()); + } + + @Test + public void testOid() { + IdType id = new IdType("urn:oid:1.2.3.4"); + assertEquals("urn:oid:1.2.3.4", id.getValueAsString()); + assertEquals("urn:oid:1.2.3.4", id.getIdPart()); + assertEquals("urn:oid:1.2.3.4", id.toUnqualified().getValueAsString()); + assertEquals("urn:oid:1.2.3.4", id.toUnqualifiedVersionless().getValueAsString()); + assertEquals(null, id.getVersionIdPart()); + assertEquals(null, id.getResourceType()); + assertEquals(null, id.getBaseUrl()); + + assertEquals("urn:oid:1.2.3.4", id.withResourceType("Patient").getValue()); + assertEquals("urn:oid:1.2.3.4", id.withServerBase("http://foo", "Patient").getValue()); + assertEquals("urn:oid:1.2.3.4", id.withVersion("2").getValue()); + } + + @Test + public void testLocal() { + IdType id = new IdType("#foo"); + assertEquals("#foo", id.getValueAsString()); + assertEquals("#foo", id.getIdPart()); + assertEquals("#foo", id.toUnqualified().getValueAsString()); + assertEquals("#foo", id.toUnqualifiedVersionless().getValueAsString()); + assertEquals(null, id.getVersionIdPart()); + assertEquals(null, id.getResourceType()); + assertEquals(null, id.getBaseUrl()); + + assertEquals("#foo", id.withResourceType("Patient").getValue()); + assertEquals("#foo", id.withServerBase("http://foo", "Patient").getValue()); + assertEquals("#foo", id.withVersion("2").getValue()); + } + + @Test + public void testNormal() { + IdType id = new IdType("foo"); + assertEquals("foo", id.getValueAsString()); + assertEquals("foo", id.getIdPart()); + assertEquals("foo", id.toUnqualified().getValueAsString()); + assertEquals("foo", id.toUnqualifiedVersionless().getValueAsString()); + assertEquals(null, id.getVersionIdPart()); + assertEquals(null, id.getResourceType()); + assertEquals(null, id.getBaseUrl()); + + assertEquals("Patient/foo", id.withResourceType("Patient").getValue()); + assertEquals("http://foo/Patient/foo", id.withServerBase("http://foo", "Patient").getValue()); + assertEquals("foo/_history/2", id.withVersion("2").getValue()); + } @Test public void testDetectLocal() { @@ -55,16 +118,16 @@ public class IdTypeDstu3Test { @Test public void testDetectLocalBase() { assertEquals("urn:uuid:180f219f-97a8-486d-99d9-ed631fe4fc57", new IdType("urn:uuid:180f219f-97a8-486d-99d9-ed631fe4fc57").getValue()); - assertEquals("urn:uuid:", new IdType("urn:uuid:180f219f-97a8-486d-99d9-ed631fe4fc57").getBaseUrl()); - assertEquals("180f219f-97a8-486d-99d9-ed631fe4fc57", new IdType("urn:uuid:180f219f-97a8-486d-99d9-ed631fe4fc57").getIdPart()); + assertEquals(null, new IdType("urn:uuid:180f219f-97a8-486d-99d9-ed631fe4fc57").getBaseUrl()); + assertEquals("urn:uuid:180f219f-97a8-486d-99d9-ed631fe4fc57", new IdType("urn:uuid:180f219f-97a8-486d-99d9-ed631fe4fc57").getIdPart()); assertEquals("cid:180f219f-97a8-486d-99d9-ed631fe4fc57", new IdType("cid:180f219f-97a8-486d-99d9-ed631fe4fc57").getValue()); - assertEquals("cid:", new IdType("cid:180f219f-97a8-486d-99d9-ed631fe4fc57").getBaseUrl()); - assertEquals("180f219f-97a8-486d-99d9-ed631fe4fc57", new IdType("cid:180f219f-97a8-486d-99d9-ed631fe4fc57").getIdPart()); + assertEquals(null, new IdType("cid:180f219f-97a8-486d-99d9-ed631fe4fc57").getBaseUrl()); + assertEquals("cid:180f219f-97a8-486d-99d9-ed631fe4fc57", new IdType("cid:180f219f-97a8-486d-99d9-ed631fe4fc57").getIdPart()); assertEquals("#180f219f-97a8-486d-99d9-ed631fe4fc57", new IdType("#180f219f-97a8-486d-99d9-ed631fe4fc57").getValue()); - assertEquals("#", new IdType("#180f219f-97a8-486d-99d9-ed631fe4fc57").getBaseUrl()); - assertEquals("180f219f-97a8-486d-99d9-ed631fe4fc57", new IdType("#180f219f-97a8-486d-99d9-ed631fe4fc57").getIdPart()); + assertEquals(null, new IdType("#180f219f-97a8-486d-99d9-ed631fe4fc57").getBaseUrl()); + assertEquals("#180f219f-97a8-486d-99d9-ed631fe4fc57", new IdType("#180f219f-97a8-486d-99d9-ed631fe4fc57").getIdPart()); } @@ -75,20 +138,20 @@ public class IdTypeDstu3Test { public void testComplicatedLocal() { IdType id = new IdType("#Patient/cid:Patient-72/_history/1"); assertTrue(id.isLocal()); - assertEquals("#", id.getBaseUrl()); + assertEquals(null, id.getBaseUrl()); assertNull(id.getResourceType()); assertNull(id.getVersionIdPart()); - assertEquals("Patient/cid:Patient-72/_history/1", id.getIdPart()); + assertEquals("#Patient/cid:Patient-72/_history/1", id.getIdPart()); IdType id2 = new IdType("#Patient/cid:Patient-72/_history/1"); assertEquals(id, id2); id2 = id2.toUnqualified(); - assertFalse(id2.isLocal()); + assertTrue(id2.isLocal()); assertNull(id2.getBaseUrl()); assertNull(id2.getResourceType()); assertNull(id2.getVersionIdPart()); - assertEquals("Patient/cid:Patient-72/_history/1", id2.getIdPart()); + assertEquals("#Patient/cid:Patient-72/_history/1", id2.getIdPart()); } diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/JsonParserDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/JsonParserDstu3Test.java index bc5ac21e26f..662c25e92a7 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/JsonParserDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/JsonParserDstu3Test.java @@ -34,52 +34,16 @@ import java.util.UUID; import org.apache.commons.io.IOUtils; import org.hamcrest.Matchers; import org.hamcrest.core.StringContains; +import org.hl7.fhir.dstu3.model.*; import org.hl7.fhir.dstu3.model.Address.AddressUse; import org.hl7.fhir.dstu3.model.Address.AddressUseEnumFactory; -import org.hl7.fhir.dstu3.model.Attachment; -import org.hl7.fhir.dstu3.model.AuditEvent; -import org.hl7.fhir.dstu3.model.Basic; -import org.hl7.fhir.dstu3.model.Binary; -import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent; import org.hl7.fhir.dstu3.model.Bundle.BundleType; -import org.hl7.fhir.dstu3.model.Claim; -import org.hl7.fhir.dstu3.model.Coding; -import org.hl7.fhir.dstu3.model.Communication; -import org.hl7.fhir.dstu3.model.Condition; import org.hl7.fhir.dstu3.model.Condition.ConditionVerificationStatus; -import org.hl7.fhir.dstu3.model.Conformance; import org.hl7.fhir.dstu3.model.Conformance.UnknownContentCode; -import org.hl7.fhir.dstu3.model.Coverage; -import org.hl7.fhir.dstu3.model.DateTimeType; -import org.hl7.fhir.dstu3.model.DateType; -import org.hl7.fhir.dstu3.model.DecimalType; -import org.hl7.fhir.dstu3.model.DiagnosticReport; -import org.hl7.fhir.dstu3.model.EnumFactory; -import org.hl7.fhir.dstu3.model.Enumeration; import org.hl7.fhir.dstu3.model.Enumerations.AdministrativeGender; -import org.hl7.fhir.dstu3.model.ExplanationOfBenefit; -import org.hl7.fhir.dstu3.model.Extension; -import org.hl7.fhir.dstu3.model.HumanName; -import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.Identifier.IdentifierUse; -import org.hl7.fhir.dstu3.model.Linkage; -import org.hl7.fhir.dstu3.model.Medication; -import org.hl7.fhir.dstu3.model.MedicationRequest; -import org.hl7.fhir.dstu3.model.Observation; import org.hl7.fhir.dstu3.model.Observation.ObservationStatus; -import org.hl7.fhir.dstu3.model.Parameters; -import org.hl7.fhir.dstu3.model.Patient; -import org.hl7.fhir.dstu3.model.PrimitiveType; -import org.hl7.fhir.dstu3.model.Quantity; -import org.hl7.fhir.dstu3.model.QuestionnaireResponse; -import org.hl7.fhir.dstu3.model.Reference; -import org.hl7.fhir.dstu3.model.RelatedPerson; -import org.hl7.fhir.dstu3.model.SampledData; -import org.hl7.fhir.dstu3.model.SimpleQuantity; -import org.hl7.fhir.dstu3.model.StringType; -import org.hl7.fhir.dstu3.model.UriType; -import org.hl7.fhir.dstu3.model.ValueSet; import org.hl7.fhir.utilities.xhtml.XhtmlNode; import org.junit.After; import org.junit.AfterClass; @@ -115,6 +79,35 @@ public class JsonParserDstu3Test { ourCtx.setNarrativeGenerator(null); } + /** + * See #544 + */ + @Test + public void testBundleStitchReferencesByUuid() throws Exception { + Bundle bundle = new Bundle(); + + DocumentManifest dm = new DocumentManifest(); + dm.getSubject().setReference("urn:uuid:96e85cca-9797-45d6-834a-c4eb27f331d3"); + bundle.addEntry().setResource(dm); + + Patient patient = new Patient(); + patient.addName().setFamily("FAMILY"); + bundle.addEntry().setResource(patient).setFullUrl("urn:uuid:96e85cca-9797-45d6-834a-c4eb27f331d3"); + + String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle); + ourLog.info(encoded); + + bundle = ourCtx.newJsonParser().parseResource(Bundle.class, encoded); + dm = (DocumentManifest) bundle.getEntry().get(0).getResource(); + + assertEquals("urn:uuid:96e85cca-9797-45d6-834a-c4eb27f331d3", dm.getSubject().getReference()); + + Patient subject = (Patient) dm.getSubject().getResource(); + assertNotNull(subject); + assertEquals("FAMILY", subject.getNameFirstRep().getFamily()); + } + + @Test public void testIncorrectJsonTypesIdAndArray() { @@ -1660,8 +1653,8 @@ public class JsonParserDstu3Test { ourLog.info(encoded); assertEquals("urn:uuid:180f219f-97a8-486d-99d9-ed631fe4fc57", parsed.getEntry().get(0).getResource().getIdElement().getValue()); - assertEquals("urn:uuid:", parsed.getEntry().get(0).getResource().getIdElement().getBaseUrl()); - assertEquals("180f219f-97a8-486d-99d9-ed631fe4fc57", parsed.getEntry().get(0).getResource().getIdElement().getIdPart()); + assertEquals(null, parsed.getEntry().get(0).getResource().getIdElement().getBaseUrl()); + assertEquals("urn:uuid:180f219f-97a8-486d-99d9-ed631fe4fc57", parsed.getEntry().get(0).getResource().getIdElement().getIdPart()); assertThat(encoded, not(containsString("\"id\":\"180f219f-97a8-486d-99d9-ed631fe4fc57\""))); } diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/XmlParserDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/XmlParserDstu3Test.java index 1b00e00eca8..32921ead1ca 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/XmlParserDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/XmlParserDstu3Test.java @@ -77,155 +77,33 @@ public class XmlParserDstu3Test { } /** - * See #414 + * See #544 */ @Test - public void testParseXmlExtensionWithoutUrl() { - //@formatter:off - String input = "\n" + - " \n" + - " \n" + - " \n" + - ""; - //@formatter:on - - IParser parser = ourCtx.newXmlParser(); - parser.setParserErrorHandler(new LenientErrorHandler()); - Patient parsed = (Patient) parser.parseResource(input); - assertEquals(1, parsed.getExtension().size()); - assertEquals(null, parsed.getExtension().get(0).getUrl()); - assertEquals("2011-01-02T11:13:15", parsed.getExtension().get(0).getValueAsPrimitive().getValueAsString()); - - try { - parser = ourCtx.newXmlParser(); - parser.setParserErrorHandler(new StrictErrorHandler()); - parser.parseResource(input); - fail(); - } catch (DataFormatException e) { - assertEquals("Resource is missing required element 'url' in parent element 'extension'", e.getCause().getMessage()); - } + public void testBundleStitchReferencesByUuid() throws Exception { + Bundle bundle = new Bundle(); - } - - - /** - * See #414 - */ - @Test - public void testParseXmlModifierExtensionWithoutUrl() { - //@formatter:off - String input = "\n" + - " \n" + - " \n" + - " \n" + - ""; - //@formatter:on - - IParser parser = ourCtx.newXmlParser(); - parser.setParserErrorHandler(new LenientErrorHandler()); - Patient parsed = (Patient) parser.parseResource(input); - assertEquals(1, parsed.getModifierExtension().size()); - assertEquals(null, parsed.getModifierExtension().get(0).getUrl()); - assertEquals("2011-01-02T11:13:15", parsed.getModifierExtension().get(0).getValueAsPrimitive().getValueAsString()); - - try { - parser = ourCtx.newXmlParser(); - parser.setParserErrorHandler(new StrictErrorHandler()); - parser.parseResource(input); - fail(); - } catch (DataFormatException e) { - assertEquals("Resource is missing required element 'url' in parent element 'modifierExtension'", e.getCause().getMessage()); - } + DocumentManifest dm = new DocumentManifest(); + dm.getSubject().setReference("urn:uuid:96e85cca-9797-45d6-834a-c4eb27f331d3"); + bundle.addEntry().setResource(dm); - } - - @Test - public void testEncodeChainedContainedResourcer() { - Organization gp = new Organization(); - gp.setName("grandparent"); - - Organization parent = new Organization(); - parent.setName("parent"); - parent.getPartOf().setResource(gp); - - Organization child = new Organization(); - child.setName("child"); - child.getPartOf().setResource(parent); - Patient patient = new Patient(); - patient.getManagingOrganization().setResource(child); - - String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient); + patient.addName().setFamily("FAMILY"); + bundle.addEntry().setResource(patient).setFullUrl("urn:uuid:96e85cca-9797-45d6-834a-c4eb27f331d3"); + + String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(bundle); ourLog.info(encoded); - - patient = ourCtx.newXmlParser().parseResource(Patient.class, encoded); - - child = (Organization) patient.getManagingOrganization().getResource(); - assertEquals("child", child.getName()); - - parent = (Organization) child.getPartOf().getResource(); - assertEquals("parent", parent.getName()); - - gp = (Organization) parent.getPartOf().getResource(); - assertEquals("grandparent", gp.getName()); + + bundle = ourCtx.newXmlParser().parseResource(Bundle.class, encoded); + dm = (DocumentManifest) bundle.getEntry().get(0).getResource(); + + assertEquals("urn:uuid:96e85cca-9797-45d6-834a-c4eb27f331d3", dm.getSubject().getReference()); + + Patient subject = (Patient) dm.getSubject().getResource(); + assertNotNull(subject); + assertEquals("FAMILY", subject.getNameFirstRep().getFamily()); } - - /** - * If a contained resource refers to a contained resource that comes after it, it should still be successfully - * woven together. - */ - @Test - public void testParseWovenContainedResources() throws IOException { - String string = IOUtils.toString(getClass().getResourceAsStream("/bundle_with_woven_obs.xml"), StandardCharsets.UTF_8); - - IParser parser = ourCtx.newXmlParser(); - parser.setParserErrorHandler(new StrictErrorHandler()); - org.hl7.fhir.dstu3.model.Bundle bundle = parser.parseResource(Bundle.class, string); - - DiagnosticReport resource = (DiagnosticReport) bundle.getEntry().get(0).getResource(); - Observation obs = (Observation) resource.getResult().get(1).getResource(); - assertEquals("#2", obs.getId()); - Reference performerFirstRep = obs.getPerformerFirstRep(); - Practitioner performer = (Practitioner) performerFirstRep.getResource(); - assertEquals("#3", performer.getId()); - } - - @Test(expected = DataFormatException.class) - public void testContainedResourceWithNoId() throws IOException { - String string = IOUtils.toString(getClass().getResourceAsStream("/bundle_with_contained_with_no_id.xml"), StandardCharsets.UTF_8); - - IParser parser = ourCtx.newXmlParser(); - parser.setParserErrorHandler(new StrictErrorHandler()); - parser.parseResource(Bundle.class, string); - } - - @Test() - public void testContainedResourceWithNoIdLenient() throws IOException { - String string = IOUtils.toString(getClass().getResourceAsStream("/bundle_with_contained_with_no_id.xml"), StandardCharsets.UTF_8); - - IParser parser = ourCtx.newXmlParser(); - parser.setParserErrorHandler(new LenientErrorHandler()); - parser.parseResource(Bundle.class, string); - } - - @Test(expected = DataFormatException.class) - public void testParseWithInvalidLocalRef() throws IOException { - String string = IOUtils.toString(getClass().getResourceAsStream("/bundle_with_invalid_contained_ref.xml"), StandardCharsets.UTF_8); - - IParser parser = ourCtx.newXmlParser(); - parser.setParserErrorHandler(new StrictErrorHandler()); - parser.parseResource(Bundle.class, string); - } - - @Test() - public void testParseWithInvalidLocalRefLenient() throws IOException { - String string = IOUtils.toString(getClass().getResourceAsStream("/bundle_with_invalid_contained_ref.xml"), StandardCharsets.UTF_8); - - IParser parser = ourCtx.newXmlParser(); - parser.setParserErrorHandler(new LenientErrorHandler()); - parser.parseResource(Bundle.class, string); - } - + @Test public void testBundleWithBinary() { //@formatter:off @@ -257,6 +135,7 @@ public class XmlParserDstu3Test { } + @Test public void testContainedResourceInExtensionUndeclared() { Patient p = new Patient(); @@ -279,6 +158,24 @@ public class XmlParserDstu3Test { assertEquals("ORG", o.getName()); } + @Test(expected = DataFormatException.class) + public void testContainedResourceWithNoId() throws IOException { + String string = IOUtils.toString(getClass().getResourceAsStream("/bundle_with_contained_with_no_id.xml"), StandardCharsets.UTF_8); + + IParser parser = ourCtx.newXmlParser(); + parser.setParserErrorHandler(new StrictErrorHandler()); + parser.parseResource(Bundle.class, string); + } + + @Test() + public void testContainedResourceWithNoIdLenient() throws IOException { + String string = IOUtils.toString(getClass().getResourceAsStream("/bundle_with_contained_with_no_id.xml"), StandardCharsets.UTF_8); + + IParser parser = ourCtx.newXmlParser(); + parser.setParserErrorHandler(new LenientErrorHandler()); + parser.parseResource(Bundle.class, string); + } + @Test public void testDuration() { Encounter enc = new Encounter(); @@ -1023,6 +920,37 @@ public class XmlParserDstu3Test { assertThat(encoded, not(stringContainsInOrder("
John SMITH ")); } - - @Test public void testMoreExtensions() throws Exception { @@ -2176,6 +2102,8 @@ public class XmlParserDstu3Test { } + + @Test public void testParseAndEncodeCommentsOnExtensions() { //@formatter:off @@ -3105,6 +3033,106 @@ public class XmlParserDstu3Test { } } + @Test(expected = DataFormatException.class) + public void testParseWithInvalidLocalRef() throws IOException { + String string = IOUtils.toString(getClass().getResourceAsStream("/bundle_with_invalid_contained_ref.xml"), StandardCharsets.UTF_8); + + IParser parser = ourCtx.newXmlParser(); + parser.setParserErrorHandler(new StrictErrorHandler()); + parser.parseResource(Bundle.class, string); + } + + @Test() + public void testParseWithInvalidLocalRefLenient() throws IOException { + String string = IOUtils.toString(getClass().getResourceAsStream("/bundle_with_invalid_contained_ref.xml"), StandardCharsets.UTF_8); + + IParser parser = ourCtx.newXmlParser(); + parser.setParserErrorHandler(new LenientErrorHandler()); + parser.parseResource(Bundle.class, string); + } + + /** + * If a contained resource refers to a contained resource that comes after it, it should still be successfully + * woven together. + */ + @Test + public void testParseWovenContainedResources() throws IOException { + String string = IOUtils.toString(getClass().getResourceAsStream("/bundle_with_woven_obs.xml"), StandardCharsets.UTF_8); + + IParser parser = ourCtx.newXmlParser(); + parser.setParserErrorHandler(new StrictErrorHandler()); + org.hl7.fhir.dstu3.model.Bundle bundle = parser.parseResource(Bundle.class, string); + + DiagnosticReport resource = (DiagnosticReport) bundle.getEntry().get(0).getResource(); + Observation obs = (Observation) resource.getResult().get(1).getResource(); + assertEquals("#2", obs.getId()); + Reference performerFirstRep = obs.getPerformerFirstRep(); + Practitioner performer = (Practitioner) performerFirstRep.getResource(); + assertEquals("#3", performer.getId()); + } + + /** + * See #414 + */ + @Test + public void testParseXmlExtensionWithoutUrl() { + //@formatter:off + String input = "\n" + + " \n" + + " \n" + + " \n" + + ""; + //@formatter:on + + IParser parser = ourCtx.newXmlParser(); + parser.setParserErrorHandler(new LenientErrorHandler()); + Patient parsed = (Patient) parser.parseResource(input); + assertEquals(1, parsed.getExtension().size()); + assertEquals(null, parsed.getExtension().get(0).getUrl()); + assertEquals("2011-01-02T11:13:15", parsed.getExtension().get(0).getValueAsPrimitive().getValueAsString()); + + try { + parser = ourCtx.newXmlParser(); + parser.setParserErrorHandler(new StrictErrorHandler()); + parser.parseResource(input); + fail(); + } catch (DataFormatException e) { + assertEquals("Resource is missing required element 'url' in parent element 'extension'", e.getCause().getMessage()); + } + + } + + /** + * See #414 + */ + @Test + public void testParseXmlModifierExtensionWithoutUrl() { + //@formatter:off + String input = "\n" + + " \n" + + " \n" + + " \n" + + ""; + //@formatter:on + + IParser parser = ourCtx.newXmlParser(); + parser.setParserErrorHandler(new LenientErrorHandler()); + Patient parsed = (Patient) parser.parseResource(input); + assertEquals(1, parsed.getModifierExtension().size()); + assertEquals(null, parsed.getModifierExtension().get(0).getUrl()); + assertEquals("2011-01-02T11:13:15", parsed.getModifierExtension().get(0).getValueAsPrimitive().getValueAsString()); + + try { + parser = ourCtx.newXmlParser(); + parser.setParserErrorHandler(new StrictErrorHandler()); + parser.parseResource(input); + fail(); + } catch (DataFormatException e) { + assertEquals("Resource is missing required element 'url' in parent element 'modifierExtension'", e.getCause().getMessage()); + } + + } + /** * See #339 * diff --git a/hapi-fhir-structures-dstu3/src/test/resources/bundle_ref_by_uuid_544.xml b/hapi-fhir-structures-dstu3/src/test/resources/bundle_ref_by_uuid_544.xml new file mode 100644 index 00000000000..8fe8320c489 --- /dev/null +++ b/hapi-fhir-structures-dstu3/src/test/resources/bundle_ref_by_uuid_544.xml @@ -0,0 +1,174 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 1aed599ba27..2451ef541d2 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -222,6 +222,17 @@ return an HTTP 404 and not a 400, per the HTTP spec. Thanks to GitHub user @CarthageKing for the pull request! + + When parsing a Bundle containing placeholder fullUrls and references + (e.g. "urn:uuid:0000-0000") the resource reference targets did not get + populated with the given resources. Note that as a part of this + change, IdType and IdDt]]> have been modified + so that when parsing a placeholder ID, the complete placeholder including the + "urn:uuid:" or "urn:oid:" prefix will be placed into the ID part. Previously, + the prefix was treated as the base URL, which led to strange behaviour + like the placeholder being treated as a real IDs. Thanks to GitHub + user @jodue for reporting! +