From f9e4a3e1b55627d82c9a773593d99757309cc630 Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Tue, 29 Sep 2015 19:41:31 -0400 Subject: [PATCH] Fix #233 - elementQuantity gets incorrectly encoded as elementDuration --- .project | 7 ++- hapi-fhir-base/.project | 14 ++++- .../context/IRuntimeDatatypeDefinition.java | 2 +- .../context/RuntimeChildChoiceDefinition.java | 63 ++++++++++++------- .../RuntimeCompositeDatatypeDefinition.java | 4 +- .../RuntimePrimitiveDatatypeDefinition.java | 4 +- hapi-fhir-jpaserver-base/.project | 14 ++++- .../SubscriptionWebsocketHandler.java | 20 +++--- .../jpa/dao/FhirResourceDaoDstu2Test.java | 4 ++ .../provider/ResourceProviderDstu2Test.java | 3 + .../jpa/provider/SubscriptionsDstu2Test.java | 4 +- src/changes/changes.xml | 6 ++ 12 files changed, 102 insertions(+), 43 deletions(-) diff --git a/.project b/.project index 7f72f8d439e..e65da4447c0 100644 --- a/.project +++ b/.project @@ -11,8 +11,13 @@ - org.eclipse.m2e.core.maven2Builder + org.eclipse.ui.externaltools.ExternalToolBuilder + full,incremental, + + LaunchConfigHandle + <project>/.externalToolBuilders/org.eclipse.m2e.core.maven2Builder.launch + diff --git a/hapi-fhir-base/.project b/hapi-fhir-base/.project index 23adf1aaa35..ea43e2c9d18 100644 --- a/hapi-fhir-base/.project +++ b/hapi-fhir-base/.project @@ -16,13 +16,23 @@ - org.eclipse.wst.validation.validationbuilder + org.eclipse.ui.externaltools.ExternalToolBuilder + full,incremental, + + LaunchConfigHandle + <project>/.externalToolBuilders/org.eclipse.wst.validation.validationbuilder.launch + - org.eclipse.m2e.core.maven2Builder + org.eclipse.ui.externaltools.ExternalToolBuilder + full,incremental, + + LaunchConfigHandle + <project>/.externalToolBuilders/org.eclipse.m2e.core.maven2Builder (1).launch + diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/IRuntimeDatatypeDefinition.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/IRuntimeDatatypeDefinition.java index 16226aa1e33..c2a333becf5 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/IRuntimeDatatypeDefinition.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/IRuntimeDatatypeDefinition.java @@ -27,7 +27,7 @@ public interface IRuntimeDatatypeDefinition { boolean isSpecialization(); - public BaseRuntimeElementDefinition getProfileOf(); + public Class getProfileOf(); boolean isProfileOf(Class theType); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildChoiceDefinition.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildChoiceDefinition.java index 1237879da62..81574a5b1d4 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildChoiceDefinition.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildChoiceDefinition.java @@ -30,6 +30,8 @@ import java.util.Set; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.api.IBase; +import org.hl7.fhir.instance.model.api.IBaseDatatype; +import org.hl7.fhir.instance.model.api.IBaseReference; import org.hl7.fhir.instance.model.api.IBaseResource; import ca.uhn.fhir.model.api.annotation.Child; @@ -41,6 +43,7 @@ public class RuntimeChildChoiceDefinition extends BaseRuntimeDeclaredChildDefini private Map> myNameToChildDefinition; private Map, String> myDatatypeToElementName; private Map, BaseRuntimeElementDefinition> myDatatypeToElementDefinition; + private String myReferenceSuffix; public RuntimeChildChoiceDefinition(Field theField, String theElementName, Child theChildAnnotation, Description theDescriptionAnnotation, List> theChoiceTypes) { super(theField, theChildAnnotation, theDescriptionAnnotation, theElementName); @@ -82,9 +85,15 @@ public class RuntimeChildChoiceDefinition extends BaseRuntimeDeclaredChildDefini myDatatypeToElementName = new HashMap, String>(); myDatatypeToElementDefinition = new HashMap, BaseRuntimeElementDefinition>(); + if (theContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU1)) { + myReferenceSuffix = "Resource"; + } else { + myReferenceSuffix = "Reference"; + } + for (Class next : myChoiceTypes) { - String elementName; + String elementName = null; BaseRuntimeElementDefinition nextDef; boolean nonPreferred = false; if (IBaseResource.class.isAssignableFrom(next)) { @@ -109,32 +118,43 @@ public class RuntimeChildChoiceDefinition extends BaseRuntimeDeclaredChildDefini * element fooString when encoded, because markdown is a profile of string. This is according to the * FHIR spec */ - nextDefForChoice = nextDefDatatype.getProfileOf(); + nextDefForChoice = null; nonPreferred = true; + Class profileType = nextDefDatatype.getProfileOf(); + BaseRuntimeElementDefinition elementDef = theClassToElementDefinitions.get(profileType); + elementName = getElementName() + StringUtils.capitalize(elementDef.getName()); } } - elementName = getElementName() + StringUtils.capitalize(nextDefForChoice.getName()); - } - - if (myNameToChildDefinition.containsKey(elementName) == false || !nonPreferred) { - myNameToChildDefinition.put(elementName, nextDef); - } - - if (IBaseResource.class.isAssignableFrom(next)) { - Class refType = theContext.getVersion().getResourceReferenceType(); - myDatatypeToElementDefinition.put(refType, nextDef); - - String alternateElementName; - if (theContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU1)) { - alternateElementName = getElementName() + "Resource"; - } else { - alternateElementName = getElementName() + "Reference"; + if (nextDefForChoice != null) { + elementName = getElementName() + StringUtils.capitalize(nextDefForChoice.getName()); } - myDatatypeToElementName.put(refType, alternateElementName); + } + + // I don't see how elementName could be null here, but eclipse complains.. + if (elementName != null) { + if (myNameToChildDefinition.containsKey(elementName) == false || !nonPreferred) { + myNameToChildDefinition.put(elementName, nextDef); + } + } + + /* + * If this is a resource reference, the element name is "fooNameReference" + */ + if (IBaseResource.class.isAssignableFrom(next) || IBaseReference.class.isAssignableFrom(next)) { + next = theContext.getVersion().getResourceReferenceType(); + elementName = getElementName() + myReferenceSuffix; } myDatatypeToElementDefinition.put(next, nextDef); - myDatatypeToElementName.put(next, elementName); + + if (myDatatypeToElementName.containsKey(next)) { + String existing = myDatatypeToElementName.get(next); + if (!existing.equals(elementName)) { + throw new ConfigurationException("Already have element name " + existing + " for datatype " + next.getClass().getSimpleName() + " in " + getElementName() + ", cannot add " + elementName); + } + } else { + myDatatypeToElementName.put(next, elementName); + } } myNameToChildDefinition = Collections.unmodifiableMap(myNameToChildDefinition); @@ -145,7 +165,8 @@ public class RuntimeChildChoiceDefinition extends BaseRuntimeDeclaredChildDefini @Override public String getChildNameByDatatype(Class theDatatype) { - return myDatatypeToElementName.get(theDatatype); + String retVal = myDatatypeToElementName.get(theDatatype); + return retVal; } @Override diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeCompositeDatatypeDefinition.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeCompositeDatatypeDefinition.java index c7fef549c38..3c2f8351e71 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeCompositeDatatypeDefinition.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeCompositeDatatypeDefinition.java @@ -65,8 +65,8 @@ public class RuntimeCompositeDatatypeDefinition extends BaseRuntimeElementCompos } @Override - public BaseRuntimeElementDefinition getProfileOf() { - return myProfileOf; + public Class getProfileOf() { + return myProfileOfType; } @Override diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimePrimitiveDatatypeDefinition.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimePrimitiveDatatypeDefinition.java index 6288b264d70..619c87a16bb 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimePrimitiveDatatypeDefinition.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimePrimitiveDatatypeDefinition.java @@ -58,8 +58,8 @@ public class RuntimePrimitiveDatatypeDefinition extends BaseRuntimeElementDefini } @Override - public BaseRuntimeElementDefinition getProfileOf() { - return myProfileOf; + public Class getProfileOf() { + return myProfileOfType; } @Override diff --git a/hapi-fhir-jpaserver-base/.project b/hapi-fhir-jpaserver-base/.project index 68612babd9f..8f94e87b636 100644 --- a/hapi-fhir-jpaserver-base/.project +++ b/hapi-fhir-jpaserver-base/.project @@ -17,13 +17,23 @@ - org.eclipse.wst.validation.validationbuilder + org.eclipse.ui.externaltools.ExternalToolBuilder + full,incremental, + + LaunchConfigHandle + <project>/.externalToolBuilders/org.eclipse.wst.validation.validationbuilder (1).launch + - org.eclipse.m2e.core.maven2Builder + org.eclipse.ui.externaltools.ExternalToolBuilder + full,incremental, + + LaunchConfigHandle + <project>/.externalToolBuilders/org.eclipse.m2e.core.maven2Builder (2).launch + diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/SubscriptionWebsocketHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/SubscriptionWebsocketHandler.java index 80e1248c397..6f2f3a83c40 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/SubscriptionWebsocketHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/SubscriptionWebsocketHandler.java @@ -119,11 +119,11 @@ public class SubscriptionWebsocketHandler extends TextWebSocketHandler implement } } - private class SimpleBoundState implements IState { + private class BoundStaticSubscipriptionState implements IState { private WebSocketSession mySession; - public SimpleBoundState(WebSocketSession theSession) { + public BoundStaticSubscipriptionState(WebSocketSession theSession) { mySession = theSession; } @@ -157,12 +157,12 @@ public class SubscriptionWebsocketHandler extends TextWebSocketHandler implement @Autowired private FhirContext myCtx; - private class ResourceBoundState implements IState { + private class BoundDynamicSubscriptionState implements IState { private WebSocketSession mySession; private EncodingEnum myEncoding; - public ResourceBoundState(WebSocketSession theSession, EncodingEnum theEncoding) { + public BoundDynamicSubscriptionState(WebSocketSession theSession, EncodingEnum theEncoding) { mySession = theSession; myEncoding = theEncoding; } @@ -241,12 +241,12 @@ public class SubscriptionWebsocketHandler extends TextWebSocketHandler implement subscription.setCriteria(theRemaining); try { - String params = theRemaining.substring(theRemaining.indexOf('?')); - List paramValues = URLEncodedUtils.parse("http://example.com" + params, Constants.CHARSET_UTF8); + String params = theRemaining.substring(theRemaining.indexOf('?')+1); + List paramValues = URLEncodedUtils.parse(params, Constants.CHARSET_UTF8, '&'); EncodingEnum encoding = EncodingEnum.JSON; for (NameValuePair nameValuePair : paramValues) { - if (Constants.PARAM_FORMAT.equals(nameValuePair)) { - EncodingEnum nextEncoding = EncodingEnum.forContentType(nameValuePair.getValue()); + if (Constants.PARAM_FORMAT.equals(nameValuePair.getName())) { + EncodingEnum nextEncoding = Constants.FORMAT_VAL_TO_ENCODING.get(nameValuePair.getValue()); if (nextEncoding != null) { encoding = nextEncoding; } @@ -257,7 +257,7 @@ public class SubscriptionWebsocketHandler extends TextWebSocketHandler implement mySubscriptionPid = mySubscriptionDao.getSubscriptionTablePidForSubscriptionResource(id); mySubscriptionId = subscription.getIdElement(); - myState = new ResourceBoundState(theSession, encoding); + myState = new BoundDynamicSubscriptionState(theSession, encoding); return id; } catch (UnprocessableEntityException e) { @@ -298,7 +298,7 @@ public class SubscriptionWebsocketHandler extends TextWebSocketHandler implement Subscription subscription = mySubscriptionDao.read(id); mySubscriptionPid = mySubscriptionDao.getSubscriptionTablePidForSubscriptionResource(id); mySubscriptionId = subscription.getIdElement(); - myState = new SimpleBoundState(theSession); + myState = new BoundStaticSubscipriptionState(theSession); } catch (ResourceNotFoundException e) { try { theSession.close(new CloseStatus(CloseStatus.PROTOCOL_ERROR.getCode(), "Invalid bind request - Unknown subscription: " + id.getValue())); 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 3b5c856aa94..c30151deee2 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 @@ -1919,6 +1919,10 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { @Test() public void testSortByComposite() { + Observation o = new Observation(); + o.getCode().setText("testSortByComposite"); + myObservationDao.create(o); + SearchParameterMap pm = new SearchParameterMap(); pm.setSort(new SortSpec(Observation.SP_CODE_VALUE_CONCEPT)); try { 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 fe459441f1b..abc0bb18274 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 @@ -1293,6 +1293,9 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test { @Test(expected = InvalidRequestException.class) public void testSearchWithInvalidSort() throws Exception { + Observation o = new Observation(); + o.getCode().setText("testSearchWithInvalidSort"); + myObservationDao.create(o); //@formatter:off Bundle found = ourClient .search() diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/SubscriptionsDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/SubscriptionsDstu2Test.java index 3c9baecdcbe..aceac69f4b9 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/SubscriptionsDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/SubscriptionsDstu2Test.java @@ -176,7 +176,7 @@ public class SubscriptionsDstu2Test extends BaseResourceProviderDstu2Test { WebSocketClient client = new WebSocketClient(); try { client.start(); - URI echoUri = new URI("ws://localhost:" + ourPort + "/baseDstu2/websocket"); + URI echoUri = new URI("ws://localhost:" + ourPort + "/websocket/dstu2"); client.connect(socket, echoUri, new ClientUpgradeRequest()); ourLog.info("Connecting to : {}", echoUri); @@ -241,7 +241,7 @@ public class SubscriptionsDstu2Test extends BaseResourceProviderDstu2Test { WebSocketClient client = new WebSocketClient(); try { client.start(); - URI echoUri = new URI("ws://localhost:" + ourPort + "/baseDstu2/websocket"); + URI echoUri = new URI("ws://localhost:" + ourPort + "/websocket/dstu2"); client.connect(socket, echoUri, new ClientUpgradeRequest()); ourLog.info("Connecting to : {}", echoUri); diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 22e1d3cfea5..5945427e7e0 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -95,6 +95,12 @@ JPA server Patient/[id]/$everything operation now supports _lastUpdated filtering and _sort'ing of results. + + Fix parser issue where profiled choice element datatypes (e.g. value[x] where one allowable + type is Duration, which is a profile of Quantity) get incorrectly encoded using the + profiled datatype name instead of the base datatype name as required by the FHIR + spec. Thanks to Nehashri Puttu Lokesh for reporting! +