From 6f7ef96b975375a8542a125c61f3bb2d34f8139a Mon Sep 17 00:00:00 2001
From: James Agnew
Date: Tue, 16 Jun 2015 11:56:30 -0400
Subject: [PATCH] Support $validate operation correctly in DSTU2 clients and in
testpage overlay
---
.../ca/uhn/fhir/model/primitive/IdDt.java | 55 +-
.../ca/uhn/fhir/rest/client/BaseClient.java | 2 +-
.../rest/client/BaseHttpClientInvocation.java | 2 +-
.../uhn/fhir/rest/client/GenericClient.java | 20 +-
.../BaseHttpClientInvocationWithContents.java | 6 +-
.../method/HttpDeleteClientInvocation.java | 2 +-
.../rest/method/HttpGetClientInvocation.java | 2 +-
.../method/HttpSimpleGetClientInvocation.java | 2 +-
.../uhn/fhir/rest/param/ReferenceParam.java | 6 +-
.../java/ca/uhn/fhir/jpa/dao/BaseFhirDao.java | 4 +-
.../uhn/fhir/jpa/dao/BaseFhirResourceDao.java | 19 +-
.../jpa/dao/FhirBundleResourceDaoDstu2.java | 24 +
.../uhn/fhir/jpa/dao/FhirSystemDaoDstu1.java | 18 +-
.../uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java | 33 +-
.../ca/uhn/fhir/jpa/entity/ResourceLink.java | 8 +-
.../provider/JpaResourceProviderDstu2.java | 2 +-
.../jpa/dao/FhirResourceDaoDstu2Test.java | 6 +
.../fhir/jpa/dao/FhirSystemDaoDstu1Test.java | 5 +-
.../fhir/jpa/dao/FhirSystemDaoDstu2Test.java | 4 +-
.../provider/ResourceProviderDstu2Test.java | 29 ++
.../src/test/resources/document-father.json | 475 ++++++++++++++++++
.../ca/uhn/fhir/model/primitive/IdDtTest.java | 43 +-
.../ca/uhn/fhir/parser/JsonParserTest.java | 2 +-
.../ca/uhn/fhir/parser/XmlParserTest.java | 2 +
.../uhn/fhir/parser/JsonParserDstu2Test.java | 92 +++-
.../rest/client/GenericClientDstu2Test.java | 10 +-
.../org/hl7/fhir/instance/model/IdType.java | 53 +-
.../java/ca/uhn/fhir/model/IdTypeTest.java | 226 +++++++++
.../main/java/ca/uhn/fhir/to/Controller.java | 5 +-
.../fhir/tinder/TinderJpaRestServerMojo.java | 3 -
.../resources/vm/jpa_resource_provider.vm | 5 +-
.../src/main/resources/vm/jpa_spring_beans.vm | 7 +-
restful-server-example/pom.xml | 2 +-
33 files changed, 1076 insertions(+), 98 deletions(-)
create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirBundleResourceDaoDstu2.java
create mode 100644 hapi-fhir-jpaserver-base/src/test/resources/document-father.json
create mode 100644 hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/model/IdTypeTest.java
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 ba9a72e7c45..5bc0efa6448 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
@@ -283,6 +283,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;
+ }
+
StringBuilder b = new StringBuilder();
if (isNotBlank(myBaseUrl)) {
b.append(myBaseUrl);
@@ -395,11 +400,15 @@ 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
+ * or it begins with "cid:" or "urn:")
*/
@Override
public boolean isLocal() {
- return myUnqualifiedId != null && myUnqualifiedId.isEmpty() == false && myUnqualifiedId.charAt(0) == '#';
+ if (myBaseUrl == null) {
+ return false;
+ }
+ return "#".equals(myBaseUrl) || myBaseUrl.equals("cid:") || myBaseUrl.startsWith("urn:");
}
/**
@@ -410,6 +419,34 @@ public class IdDt extends UriDt implements IPrimitiveDatatype, IIdType {
setValue(theId.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;
+ }
+ }
+
/**
* Set the value
*
@@ -426,15 +463,25 @@ 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);
myUnqualifiedId = null;
myUnqualifiedVersionId = null;
myResourceType = null;
- } else if (theValue.charAt(0) == '#') {
+ } else if (theValue.charAt(0) == '#' && theValue.length() > 1) {
super.setValue(theValue);
- myUnqualifiedId = theValue;
+ myBaseUrl = "#";
+ myUnqualifiedId = theValue.substring(1);
+ myUnqualifiedVersionId = null;
+ myResourceType = null;
+ myHaveComponentParts = true;
+ } else if (localPrefix != null) {
+ myBaseUrl = localPrefix;
+ myUnqualifiedId = theValue.substring(localPrefix.length());
myUnqualifiedVersionId = null;
myResourceType = null;
myHaveComponentParts = true;
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseClient.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseClient.java
index 08cbd27f5b2..b08cb731599 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseClient.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseClient.java
@@ -186,7 +186,7 @@ public abstract class BaseClient implements IRestfulClient {
encoding=theEncoding;
}
- httpRequest = clientInvocation.asHttpRequest(myUrlBase, params, encoding);
+ httpRequest = clientInvocation.asHttpRequest(myUrlBase, params, encoding, thePrettyPrint);
if (theLogRequestAndResponse) {
ourLog.info("Client invoking: {}", httpRequest);
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseHttpClientInvocation.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseHttpClientInvocation.java
index 0cd4a316e8e..7c4a708695f 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseHttpClientInvocation.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseHttpClientInvocation.java
@@ -56,7 +56,7 @@ public abstract class BaseHttpClientInvocation {
* The encoding to use for any serialized content sent to the
* server
*/
- public abstract HttpRequestBase asHttpRequest(String theUrlBase, Map> theExtraParams, EncodingEnum theEncoding);
+ public abstract HttpRequestBase asHttpRequest(String theUrlBase, Map> theExtraParams, EncodingEnum theEncoding, Boolean thePrettyPrint);
protected static void appendExtraParamsWithQuestionMark(Map> theExtraParams, StringBuilder theUrlBuilder, boolean theWithQuestionMark) {
if (theExtraParams == null) {
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java
index 519df9085dd..06f3595fa49 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java
@@ -158,7 +158,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
HttpGetClientInvocation invocation = MethodUtil.createConformanceInvocation();
if (isKeepResponses()) {
- myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
+ myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint());
}
@SuppressWarnings("unchecked")
@@ -178,7 +178,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
public MethodOutcome create(IResource theResource) {
BaseHttpClientInvocation invocation = MethodUtil.createCreateInvocation(theResource, myContext);
if (isKeepResponses()) {
- myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
+ myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint());
}
RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource);
@@ -200,7 +200,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
public MethodOutcome delete(final Class extends IResource> theType, IdDt theId) {
HttpDeleteClientInvocation invocation = DeleteMethodBinding.createDeleteInvocation(theId.withResourceType(toResourceName(theType)));
if (isKeepResponses()) {
- myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
+ myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint());
}
final String resourceName = myContext.getResourceDefinition(theType).getName();
@@ -236,7 +236,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
}
if (isKeepResponses()) {
- myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
+ myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint());
}
if (theIfVersionMatches != null) {
@@ -312,7 +312,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
String id = theIdDt != null && theIdDt.isEmpty() == false ? theIdDt.getValue() : null;
HttpGetClientInvocation invocation = HistoryMethodBinding.createHistoryInvocation(resourceName, id, theSince, theLimit);
if (isKeepResponses()) {
- myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
+ myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint());
}
BundleResponseHandler binding = new BundleResponseHandler(theType);
@@ -430,7 +430,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
BaseHttpClientInvocation invocation = SearchMethodBinding.createSearchInvocation(myContext, toResourceName(theType), params, null, null, null);
if (isKeepResponses()) {
- myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
+ myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint());
}
BundleResponseHandler binding = new BundleResponseHandler(theType);
@@ -474,7 +474,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
public List transaction(List theResources) {
BaseHttpClientInvocation invocation = TransactionMethodBinding.createTransactionInvocation(theResources, myContext);
if (isKeepResponses()) {
- myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
+ myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint());
}
Bundle resp = invokeClient(myContext, new BundleResponseHandler(null), invocation, myLogRequestAndResponse);
@@ -491,7 +491,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
public MethodOutcome update(IdDt theIdDt, IResource theResource) {
BaseHttpClientInvocation invocation = MethodUtil.createUpdateInvocation(theResource, null, theIdDt, myContext);
if (isKeepResponses()) {
- myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
+ myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint());
}
RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource);
@@ -522,7 +522,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
if (isKeepResponses()) {
- myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
+ myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint());
}
RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource);
@@ -601,7 +601,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
// }
if (isKeepResponses()) {
- myLastRequest = theInvocation.asHttpRequest(getServerBase(), theParams, getEncoding());
+ myLastRequest = theInvocation.asHttpRequest(getServerBase(), theParams, getEncoding(), myPrettyPrint);
}
Z resp = invokeClient(myContext, theHandler, theInvocation, myParamEncoding, myPrettyPrint, myQueryLogRequestAndResponse || myLogRequestAndResponse);
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseHttpClientInvocationWithContents.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseHttpClientInvocationWithContents.java
index 9f4a05c5898..00823befecb 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseHttpClientInvocationWithContents.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseHttpClientInvocationWithContents.java
@@ -188,7 +188,7 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
}
@Override
- public HttpRequestBase asHttpRequest(String theUrlBase, Map> theExtraParams, EncodingEnum theEncoding) throws DataFormatException {
+ public HttpRequestBase asHttpRequest(String theUrlBase, Map> theExtraParams, EncodingEnum theEncoding, Boolean thePrettyPrint) throws DataFormatException {
StringBuilder url = new StringBuilder();
if (myUrlPath == null) {
@@ -236,6 +236,10 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
parser = myContext.newXmlParser();
}
+ if (thePrettyPrint != null) {
+ parser.setPrettyPrint(thePrettyPrint);
+ }
+
parser.setOmitResourceId(myOmitResourceId);
AbstractHttpEntity entity;
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/HttpDeleteClientInvocation.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/HttpDeleteClientInvocation.java
index 02471c13751..374c0d66501 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/HttpDeleteClientInvocation.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/HttpDeleteClientInvocation.java
@@ -50,7 +50,7 @@ public class HttpDeleteClientInvocation extends BaseHttpClientInvocation {
}
@Override
- public HttpRequestBase asHttpRequest(String theUrlBase, Map> theExtraParams, EncodingEnum theEncoding) {
+ public HttpRequestBase asHttpRequest(String theUrlBase, Map> theExtraParams, EncodingEnum theEncoding, Boolean thePrettyPrint) {
StringBuilder b = new StringBuilder();
b.append(theUrlBase);
if (!theUrlBase.endsWith("/")) {
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/HttpGetClientInvocation.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/HttpGetClientInvocation.java
index e76370c58c4..b9276db009f 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/HttpGetClientInvocation.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/HttpGetClientInvocation.java
@@ -79,7 +79,7 @@ public class HttpGetClientInvocation extends BaseHttpClientInvocation {
}
@Override
- public HttpGet asHttpRequest(String theUrlBase, Map> theExtraParams, EncodingEnum theEncoding) {
+ public HttpGet asHttpRequest(String theUrlBase, Map> theExtraParams, EncodingEnum theEncoding, Boolean thePrettyPrint) {
StringBuilder b = new StringBuilder();
if (!myUrlPath.contains("://")) {
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/HttpSimpleGetClientInvocation.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/HttpSimpleGetClientInvocation.java
index 4aabade3d47..7a68a8497c4 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/HttpSimpleGetClientInvocation.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/HttpSimpleGetClientInvocation.java
@@ -38,7 +38,7 @@ public class HttpSimpleGetClientInvocation extends BaseHttpClientInvocation {
}
@Override
- public HttpRequestBase asHttpRequest(String theUrlBase, Map> theExtraParams, EncodingEnum theEncoding) {
+ public HttpRequestBase asHttpRequest(String theUrlBase, Map> theExtraParams, EncodingEnum theEncoding, Boolean thePrettyPrint) {
HttpGet retVal = new HttpGet(myUrl);
super.addHeadersToRequest(retVal);
return retVal;
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ReferenceParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ReferenceParam.java
index 6e4f2899319..62422f01d0f 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ReferenceParam.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ReferenceParam.java
@@ -85,7 +85,11 @@ public class ReferenceParam extends IdDt implements IQueryParameterType {
if (myBase.getMissing()!=null) {
return myBase.getValueAsQueryToken();
}
- return getIdPart();
+ if (isLocal()) {
+ return getValue();
+ } else {
+ return getIdPart();
+ }
}
public void setChain(String theChain) {
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseFhirDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseFhirDao.java
index 767306b7d2e..e6881c8bbdc 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseFhirDao.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseFhirDao.java
@@ -1048,8 +1048,8 @@ public abstract class BaseFhirDao implements IDao {
quantityParams = extractSearchParamQuantity(entity, theResource);
dateParams = extractSearchParamDates(entity, theResource);
- ourLog.info("Indexing resource: {}", entity.getId());
- ourLog.info("Storing string indexes: {}", stringParams);
+// ourLog.info("Indexing resource: {}", entity.getId());
+ ourLog.trace("Storing string indexes: {}", stringParams);
tokenParams = new ArrayList();
for (BaseResourceIndexedSearchParam next : extractSearchParamTokens(entity, theResource)) {
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseFhirResourceDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseFhirResourceDao.java
index fb637cf7e35..41b20daca1e 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseFhirResourceDao.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseFhirResourceDao.java
@@ -574,12 +574,12 @@ public abstract class BaseFhirResourceDao extends BaseFhirD
ReferenceParam ref = (ReferenceParam) params;
String resourceId = ref.getValueAsQueryToken();
- if (resourceId.contains("/")) {
- IdDt dt = new IdDt(resourceId);
- resourceId = dt.getIdPart();
- }
if (isBlank(ref.getChain())) {
+ if (resourceId.contains("/")) {
+ IdDt dt = new IdDt(resourceId);
+ resourceId = dt.getIdPart();
+ }
Long targetPid = translateForcedIdToPid(new IdDt(resourceId));
ourLog.info("Searching for resource link with target PID: {}", targetPid);
Predicate eq = builder.equal(from.get("myTargetResourcePid"), targetPid);
@@ -1097,6 +1097,9 @@ public abstract class BaseFhirResourceDao extends BaseFhirD
private DaoMethodOutcome doCreate(T theResource, String theIfNoneExist, boolean thePerformIndexing) {
StopWatch w = new StopWatch();
+
+ preProcessResourceForStorage(theResource);
+
ResourceTable entity = new ResourceTable();
entity.setResourceType(toResourceName(theResource));
@@ -1112,7 +1115,7 @@ public abstract class BaseFhirResourceDao extends BaseFhirD
}
}
- if (theResource.getId().isEmpty() == false) {
+ if (isNotBlank(theResource.getId().getIdPart())) {
if (isValidPid(theResource.getId())) {
throw new UnprocessableEntityException(
"This server cannot create an entity with a user-specified numeric ID - Client should not specify an ID when creating a new resource, or should include at least one letter in the ID to force a client-defined ID");
@@ -1139,6 +1142,10 @@ public abstract class BaseFhirResourceDao extends BaseFhirD
return outcome;
}
+ protected void preProcessResourceForStorage(T theResource) {
+ // nothing by default
+ }
+
@Override
public TagList getAllResourceTags() {
StopWatch w = new StopWatch();
@@ -1995,6 +2002,8 @@ public abstract class BaseFhirResourceDao extends BaseFhirD
public DaoMethodOutcome update(T theResource, String theMatchUrl, boolean thePerformIndexing) {
StopWatch w = new StopWatch();
+ preProcessResourceForStorage(theResource);
+
final ResourceTable entity;
IdDt resourceId;
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirBundleResourceDaoDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirBundleResourceDaoDstu2.java
new file mode 100644
index 00000000000..238b4fac512
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirBundleResourceDaoDstu2.java
@@ -0,0 +1,24 @@
+package ca.uhn.fhir.jpa.dao;
+
+import ca.uhn.fhir.model.dstu2.resource.Bundle;
+import ca.uhn.fhir.model.dstu2.valueset.BundleTypeEnum;
+import ca.uhn.fhir.model.primitive.UriDt;
+import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
+
+public class FhirBundleResourceDaoDstu2 extends FhirResourceDaoDstu2 {
+
+ @Override
+ protected void preProcessResourceForStorage(Bundle theResource) {
+ super.preProcessResourceForStorage(theResource);
+
+ if (theResource.getTypeElement().getValueAsEnum() != BundleTypeEnum.DOCUMENT) {
+ String message = "Unable to store a Bundle resource on this server with a Bundle.type value other than '" + BundleTypeEnum.DOCUMENT.getCode() + "' - Value was: " + (theResource.getTypeElement().getValueAsEnum() != null ? theResource.getTypeElement().getValueAsEnum().getCode() : "(missing)");
+ throw new UnprocessableEntityException(message);
+ }
+
+ theResource.setBase((UriDt)null);
+ }
+
+
+
+}
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 77c29af3927..7bfc0566cf2 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
@@ -60,14 +60,18 @@ public class FhirSystemDaoDstu1 extends BaseFhirSystemDao> {
for (int i = 0; i < theResources.size(); i++) {
IResource res = theResources.get(i);
- if (res.getId().hasIdPart() && !res.getId().hasResourceType()) {
+ if (res.getId().hasIdPart() && !res.getId().hasResourceType() && !res.getId().isLocal()) {
res.setId(new IdDt(toResourceName(res.getClass()), res.getId().getIdPart()));
}
/*
* Ensure that the bundle doesn't have any duplicates, since this causes all kinds of weirdness
*/
- if (res.getId().hasResourceType() && res.getId().hasIdPart()) {
+ if (res.getId().isLocal()) {
+ if (!allIds.add(res.getId())) {
+ throw new InvalidRequestException("Transaction bundle contains multiple resources with ID: " + res.getId());
+ }
+ } else if (res.getId().hasResourceType() && res.getId().hasIdPart()) {
IdDt nextId = res.getId().toUnqualifiedVersionless();
if (!allIds.add(nextId)) {
throw new InvalidRequestException("Transaction bundle contains multiple resources with ID: " + nextId);
@@ -134,7 +138,7 @@ public class FhirSystemDaoDstu1 extends BaseFhirSystemDao> {
} else {
throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseFhirSystemDao.class, "transactionOperationWithMultipleMatchFailure", nextResouceOperationIn.name(), matchUrl, candidateMatches.size()));
}
- } else if (nextId.isEmpty()) {
+ } else if (nextId.isEmpty() || nextId.isLocal()) {
entity = null;
} else {
entity = tryToLoadEntity(nextId);
@@ -144,7 +148,7 @@ public class FhirSystemDaoDstu1 extends BaseFhirSystemDao> {
if (entity == null) {
nextResouceOperationOut = BundleEntryTransactionMethodEnum.POST;
entity = toEntity(nextResource);
- if (nextId.isEmpty() == false && nextId.getIdPart().startsWith("cid:")) {
+ if (nextId.isEmpty() == false && "cid:".equals(nextId.getBaseUrl())) {
ourLog.debug("Resource in transaction has ID[{}], will replace with server assigned ID", nextId.getIdPart());
} else if (nextResouceOperationIn == BundleEntryTransactionMethodEnum.POST) {
if (nextId.isEmpty() == false) {
@@ -209,11 +213,11 @@ public class FhirSystemDaoDstu1 extends BaseFhirSystemDao> {
if (nextId.toUnqualifiedVersionless().equals(newId)) {
ourLog.info("Transaction resource ID[{}] is being updated", newId);
} else {
- if (!nextId.getIdPart().startsWith("#")) {
- nextId = new IdDt(resourceName, nextId.getIdPart());
+ if (nextId.isLocal()) {
+// nextId = new IdDt(resourceName, nextId.getIdPart());
ourLog.info("Transaction resource ID[{}] has been assigned new ID[{}]", nextId, newId);
idConversions.put(nextId, newId);
- idConversions.put(new IdDt(nextId.getIdPart()), newId);
+ idConversions.put(new IdDt(resourceName + "/" + nextId.getValue()), newId);
}
}
}
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 40f785984eb..1d765b83ed6 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
@@ -151,7 +151,7 @@ public class FhirSystemDaoDstu2 extends BaseFhirSystemDao {
if (res != null) {
nextResourceId = res.getId();
- if (nextResourceId.hasIdPart() && !nextResourceId.hasResourceType()) {
+ if (nextResourceId.hasIdPart() && !nextResourceId.hasResourceType() && !nextResourceId.isLocal()) {
nextResourceId = new IdDt(toResourceName(res.getClass()), nextResourceId.getIdPart());
res.setId(nextResourceId);
}
@@ -159,7 +159,11 @@ public class FhirSystemDaoDstu2 extends BaseFhirSystemDao {
/*
* Ensure that the bundle doesn't have any duplicates, since this causes all kinds of weirdness
*/
- if (nextResourceId.hasResourceType() && nextResourceId.hasIdPart()) {
+ if (nextResourceId.isLocal()) {
+ if (!allIds.add(nextResourceId)) {
+ throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseFhirSystemDao.class, "transactionContainsMultipleWithDuplicateId", nextResourceId));
+ }
+ } else if (nextResourceId.hasResourceType() && nextResourceId.hasIdPart()) {
IdDt nextId = nextResourceId.toUnqualifiedVersionless();
if (!allIds.add(nextId)) {
throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseFhirSystemDao.class, "transactionContainsMultipleWithDuplicateId", nextId));
@@ -173,6 +177,8 @@ public class FhirSystemDaoDstu2 extends BaseFhirSystemDao {
throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseFhirSystemDao.class, "transactionEntryHasInvalidVerb", nextEntry.getTransaction().getMethod()));
}
+ String resourceType = res != null ? getContext().getResourceDefinition(res).getName() : null;
+
switch (verb) {
case POST: {
// CREATE
@@ -182,7 +188,7 @@ public class FhirSystemDaoDstu2 extends BaseFhirSystemDao {
DaoMethodOutcome outcome;
Entry newEntry = response.addEntry();
outcome = resourceDao.create(res, nextEntry.getTransaction().getIfNoneExist(), false);
- handleTransactionCreateOrUpdateOutcome(idSubstitutions, idToPersistedOutcome, nextResourceId, outcome, newEntry);
+ handleTransactionCreateOrUpdateOutcome(idSubstitutions, idToPersistedOutcome, nextResourceId, outcome, newEntry, resourceType);
break;
}
case DELETE: {
@@ -218,7 +224,7 @@ public class FhirSystemDaoDstu2 extends BaseFhirSystemDao {
outcome = resourceDao.update(res, parts.getResourceType() + '?' + parts.getParams(), false);
}
- handleTransactionCreateOrUpdateOutcome(idSubstitutions, idToPersistedOutcome, nextResourceId, outcome, newEntry);
+ handleTransactionCreateOrUpdateOutcome(idSubstitutions, idToPersistedOutcome, nextResourceId, outcome, newEntry, resourceType);
break;
}
case GET: {
@@ -249,7 +255,8 @@ public class FhirSystemDaoDstu2 extends BaseFhirSystemDao {
int configuredMax = 100; // this should probably be configurable or something
if (bundle.size() > configuredMax) {
- oo.addIssue().setSeverity(IssueSeverityEnum.WARNING).setDetails("Search nested within transaction found more than " + configuredMax + " matches, but paging is not supported in nested transactions");
+ oo.addIssue().setSeverity(IssueSeverityEnum.WARNING)
+ .setDetails("Search nested within transaction found more than " + configuredMax + " matches, but paging is not supported in nested transactions");
}
List resourcesToAdd = bundle.getResources(0, Math.min(bundle.size(), configuredMax));
for (IBaseResource next : resourcesToAdd) {
@@ -330,16 +337,18 @@ public class FhirSystemDaoDstu2 extends BaseFhirSystemDao {
return url;
}
- private static void handleTransactionCreateOrUpdateOutcome(Map idSubstitutions, Map idToPersistedOutcome, IdDt nextResourceId, DaoMethodOutcome outcome, Entry newEntry) {
+ private static void handleTransactionCreateOrUpdateOutcome(Map idSubstitutions, Map idToPersistedOutcome, IdDt nextResourceId, DaoMethodOutcome outcome,
+ Entry newEntry, String theResourceType) {
IdDt newId = outcome.getId().toUnqualifiedVersionless();
- IdDt resourceId = nextResourceId.toUnqualifiedVersionless();
+ IdDt resourceId = nextResourceId.isLocal() ? nextResourceId : nextResourceId.toUnqualifiedVersionless();
if (newId.equals(resourceId) == false) {
- /*
- * The correct way for substitution IDs to be is to be with no resource type, but we'll accept the qualified
- * kind too just to be lenient.
- */
idSubstitutions.put(resourceId, newId);
- idSubstitutions.put(resourceId.withResourceType(null), newId);
+ if (resourceId.isLocal()) {
+ /*
+ * The correct way for substitution IDs to be is to be with no resource type, but we'll accept the qualified kind too just to be lenient.
+ */
+ idSubstitutions.put(new IdDt(theResourceType + '/' + resourceId.getValue()), newId);
+ }
}
idToPersistedOutcome.put(newId, outcome);
if (outcome.getCreated().booleanValue()) {
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceLink.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceLink.java
index 1a63c7b9363..519088cf4c0 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceLink.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceLink.java
@@ -50,17 +50,17 @@ public class ResourceLink implements Serializable {
private String mySourcePath;
@ManyToOne(optional = false)
- @JoinColumn(name = "SRC_RESOURCE_ID", referencedColumnName="RES_ID")
+ @JoinColumn(name = "SRC_RESOURCE_ID", referencedColumnName="RES_ID", nullable=false)
private ResourceTable mySourceResource;
- @Column(name = "SRC_RESOURCE_ID", insertable = false, updatable = false)
+ @Column(name = "SRC_RESOURCE_ID", insertable = false, updatable = false, nullable=false)
private Long mySourceResourcePid;
@ManyToOne(optional = false)
- @JoinColumn(name = "TARGET_RESOURCE_ID", referencedColumnName="RES_ID")
+ @JoinColumn(name = "TARGET_RESOURCE_ID", referencedColumnName="RES_ID", nullable=false)
private ResourceTable myTargetResource;
- @Column(name = "TARGET_RESOURCE_ID", insertable = false, updatable = false)
+ @Column(name = "TARGET_RESOURCE_ID", insertable = false, updatable = false,nullable=false)
private Long myTargetResourcePid;
public ResourceLink() {
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaResourceProviderDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaResourceProviderDstu2.java
index 8d980424f5a..966e5ed933b 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaResourceProviderDstu2.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaResourceProviderDstu2.java
@@ -154,7 +154,7 @@ public class JpaResourceProviderDstu2 extends BaseJpaResour
}
@Validate
- public MethodOutcome validate(@ResourceParam T theResource, @ResourceParam String theRawResource, @ResourceParam EncodingEnum theEncoding, @Validate.Mode ValidationModeEnum theMode,
+ public MethodOutcome validate(@ResourceParam T theResource, @IdParam IdDt theId, @ResourceParam String theRawResource, @ResourceParam EncodingEnum theEncoding, @Validate.Mode ValidationModeEnum theMode,
@Validate.Profile String theProfile) {
final OperationOutcome oo = new OperationOutcome();
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2Test.java
index 70ae9d2c44d..330f98ea13a 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2Test.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2Test.java
@@ -1395,6 +1395,12 @@ public class FhirResourceDaoDstu2Test {
assertEquals(1, result.size());
assertEquals(obsId01.getIdPart(), result.get(0).getId().getIdPart());
+ result = toList(ourObservationDao.search(Observation.SP_PATIENT, new ReferenceParam(patientId01.getIdPart())));
+ assertEquals(1, result.size());
+
+ result = toList(ourObservationDao.search(Observation.SP_PATIENT, new ReferenceParam(patientId01.getIdPart())));
+ assertEquals(1, result.size());
+
result = toList(ourObservationDao.search(Observation.SP_SUBJECT, new ReferenceParam(Patient.SP_IDENTIFIER, "999999999999")));
assertEquals(0, result.size());
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu1Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu1Test.java
index f80558849a4..cf4971e8a3d 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu1Test.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu1Test.java
@@ -358,7 +358,10 @@ public class FhirSystemDaoDstu1Test {
List response = ourSystemDao.transaction(res);
- ourLog.info(ourFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(response.get(0)));
+ String encodeResourceToString = ourFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(response.get(0));
+ ourLog.info(encodeResourceToString);
+
+ assertThat(encodeResourceToString, not(containsString("smsp")));
}
/**
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2Test.java
index d467bd84964..5bd38e37d35 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2Test.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2Test.java
@@ -846,7 +846,7 @@ public class FhirSystemDaoDstu2Test {
assertEquals(OperationOutcome.class, resp.getEntry().get(0).getResource().getClass());
OperationOutcome outcome = (OperationOutcome) resp.getEntry().get(0).getResource();
- assertThat(outcome.getIssue().get(1).getDetails(), containsString("Placeholder resource ID \"Patient/urn:oid:0.1.2.3\" was replaced with permanent ID \"Patient/"));
+ assertThat(outcome.getIssue().get(1).getDetails(), containsString("Placeholder resource ID \"urn:oid:0.1.2.3\" was replaced with permanent ID \"Patient/"));
assertTrue(resp.getEntry().get(1).getTransactionResponse().getLocation(), new IdDt(resp.getEntry().get(1).getTransactionResponse().getLocation()).getIdPart().matches("^[0-9]+$"));
assertTrue(resp.getEntry().get(2).getTransactionResponse().getLocation(), new IdDt(resp.getEntry().get(2).getTransactionResponse().getLocation()).getIdPart().matches("^[0-9]+$"));
@@ -894,7 +894,7 @@ public class FhirSystemDaoDstu2Test {
assertEquals(OperationOutcome.class, resp.getEntry().get(0).getResource().getClass());
OperationOutcome outcome = (OperationOutcome) resp.getEntry().get(0).getResource();
- assertThat(outcome.getIssue().get(1).getDetails(), containsString("Placeholder resource ID \"Patient/urn:oid:0.1.2.3\" was replaced with permanent ID \"Patient/"));
+ assertThat(outcome.getIssue().get(1).getDetails(), containsString("Placeholder resource ID \"urn:oid:0.1.2.3\" was replaced with permanent ID \"Patient/"));
assertTrue(resp.getEntry().get(1).getTransactionResponse().getLocation(), new IdDt(resp.getEntry().get(1).getTransactionResponse().getLocation()).getIdPart().matches("^[0-9]+$"));
assertTrue(resp.getEntry().get(2).getTransactionResponse().getLocation(), new IdDt(resp.getEntry().get(2).getTransactionResponse().getLocation()).getIdPart().matches("^[0-9]+$"));
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java
index b22a7a716c8..2c57a05eb53 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java
@@ -82,6 +82,7 @@ import ca.uhn.fhir.rest.client.IGenericClient;
import ca.uhn.fhir.rest.client.ServerValidationModeEnum;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.gclient.IQuery;
+import ca.uhn.fhir.rest.gclient.IReadExecutable;
import ca.uhn.fhir.rest.gclient.StringClientParam;
import ca.uhn.fhir.rest.gclient.TokenClientParam;
import ca.uhn.fhir.rest.server.Constants;
@@ -410,6 +411,34 @@ public class ResourceProviderDstu2Test {
}
+ @Test
+ public void testBundleCreate() throws Exception {
+ IGenericClient client = ourClient;
+
+ String resBody = IOUtils.toString(ResourceProviderDstu2Test.class.getResource("/document-father.json"));
+ IdDt id = client.create().resource(resBody).execute().getId();
+
+ ourLog.info("Created: {}", id);
+
+ ca.uhn.fhir.model.dstu2.resource.Bundle bundle = client.read().resource(ca.uhn.fhir.model.dstu2.resource.Bundle.class).withId(id).execute();
+
+ ourLog.info(ourFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle));
+ }
+
+ @Test
+ public void testBundleCreateWithTypeTransaction() throws Exception {
+ IGenericClient client = ourClient;
+
+ String resBody = IOUtils.toString(ResourceProviderDstu2Test.class.getResource("/document-father.json"));
+ resBody = resBody.replace("\"type\": \"document\"", "\"type\": \"transaction\"");
+ try {
+ client.create().resource(resBody).execute().getId();
+ fail();
+ } catch (UnprocessableEntityException e) {
+ assertThat(e.getMessage(), containsString("Unable to store a Bundle resource on this server with a Bundle.type value other than 'document' - Value was: transaction"));
+ }
+ }
+
/**
* See #147
*/
diff --git a/hapi-fhir-jpaserver-base/src/test/resources/document-father.json b/hapi-fhir-jpaserver-base/src/test/resources/document-father.json
new file mode 100644
index 00000000000..923bb6ea805
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/test/resources/document-father.json
@@ -0,0 +1,475 @@
+{
+ "resourceType": "Bundle",
+ "meta": {
+ "lastUpdated": "2013-05-28T22:12:21Z",
+ "tag": [
+ {
+ "system": "http://hl7.org/fhir/tag",
+ "code": "document"
+ }
+ ]
+ },
+ "type": "document",
+ "base": "http://fhir.healthintersections.com.au/open",
+ "entry": [
+ {
+ "base": "urn:uuid:",
+ "resource": {
+ "resourceType": "Composition",
+ "id": "180f219f-97a8-486d-99d9-ed631fe4fc57",
+ "meta": {
+ "lastUpdated": "2013-05-28T22:12:21Z"
+ },
+ "text": {
+ "status": "generated",
+ "div": ""
+ },
+ "date": "2013-02-01T12:30:02Z",
+ "type": {
+ "coding": [
+ {
+ "system": "http://loinc.org",
+ "code": "28655-9"
+ }
+ ],
+ "text": "Discharge Summary from Responsible Clinician"
+ },
+ "status": "final",
+ "confidentiality": "N",
+ "subject": {
+ "reference": "http://fhir.healthintersections.com.au/open/Patient/d1",
+ "display": "Eve Everywoman"
+ },
+ "author": [
+ {
+ "reference": "Practitioner/example",
+ "display": "Doctor Dave"
+ }
+ ],
+ "encounter": {
+ "reference": "http://fhir.healthintersections.com.au/open/Encounter/doc-example"
+ },
+ "section": [
+ {
+ "title": "Reason for admission",
+ "content": {
+ "reference": "urn:uuid:d0dd51d3-3ab2-4c84-b697-a630c3e40e7a"
+ }
+ },
+ {
+ "title": "Medications on Discharge",
+ "content": {
+ "reference": "urn:uuid:673f8db5-0ffd-4395-9657-6da00420bbc1"
+ }
+ },
+ {
+ "title": "Known allergies",
+ "content": {
+ "reference": "urn:uuid:68f86194-e6e1-4f65-b64a-5314256f8d7b"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "resource": {
+ "resourceType": "Practitioner",
+ "id": "example",
+ "meta": {
+ "lastUpdated": "2013-05-05T16:13:03Z"
+ },
+ "text": {
+ "status": "generated",
+ "div": "\n \n
Dr Adam Careful is a Referring Practitioner for Acme Hospital from 1-Jan 2012 to 31-Mar\n 2012
\n \n
"
+ },
+ "identifier": [
+ {
+ "system": "http://www.acme.org/practitioners",
+ "value": "23"
+ }
+ ],
+ "name": {
+ "family": [
+ "Careful"
+ ],
+ "given": [
+ "Adam"
+ ],
+ "prefix": [
+ "Dr"
+ ]
+ },
+ "practitionerRole": [
+ {
+ "managingOrganization": {
+ "reference": "Organization/1"
+ },
+ "role": {
+ "coding": [
+ {
+ "system": "http://hl7.org/fhir/v2/0286",
+ "code": "RP"
+ }
+ ]
+ },
+ "period": {
+ "start": "2012-01-01",
+ "end": "2012-03-31"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "resource": {
+ "resourceType": "Patient",
+ "id": "d1",
+ "text": {
+ "status": "generated",
+ "div": "\n
Eve Everywoman
\n
"
+ },
+ "name": [
+ {
+ "text": "Eve Everywoman",
+ "family": [
+ "Everywoman1"
+ ],
+ "given": [
+ "Eve"
+ ]
+ }
+ ],
+ "telecom": [
+ {
+ "system": "phone",
+ "value": "555-555-2003",
+ "use": "work"
+ }
+ ],
+ "gender": "female",
+ "birthDate": "1955-01-06",
+ "address": [
+ {
+ "use": "home",
+ "line": [
+ "2222 Home Street"
+ ]
+ }
+ ],
+ "active": true
+ }
+ },
+ {
+ "resource": {
+ "resourceType": "Encounter",
+ "id": "doc-example",
+ "meta": {
+ "lastUpdated": "2013-05-05T16:13:03Z"
+ },
+ "text": {
+ "status": "generated",
+ "div": " Admitted to Orthopedics Service,\n Middlemore Hospital between Jan 20 and Feb ist 2013
"
+ },
+ "identifier": [
+ {
+ "value": "S100"
+ }
+ ],
+ "status": "finished",
+ "class": "inpatient",
+ "type": [
+ {
+ "text": "Orthopedic Admission"
+ }
+ ],
+ "patient": {
+ "reference": "Patient/d1"
+ },
+ "period": {
+ "start": "2013-01-20T12:30:02Z",
+ "end": "2013-02-01T12:30:02Z"
+ },
+ "hospitalization": {
+ "dischargeDisposition": {
+ "text": "Discharged to care of GP"
+ }
+ }
+ }
+ },
+ {
+ "base": "urn:uuid:",
+ "resource": {
+ "resourceType": "List",
+ "id": "d0dd51d3-3ab2-4c84-b697-a630c3e40e7a",
+ "meta": {
+ "lastUpdated": "2013-05-05T16:13:03Z"
+ },
+ "text": {
+ "status": "additional",
+ "div": "\n
\n \n \n Details | \n | \n
\n \n \n \n Acute Asthmatic attack. Was wheezing\n for days prior to admission. | \n | \n
\n \n
\n
"
+ },
+ "code": {
+ "coding": [
+ {
+ "system": "http://loinc.org",
+ "code": "8646-2",
+ "display": "Hospital admission diagnosis"
+ }
+ ]
+ },
+ "subject": {
+ "reference": "http://fhir.healthintersections.com.au/open/Patient/d1",
+ "display": "Peter Patient"
+ },
+ "status": "current",
+ "mode": "working",
+ "entry": [
+ {
+ "item": {
+ "reference": "urn:uuid:541a72a8-df75-4484-ac89-ac4923f03b81"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "base": "urn:uuid:",
+ "resource": {
+ "resourceType": "Observation",
+ "id": "541a72a8-df75-4484-ac89-ac4923f03b81",
+ "meta": {
+ "lastUpdated": "2013-05-05T16:13:03Z"
+ },
+ "text": {
+ "status": "additional",
+ "div": " Acute Asthmatic attack. Was wheezing\n for days prior to admission.
"
+ },
+ "code": {
+ "coding": [
+ {
+ "system": "http://loinc.org",
+ "code": "46241-6"
+ }
+ ],
+ "text": "Reason for admission"
+ },
+ "valueString": "Acute Asthmatic attack. Was wheezing for days prior to admission.",
+ "status": "final",
+ "reliability": "ok"
+ }
+ },
+ {
+ "base": "urn:uuid:",
+ "resource": {
+ "resourceType": "List",
+ "id": "673f8db5-0ffd-4395-9657-6da00420bbc1",
+ "meta": {
+ "lastUpdated": "2013-05-05T16:13:03Z"
+ },
+ "text": {
+ "status": "additional",
+ "div": "\n
\n \n \n Medication | \n Last Change | \n Last ChangeReason | \n
\n \n \n \n Theophylline 200mg BD after meals | \n continued | \n
\n \n Ventolin Inhaler | \n stopped | \n Getting side effect of tremor | \n
\n \n
\n
"
+ },
+ "code": {
+ "coding": [
+ {
+ "system": "http://loinc.org",
+ "code": "10183-2",
+ "display": "Hospital discharge medications"
+ }
+ ]
+ },
+ "subject": {
+ "reference": "http://fhir.healthintersections.com.au/open/Patient/d1",
+ "display": "Peter Patient"
+ },
+ "status": "current",
+ "mode": "working",
+ "entry": [
+ {
+ "flag": [
+ {
+ "coding": [
+ {
+ "system": "http://www.ithealthboard.health.nz/fhir/ValueSet/medicationStatus",
+ "code": "started"
+ }
+ ]
+ }
+ ],
+ "item": {
+ "reference": "urn:uuid:124a6916-5d84-4b8c-b250-10cefb8e6e86"
+ }
+ },
+ {
+ "flag": [
+ {
+ "coding": [
+ {
+ "system": "http://www.ithealthboard.health.nz/fhir/ValueSet/medicationStatus",
+ "code": "stopped"
+ }
+ ]
+ }
+ ],
+ "deleted": true,
+ "item": {
+ "reference": "MedicationPrescription/1",
+ "display": "use of Ventolin Inhaler was discontinued"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "base": "urn:uuid:",
+ "resource": {
+ "resourceType": "MedicationPrescription",
+ "id": "124a6916-5d84-4b8c-b250-10cefb8e6e86",
+ "meta": {
+ "lastUpdated": "2013-05-05T16:13:03Z"
+ },
+ "text": {
+ "status": "generated",
+ "div": "\n
Theophylline 200mg twice a day
\n
"
+ },
+ "contained": [
+ {
+ "resourceType": "Medication",
+ "id": "med1",
+ "name": "Theophylline 200mg",
+ "code": {
+ "coding": [
+ {
+ "system": "http://snomed.info/sct",
+ "code": "66493003"
+ }
+ ]
+ }
+ }
+ ],
+ "patient": {
+ "reference": "http://fhir.healthintersections.com.au/open/Patient/d1",
+ "display": "Peter Patient"
+ },
+ "prescriber": {
+ "reference": "Practitioner/example",
+ "display": "Peter Practitioner"
+ },
+ "reasonCodeableConcept": {
+ "text": "Management of Asthma"
+ },
+ "medicationReference": {
+ "reference": "#med1",
+ "display": "Theophylline 200mg BD"
+ },
+ "dosageInstruction": [
+ {
+ "additionalInstructions": {
+ "text": "Take with Food"
+ },
+ "scheduledTiming": {
+ "repeat": {
+ "frequency": 2,
+ "period": 1,
+ "periodUnits": "d"
+ }
+ },
+ "route": {
+ "coding": [
+ {
+ "system": "http://snomed.info/sct",
+ "code": "394899003",
+ "display": "oral administration of treatment"
+ }
+ ]
+ },
+ "doseQuantity": {
+ "value": 1,
+ "units": "tablet",
+ "system": "http://unitsofmeasure.org",
+ "code": "tbl"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "base": "urn:uuid:",
+ "resource": {
+ "resourceType": "List",
+ "id": "68f86194-e6e1-4f65-b64a-5314256f8d7b",
+ "meta": {
+ "lastUpdated": "2013-05-05T16:13:03Z"
+ },
+ "text": {
+ "status": "additional",
+ "div": "\n
\n \n \n Allergen | \n Reaction | \n
\n \n \n \n Doxycycline | \n Hives | \n
\n \n
\n
"
+ },
+ "code": {
+ "coding": [
+ {
+ "system": "http://loinc.org",
+ "code": "48765-2",
+ "display": "Allergies and adverse reactions Document"
+ }
+ ]
+ },
+ "subject": {
+ "reference": "http://fhir.healthintersections.com.au/open/Patient/d1",
+ "display": "Peter Patient"
+ },
+ "status": "current",
+ "mode": "working",
+ "entry": [
+ {
+ "item": {
+ "reference": "urn:uuid:47600e0f-b6b5-4308-84b5-5dec157f7637"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "base": "urn:uuid:",
+ "resource": {
+ "resourceType": "AllergyIntolerance",
+ "id": "47600e0f-b6b5-4308-84b5-5dec157f7637",
+ "meta": {
+ "lastUpdated": "2013-05-05T16:13:03Z"
+ },
+ "text": {
+ "status": "generated",
+ "div": "Sensitivity to Doxycycline :\n Hives
"
+ },
+ "recordedDate": "2012-09-17",
+ "patient": {
+ "reference": "http://fhir.healthintersections.com.au/open/Patient/d1",
+ "display": "Eve Everywoman"
+ },
+ "substance": {
+ "text": "Doxycycline"
+ },
+ "status": "confirmed",
+ "criticality": "high",
+ "type": "immune",
+ "event": [
+ {
+ "manifestation": [
+ {
+ "coding": [
+ {
+ "system": "http://example.org/system",
+ "code": "xxx",
+ "display": "Hives"
+ }
+ ],
+ "text": "Hives"
+ }
+ ]
+ }
+ ]
+ }
+ }
+ ]
+}
\ No newline at end of file
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 d0c2e9f363c..13578cf1cf3 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
@@ -34,6 +34,22 @@ public class IdDtTest {
assertFalse(id.isLocal());
}
+ @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("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("#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());
+ }
+
+
/**
* See #67
*/
@@ -41,34 +57,11 @@ public class IdDtTest {
public void testComplicatedLocal() {
IdDt id = new IdDt("#Patient/cid:Patient-72/_history/1");
assertTrue(id.isLocal());
- assertNull(id.getBaseUrl());
+ assertEquals("#", 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());
- IdDt id2 = new IdDt("#Patient/cid:Patient-72/_history/1");
- assertEquals(id, id2);
-
- id2 = id2.toUnqualified();
- assertTrue(id2.isLocal());
- assertNull(id2.getBaseUrl());
- assertNull(id2.getResourceType());
- assertNull(id2.getVersionIdPart());
- assertEquals("#Patient/cid:Patient-72/_history/1", id2.getIdPart());
-
- id2 = id2.toVersionless();
- assertTrue(id2.isLocal());
- assertNull(id2.getBaseUrl());
- assertNull(id2.getResourceType());
- assertNull(id2.getVersionIdPart());
- assertEquals("#Patient/cid:Patient-72/_history/1", id2.getIdPart());
-
- id2 = id2.toUnqualifiedVersionless();
- assertTrue(id2.isLocal());
- assertNull(id2.getBaseUrl());
- assertNull(id2.getResourceType());
- assertNull(id2.getVersionIdPart());
- assertEquals("#Patient/cid:Patient-72/_history/1", id2.getIdPart());
}
@Test
diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/JsonParserTest.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/JsonParserTest.java
index 588a5f1891b..a67059ccc58 100644
--- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/JsonParserTest.java
+++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/JsonParserTest.java
@@ -99,7 +99,7 @@ public class JsonParserTest {
assertEquals(exp, act);
}
-
+
@Test
public void testDecimalPrecisionPreserved() {
String number = "52.3779939997090374535378485873776474764643249869328698436986235758587";
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 ca2a8241465..738facb25c4 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
@@ -537,6 +537,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());
assertNotNull(patient.getManagingOrganization().getResource());
org = (Organization) patient.getManagingOrganization().getResource();
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 dbdfcc3e82b..b591f0c7b09 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
@@ -1,7 +1,13 @@
package ca.uhn.fhir.parser;
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.stringContainsInOrder;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThat;
import java.io.IOException;
import java.util.ArrayList;
@@ -14,7 +20,6 @@ import net.sf.json.JsonConfig;
import org.apache.commons.io.IOUtils;
import org.hamcrest.Matchers;
-import org.hamcrest.core.StringContains;
import org.junit.Assert;
import org.junit.Test;
@@ -64,6 +69,87 @@ public class JsonParserDstu2Test {
assertThat(ourCtx.newJsonParser().setOmitResourceId(true).encodeResourceToString(p), not(containsString("123")));
}
+ @Test
+ public void testParseAndEncodeBundleWithUuidBase() {
+ //@formatter:off
+ String input =
+ "{\n" +
+ " \"resourceType\":\"Bundle\",\n" +
+ " \"type\":\"document\",\n" +
+ " \"entry\":[\n" +
+ " {\n" +
+ " \"base\":\"urn:uuid:\",\n" +
+ " \"resource\":{\n" +
+ " \"resourceType\":\"Composition\",\n" +
+ " \"id\":\"180f219f-97a8-486d-99d9-ed631fe4fc57\",\n" +
+ " \"meta\":{\n" +
+ " \"lastUpdated\":\"2013-05-28T22:12:21Z\"\n" +
+ " },\n" +
+ " \"text\":{\n" +
+ " \"status\":\"generated\",\n" +
+ " \"div\":\"\"\n" +
+ " },\n" +
+ " \"date\":\"2013-02-01T12:30:02Z\",\n" +
+ " \"type\":{\n" +
+ " \"coding\":[\n" +
+ " {\n" +
+ " \"system\":\"http://loinc.org\",\n" +
+ " \"code\":\"28655-9\"\n" +
+ " }\n" +
+ " ],\n" +
+ " \"text\":\"Discharge Summary from Responsible Clinician\"\n" +
+ " },\n" +
+ " \"status\":\"final\",\n" +
+ " \"confidentiality\":\"N\",\n" +
+ " \"subject\":{\n" +
+ " \"reference\":\"http://fhir.healthintersections.com.au/open/Patient/d1\",\n" +
+ " \"display\":\"Eve Everywoman\"\n" +
+ " },\n" +
+ " \"author\":[\n" +
+ " {\n" +
+ " \"reference\":\"Practitioner/example\",\n" +
+ " \"display\":\"Doctor Dave\"\n" +
+ " }\n" +
+ " ],\n" +
+ " \"encounter\":{\n" +
+ " \"reference\":\"http://fhir.healthintersections.com.au/open/Encounter/doc-example\"\n" +
+ " },\n" +
+ " \"section\":[\n" +
+ " {\n" +
+ " \"title\":\"Reason for admission\",\n" +
+ " \"content\":{\n" +
+ " \"reference\":\"urn:uuid:d0dd51d3-3ab2-4c84-b697-a630c3e40e7a\"\n" +
+ " }\n" +
+ " },\n" +
+ " {\n" +
+ " \"title\":\"Medications on Discharge\",\n" +
+ " \"content\":{\n" +
+ " \"reference\":\"urn:uuid:673f8db5-0ffd-4395-9657-6da00420bbc1\"\n" +
+ " }\n" +
+ " },\n" +
+ " {\n" +
+ " \"title\":\"Known allergies\",\n" +
+ " \"content\":{\n" +
+ " \"reference\":\"urn:uuid:68f86194-e6e1-4f65-b64a-5314256f8d7b\"\n" +
+ " }\n" +
+ " }\n" +
+ " ]\n" +
+ " }\n" +
+ " }" +
+ " ]" +
+ "}";
+ //@formatter:on
+
+ ca.uhn.fhir.model.dstu2.resource.Bundle parsed = ourCtx.newJsonParser().parseResource(ca.uhn.fhir.model.dstu2.resource.Bundle.class, input);
+
+ String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(parsed);
+ 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());
+ assertThat(encoded, containsString("\"id\":\"180f219f-97a8-486d-99d9-ed631fe4fc57\""));
+ }
@Test
diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java
index 5f8059c9da3..e9138ae73e6 100644
--- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java
+++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java
@@ -1108,7 +1108,15 @@ public class GenericClientDstu2Test {
assertNotNull(response.getOperationOutcome());
assertEquals("FOOBAR", response.getOperationOutcome().getIssueFirstRep().getDetailsElement().getValue());
idx++;
-}
+
+ response = client.validate().resource(ourCtx.newJsonParser().encodeResourceToString(p)).prettyPrint().execute();
+ assertEquals("http://example.com/fhir/Patient/$validate?_format=json&_pretty=true", capt.getAllValues().get(idx).getURI().toASCIIString());
+ assertEquals("POST", capt.getAllValues().get(idx).getRequestLine().getMethod());
+ assertThat(extractBody(capt, idx), containsString("\"resourceType\":\"Parameters\",\n"));
+ assertNotNull(response.getOperationOutcome());
+ assertEquals("FOOBAR", response.getOperationOutcome().getIssueFirstRep().getDetailsElement().getValue());
+ idx++;
+ }
@BeforeClass
public static void beforeClass() {
diff --git a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/IdType.java b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/IdType.java
index 780b289e102..4520f4d5dc2 100644
--- a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/IdType.java
+++ b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/IdType.java
@@ -29,7 +29,8 @@ package org.hl7.fhir.instance.model;
*/
-import static org.apache.commons.lang3.StringUtils.*;
+import static org.apache.commons.lang3.StringUtils.isBlank;
+import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.math.BigDecimal;
@@ -42,6 +43,8 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
+import ca.uhn.fhir.parser.DataFormatException;
+
/**
* This class represents the logical identity for a resource, or as much of that
* identity is known. In FHIR, every resource must have a "logical ID" which
@@ -311,6 +314,11 @@ public final class IdType extends UriType implements IPrimitiveType, IId
public String getValue() {
String retVal = super.getValue();
if (retVal == null && myHaveComponentParts) {
+
+ if (determineLocalPrefix(myBaseUrl) != null && myResourceType == null && myUnqualifiedVersionId == null) {
+ return myBaseUrl + myUnqualifiedId;
+ }
+
StringBuilder b = new StringBuilder();
if (isNotBlank(myBaseUrl)) {
b.append(myBaseUrl);
@@ -428,9 +436,37 @@ public final class IdType extends UriType implements IPrimitiveType, IId
*/
@Override
public boolean isLocal() {
- return myUnqualifiedId != null && myUnqualifiedId.isEmpty() == false && myUnqualifiedId.charAt(0) == '#';
+ return "#".equals(myBaseUrl);
}
+ 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;
+ }
+ }
+
/**
* Set the value
*
@@ -443,22 +479,29 @@ public final class IdType extends UriType implements IPrimitiveType, IId
*
*/
@Override
- public IdType setValue(String theValue) {
+ public IdType setValue(String theValue) throws DataFormatException {
// TODO: add validation
super.setValue(theValue);
myHaveComponentParts = false;
+
+ String localPrefix = determineLocalPrefix(theValue);
+
if (StringUtils.isBlank(theValue)) {
myBaseUrl = null;
super.setValue(null);
myUnqualifiedId = null;
myUnqualifiedVersionId = null;
myResourceType = null;
- } else if (theValue.charAt(0) == '#') {
+ } else if (theValue.charAt(0) == '#' && theValue.length() > 1) {
super.setValue(theValue);
- myUnqualifiedId = theValue;
+ myBaseUrl = "#";
+ myUnqualifiedId = theValue.substring(1);
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;
diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/model/IdTypeTest.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/model/IdTypeTest.java
new file mode 100644
index 00000000000..7312c7bef1f
--- /dev/null
+++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/model/IdTypeTest.java
@@ -0,0 +1,226 @@
+package ca.uhn.fhir.model;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.math.BigDecimal;
+
+import org.hl7.fhir.instance.model.IdType;
+import org.hl7.fhir.instance.model.Patient;
+import org.hl7.fhir.instance.model.Reference;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import ca.uhn.fhir.context.FhirContext;
+
+public class IdTypeTest {
+
+ private static FhirContext ourCtx;
+
+ private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(IdTypeTest.class);
+
+ @Test
+ public void testDetectLocal() {
+ IdType id;
+
+ id = new IdType("#123");
+ assertEquals("#123", id.getValue());
+ assertTrue(id.isLocal());
+
+ id = new IdType("#Medication/499059CE-CDD4-48BC-9014-528A35D15CED/_history/1");
+ assertEquals("#Medication/499059CE-CDD4-48BC-9014-528A35D15CED/_history/1", id.getValue());
+ assertTrue(id.isLocal());
+
+ id = new IdType("http://example.com/Patient/33#123");
+ assertEquals("http://example.com/Patient/33#123", id.getValue());
+ assertFalse(id.isLocal());
+ }
+
+ @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("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("#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());
+ }
+
+
+ /**
+ * See #67
+ */
+ @Test
+ public void testComplicatedLocal() {
+ IdType id = new IdType("#Patient/cid:Patient-72/_history/1");
+ assertTrue(id.isLocal());
+ assertEquals("#", id.getBaseUrl());
+ assertNull(id.getResourceType());
+ assertNull(id.getVersionIdPart());
+ 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());
+ assertNull(id2.getBaseUrl());
+ assertNull(id2.getResourceType());
+ assertNull(id2.getVersionIdPart());
+ assertEquals("Patient/cid:Patient-72/_history/1", id2.getIdPart());
+
+ }
+
+ @Test
+ public void testDetermineBase() {
+
+ IdType rr;
+
+ rr = new IdType("http://foo/fhir/Organization/123");
+ assertEquals("http://foo/fhir", rr.getBaseUrl());
+
+ rr = new IdType("http://foo/fhir/Organization/123/_history/123");
+ assertEquals("http://foo/fhir", rr.getBaseUrl());
+
+ rr = new IdType("Organization/123/_history/123");
+ assertEquals(null, rr.getBaseUrl());
+
+ }
+
+ @Test
+ public void testParseValueAbsolute() {
+ Patient patient = new Patient();
+ IdType rr = new IdType();
+ rr.setValue("http://foo/fhir/Organization/123");
+
+ patient.setManagingOrganization(new Reference(rr));
+
+ Patient actual = parseAndEncode(patient);
+ Reference ref = actual.getManagingOrganization();
+ assertEquals("Organization", ref.getReferenceElement().getResourceType());
+ assertEquals("123", ref.getReferenceElement().getIdPart());
+
+ }
+
+ @Test
+ public void testBigDecimalIds() {
+
+ IdType id = new IdType(new BigDecimal("123"));
+ assertEquals(id.getIdPartAsBigDecimal(), new BigDecimal("123"));
+
+ }
+
+ @Test
+ public void testParseValueAbsoluteWithVersion() {
+ Patient patient = new Patient();
+ IdType rr = new IdType();
+ rr.setValue("http://foo/fhir/Organization/123/_history/999");
+ patient.setManagingOrganization(new Reference(rr));
+
+ Patient actual = parseAndEncode(patient);
+ Reference ref = actual.getManagingOrganization();
+ assertEquals("Organization", ref.getReferenceElement().getResourceType());
+ assertEquals("123", ref.getReferenceElement().getIdPart());
+ assertEquals(null, ref.getReferenceElement().getVersionIdPart());
+
+ }
+
+
+ @Test
+ public void testViewMethods() {
+ IdType i = new IdType("http://foo/fhir/Organization/123/_history/999");
+ assertEquals("Organization/123/_history/999", i.toUnqualified().getValue());
+ assertEquals("http://foo/fhir/Organization/123", i.toVersionless().getValue());
+ assertEquals("Organization/123", i.toUnqualifiedVersionless().getValue());
+ }
+
+ @Test
+ public void testParseValueWithVersion() {
+ Patient patient = new Patient();
+ IdType rr = new IdType();
+ rr.setValue("/123/_history/999");
+ patient.setManagingOrganization(new Reference(rr));
+
+ Patient actual = parseAndEncode(patient);
+ Reference ref = actual.getManagingOrganization();
+ assertEquals(null, ref.getReferenceElement().getResourceType());
+ assertEquals("123", ref.getReferenceElement().getIdPart());
+ assertEquals(null, ref.getReferenceElement().getVersionIdPart());
+
+ }
+
+ @Test
+ public void testParseValueMissingType1() {
+ Patient patient = new Patient();
+ IdType rr = new IdType();
+ rr.setValue("/123");
+ patient.setManagingOrganization(new Reference(rr));
+
+ Patient actual = parseAndEncode(patient);
+ Reference ref = actual.getManagingOrganization();
+ assertEquals(null, ref.getReferenceElement().getResourceType());
+ assertEquals("123", ref.getReferenceElement().getIdPart());
+
+ }
+
+ @Test
+ public void testParseValueMissingType2() {
+ Patient patient = new Patient();
+ IdType rr = new IdType();
+ rr.setValue("123");
+ patient.setManagingOrganization(new Reference(rr));
+
+ Patient actual = parseAndEncode(patient);
+ Reference ref = actual.getManagingOrganization();
+ assertEquals(null, ref.getReferenceElement().getResourceType());
+ assertEquals("123", ref.getReferenceElement().getIdPart());
+
+ }
+
+ @Test
+ public void testParseValueRelative1() {
+ Patient patient = new Patient();
+ IdType rr = new IdType();
+ rr.setValue("Organization/123");
+ patient.setManagingOrganization(new Reference(rr));
+
+ Patient actual = parseAndEncode(patient);
+ Reference ref = actual.getManagingOrganization();
+ assertEquals("Organization", ref.getReferenceElement().getResourceType());
+ assertEquals("123", ref.getReferenceElement().getIdPart());
+
+ }
+
+ @Test
+ public void testParseValueRelative2() {
+ Patient patient = new Patient();
+ IdType rr = new IdType();
+ rr.setValue("/Organization/123");
+ patient.setManagingOrganization(new Reference(rr));
+
+ Patient actual = parseAndEncode(patient);
+ Reference ref = actual.getManagingOrganization();
+ assertEquals("Organization", ref.getReferenceElement().getResourceType());
+ assertEquals("123", ref.getReferenceElement().getIdPart());
+
+ }
+
+ private Patient parseAndEncode(Patient patient) {
+ String encoded = ourCtx.newXmlParser().encodeResourceToString(patient);
+ ourLog.info("\n" + encoded);
+ return ourCtx.newXmlParser().parseResource(Patient.class, encoded);
+ }
+
+ @BeforeClass
+ public static void beforeClass() {
+ ourCtx = new FhirContext();
+ }
+
+}
diff --git a/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/Controller.java b/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/Controller.java
index e2d34e880e2..17092aac9df 100644
--- a/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/Controller.java
+++ b/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/Controller.java
@@ -597,6 +597,7 @@ public class Controller {
CaptureInterceptor interceptor = new CaptureInterceptor();
GenericClient client = theRequest.newClient(theReq, getContext(theRequest), myConfig, interceptor);
+ client.setPrettyPrint(true);
Class extends IResource> type = null; // def.getImplementingClass();
if ("history-type".equals(theMethod)) {
@@ -616,8 +617,10 @@ public class Controller {
try {
if (body.startsWith("{")) {
resource = getContext(theRequest).newJsonParser().parseResource(type, body);
+ client.setEncoding(EncodingEnum.JSON);
} else if (body.startsWith("<")) {
resource = getContext(theRequest).newXmlParser().parseResource(type, body);
+ client.setEncoding(EncodingEnum.XML);
} else {
theModel.put("errorMsg", "Message body does not appear to be a valid FHIR resource instance document. Body should start with '<' (for XML encoding) or '{' (for JSON encoding).");
return;
@@ -637,7 +640,7 @@ public class Controller {
try {
if (validate) {
outcomeDescription = "Validate Resource";
- client.validate(resource);
+ client.validate().resource(resource).prettyPrint().execute();
} else {
String id = theReq.getParameter("resource-create-id");
if ("update".equals(theMethod)) {
diff --git a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/TinderJpaRestServerMojo.java b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/TinderJpaRestServerMojo.java
index 2d0f8a22f81..f99711471b7 100644
--- a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/TinderJpaRestServerMojo.java
+++ b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/TinderJpaRestServerMojo.java
@@ -92,9 +92,6 @@ public class TinderJpaRestServerMojo extends AbstractMojo {
}
for (String next : keys) {
if (next.startsWith("resource.")) {
- if (next.endsWith(".Bundle")) {
- continue;
- }
baseResourceNames.add(next.substring("resource.".length()).toLowerCase());
}
}
diff --git a/hapi-tinder-plugin/src/main/resources/vm/jpa_resource_provider.vm b/hapi-tinder-plugin/src/main/resources/vm/jpa_resource_provider.vm
index 3096e10d358..300e95c0a8d 100644
--- a/hapi-tinder-plugin/src/main/resources/vm/jpa_resource_provider.vm
+++ b/hapi-tinder-plugin/src/main/resources/vm/jpa_resource_provider.vm
@@ -7,7 +7,8 @@ import org.apache.commons.lang3.StringUtils;
import ca.uhn.fhir.jpa.provider.JpaResourceProvider${versionCapitalized};
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
-import ca.uhn.fhir.model.api.*;
+import ca.uhn.fhir.model.api.IResource;
+import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.annotation.*;
import ca.uhn.fhir.model.${version}.composite.*;
import ca.uhn.fhir.model.${version}.resource.*;
@@ -16,7 +17,7 @@ import ca.uhn.fhir.rest.param.*;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.model.dstu.resource.Binary;
// import ca.uhn.fhir.model.dstu2.resource.Bundle;
-import ca.uhn.fhir.model.api.Bundle;
+// import ca.uhn.fhir.model.api.Bundle;
public class ${className}ResourceProvider extends JpaResourceProvider${versionCapitalized}<${className}> {
diff --git a/hapi-tinder-plugin/src/main/resources/vm/jpa_spring_beans.vm b/hapi-tinder-plugin/src/main/resources/vm/jpa_spring_beans.vm
index 9ed8d416adc..91d1dcaa381 100644
--- a/hapi-tinder-plugin/src/main/resources/vm/jpa_spring_beans.vm
+++ b/hapi-tinder-plugin/src/main/resources/vm/jpa_spring_beans.vm
@@ -27,7 +27,12 @@
#foreach ( $res in $resources )
-
+
+#else
+ class="ca.uhn.fhir.jpa.dao.FhirResourceDao${versionCapitalized}">
+#end
diff --git a/restful-server-example/pom.xml b/restful-server-example/pom.xml
index ff9f7a68de0..8e269dc232d 100644
--- a/restful-server-example/pom.xml
+++ b/restful-server-example/pom.xml
@@ -14,7 +14,7 @@
ca.uhn.hapi.example
restful-server-example
- 1.0
+ 1.0-SNAPSHOT
war
HAPI FHIR Sample RESTful Server