From 99568a4b308402a7cb30b3b80c11ca069ed7bd2b Mon Sep 17 00:00:00 2001 From: James Agnew Date: Sun, 5 Jun 2016 11:33:43 -0400 Subject: [PATCH] Clean up client fluent param types and add some test coverage --- .../fhir/rest/gclient/DateClientParam.java | 40 +- .../rest/gclient/QuantityClientParam.java | 2 +- .../uhn/fhir/rest/gclient/UriClientParam.java | 12 + .../main/java/ca/uhn/fhir/util/BeanUtils.java | 56 --- .../fhir/util/PrettyPrintWriterWrapper.java | 17 + .../java/ca/uhn/fhir/util/ReflectionUtil.java | 19 +- .../fhir/util/jar/DependencyLogFactory.java | 21 +- .../uhn/fhir/util/reflection/IBeanUtils.java | 31 -- .../util/reflection/JavaBeansBeanUtil.java | 70 --- .../util/reflection/JavaReflectBeanUtil.java | 63 --- .../java/ca/uhn/fhir/util/BeanUtilTest.java | 78 ---- .../ca/uhn/fhir/util/ReflectionUtilTest.java | 20 + hapi-fhir-cobertura/pom.xml | 4 + .../java/ca/uhn/fhir/jpa/util/LogicUtil.java | 10 + .../uhn/fhir/parser/XmlParserDstu2Test.java | 43 +- .../rest/client/GenericClientDstu2Test.java | 70 +++ .../uhn/fhir/parser/XmlParserDstu3Test.java | 20 + .../rest/client/GenericClientDstu3Test.java | 417 +++++++++++++++++- src/changes/changes.xml | 8 + 19 files changed, 662 insertions(+), 339 deletions(-) delete mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/util/BeanUtils.java delete mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/util/reflection/IBeanUtils.java delete mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/util/reflection/JavaBeansBeanUtil.java delete mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/util/reflection/JavaReflectBeanUtil.java delete mode 100644 hapi-fhir-base/src/test/java/ca/uhn/fhir/util/BeanUtilTest.java diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/DateClientParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/DateClientParam.java index 8633712dae5..b3447735b83 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/DateClientParam.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/DateClientParam.java @@ -25,6 +25,7 @@ import java.util.Date; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.TemporalPrecisionEnum; import ca.uhn.fhir.model.primitive.DateTimeDt; +import ca.uhn.fhir.rest.param.ParamPrefixEnum; /** * Date parameter type for use in fluent client interfaces @@ -43,30 +44,32 @@ public class DateClientParam extends BaseClientParam implements IParam { } public IDateSpecifier after() { - return new DateWithPrefix(">"); + return new DateWithPrefix(ParamPrefixEnum.GREATERTHAN); } public IDateSpecifier afterOrEquals() { - return new DateWithPrefix(">="); + return new DateWithPrefix(ParamPrefixEnum.GREATERTHAN_OR_EQUALS); } public IDateSpecifier before() { - return new DateWithPrefix("<"); + return new DateWithPrefix(ParamPrefixEnum.LESSTHAN); } public IDateSpecifier beforeOrEquals() { - return new DateWithPrefix("<="); + return new DateWithPrefix(ParamPrefixEnum.LESSTHAN_OR_EQUALS); } public IDateSpecifier exactly() { - return new DateWithPrefix(""); + return new DateWithPrefix(ParamPrefixEnum.EQUAL); } private class Criterion implements ICriterion, ICriterionInternal { private String myValue; + private ParamPrefixEnum myPrefix; - public Criterion(String theValue) { + public Criterion(ParamPrefixEnum thePrefix, String theValue) { + myPrefix = thePrefix; myValue = theValue; } @@ -77,15 +80,20 @@ public class DateClientParam extends BaseClientParam implements IParam { @Override public String getParameterValue(FhirContext theContext) { - return myValue; + StringBuilder b = new StringBuilder(); + if (myPrefix != null && myPrefix != ParamPrefixEnum.EQUAL) { + b.append(myPrefix.getValueForContext(theContext)); + } + b.append(myValue); + return b.toString(); } } private class DateWithPrefix implements IDateSpecifier { - private String myPrefix; + private ParamPrefixEnum myPrefix; - public DateWithPrefix(String thePrefix) { + public DateWithPrefix(ParamPrefixEnum thePrefix) { myPrefix = thePrefix; } @@ -93,35 +101,35 @@ public class DateClientParam extends BaseClientParam implements IParam { public ICriterion day(Date theValue) { DateTimeDt dt = new DateTimeDt(theValue); dt.setPrecision(TemporalPrecisionEnum.DAY); - return new Criterion(myPrefix + dt.getValueAsString()); + return new Criterion(myPrefix, dt.getValueAsString()); } @Override public ICriterion day(String theValue) { DateTimeDt dt = new DateTimeDt(theValue); dt.setPrecision(TemporalPrecisionEnum.DAY); - return new Criterion(myPrefix + dt.getValueAsString()); + return new Criterion(myPrefix , dt.getValueAsString()); } @Override public ICriterion now() { - DateTimeDt dt = new DateTimeDt(); - dt.setPrecision(TemporalPrecisionEnum.DAY); - return new Criterion(myPrefix + dt.getValueAsString()); + DateTimeDt dt = DateTimeDt.withCurrentTime(); + dt.setPrecision(TemporalPrecisionEnum.SECOND); + return new Criterion(myPrefix , dt.getValueAsString()); } @Override public ICriterion second(Date theValue) { DateTimeDt dt = new DateTimeDt(theValue); dt.setPrecision(TemporalPrecisionEnum.SECOND); - return new Criterion(myPrefix + dt.getValueAsString()); + return new Criterion(myPrefix , dt.getValueAsString()); } @Override public ICriterion second(String theValue) { DateTimeDt dt = new DateTimeDt(theValue); dt.setPrecision(TemporalPrecisionEnum.SECOND); - return new Criterion(myPrefix + dt.getValueAsString()); + return new Criterion(myPrefix , dt.getValueAsString()); } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/QuantityClientParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/QuantityClientParam.java index 8a2c196aea9..727636985d4 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/QuantityClientParam.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/QuantityClientParam.java @@ -191,7 +191,7 @@ public class QuantityClientParam extends BaseClientParam implements IParam { @Override public ICriterion andUnits(String theUnits) { - return andUnits(theUnits, null); + return andUnits(null, theUnits); } @Override diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/UriClientParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/UriClientParam.java index 8d50ff2b3df..283ead114f5 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/UriClientParam.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/UriClientParam.java @@ -24,6 +24,7 @@ import java.util.Arrays; import java.util.List; import ca.uhn.fhir.model.primitive.StringDt; +import ca.uhn.fhir.util.CoverageIgnore; /** * @@ -45,11 +46,22 @@ public class UriClientParam extends BaseClientParam implements IParam { /** * The string matches the given value (servers will often, but are not required to) implement this as a left match, meaning that a value of "smi" would match "smi" and "smith". + * @param theValue THIS PARAMETER DOES NOT DO ANYTHING - This method was added by accident + * + * @deprecated theValue does not do anything, use {@link #matches()} instead */ + @CoverageIgnore + @Deprecated public IUriMatch matches(String theValue) { return new UriMatches(); } + /** + * The string matches the given value (servers will often, but are not required to) implement this as a left match, meaning that a value of "smi" would match "smi" and "smith". + */ + public IUriMatch matches() { + return new UriMatches(); + } public interface IUriMatch { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/BeanUtils.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/BeanUtils.java deleted file mode 100644 index 9ba0da18d5c..00000000000 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/BeanUtils.java +++ /dev/null @@ -1,56 +0,0 @@ -package ca.uhn.fhir.util; - -/* - * #%L - * HAPI FHIR - Core Library - * %% - * Copyright (C) 2014 - 2016 University Health Network - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -import java.lang.reflect.Method; - -import ca.uhn.fhir.util.reflection.IBeanUtils; - -public class BeanUtils { - - private static IBeanUtils beanUtils; - - private static IBeanUtils getBeanUtils() { - if (beanUtils == null) { - try { - beanUtils = (IBeanUtils) Class.forName("ca.uhn.fhir.util.reflection.JavaBeansBeanUtil").newInstance(); - } catch (Exception e) { - try { - beanUtils = (IBeanUtils) Class.forName("ca.uhn.fhir.util.reflection.JavaReflectBeanUtil") - .newInstance(); - } catch (Exception e1) { - throw new RuntimeException("Could not resolve BeanUtil implementation"); - } - } - } - return beanUtils; - } - - public static Method findAccessor(Class theClassToIntrospect, Class theTargetReturnType, String thePropertyName) - throws NoSuchFieldException { - return getBeanUtils().findAccessor(theClassToIntrospect, theTargetReturnType, thePropertyName); - } - - public static Method findMutator(Class theClassToIntrospect, Class theTargetReturnType, String thePropertyName) - throws NoSuchFieldException { - return getBeanUtils().findMutator(theClassToIntrospect, theTargetReturnType, thePropertyName); - } -} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/PrettyPrintWriterWrapper.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/PrettyPrintWriterWrapper.java index 15584a75f7e..a72234164b1 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/PrettyPrintWriterWrapper.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/PrettyPrintWriterWrapper.java @@ -55,31 +55,37 @@ public class PrettyPrintWriterWrapper implements XMLStreamWriter { myTarget.flush(); } + @CoverageIgnore @Override public NamespaceContext getNamespaceContext() { return myTarget.getNamespaceContext(); } + @CoverageIgnore @Override public String getPrefix(String theUri) throws XMLStreamException { return myTarget.getPrefix(theUri); } + @CoverageIgnore @Override public Object getProperty(String theName) throws IllegalArgumentException { return myTarget.getProperty(theName); } + @CoverageIgnore @Override public void setDefaultNamespace(String theUri) throws XMLStreamException { myTarget.setDefaultNamespace(theUri); } + @CoverageIgnore @Override public void setNamespaceContext(NamespaceContext theContext) throws XMLStreamException { myTarget.setNamespaceContext(theContext); } + @CoverageIgnore @Override public void setPrefix(String thePrefix, String theUri) throws XMLStreamException { myTarget.setPrefix(thePrefix, theUri); @@ -90,16 +96,19 @@ public class PrettyPrintWriterWrapper implements XMLStreamWriter { myTarget.writeAttribute(theLocalName, theValue); } + @CoverageIgnore @Override public void writeAttribute(String theNamespaceURI, String theLocalName, String theValue) throws XMLStreamException { myTarget.writeAttribute(theNamespaceURI, theLocalName, theValue); } + @CoverageIgnore @Override public void writeAttribute(String thePrefix, String theNamespaceURI, String theLocalName, String theValue) throws XMLStreamException { myTarget.writeAttribute(thePrefix, theNamespaceURI, theLocalName, theValue); } + @CoverageIgnore @Override public void writeCData(String theData) throws XMLStreamException { myTarget.writeCData(theData); @@ -130,29 +139,34 @@ public class PrettyPrintWriterWrapper implements XMLStreamWriter { myTarget.writeDefaultNamespace(theNamespaceURI); } + @CoverageIgnore @Override public void writeDTD(String theDtd) throws XMLStreamException { myTarget.writeDTD(theDtd); } + @CoverageIgnore @Override public void writeEmptyElement(String theLocalName) throws XMLStreamException { indent(); myTarget.writeEmptyElement(theLocalName); } + @CoverageIgnore @Override public void writeEmptyElement(String theNamespaceURI, String theLocalName) throws XMLStreamException { indent(); myTarget.writeEmptyElement(theNamespaceURI, theLocalName); } + @CoverageIgnore @Override public void writeEmptyElement(String thePrefix, String theLocalName, String theNamespaceURI) throws XMLStreamException { indent(); myTarget.writeEmptyElement(thePrefix, theLocalName, theNamespaceURI); } + @CoverageIgnore @Override public void writeEndDocument() throws XMLStreamException { decrementAndIndent(); @@ -170,6 +184,7 @@ public class PrettyPrintWriterWrapper implements XMLStreamWriter { } + @CoverageIgnore @Override public void writeEntityRef(String theName) throws XMLStreamException { myTarget.writeEntityRef(theName); @@ -180,11 +195,13 @@ public class PrettyPrintWriterWrapper implements XMLStreamWriter { myTarget.writeNamespace(thePrefix, theNamespaceURI); } + @CoverageIgnore @Override public void writeProcessingInstruction(String theTarget) throws XMLStreamException { myTarget.writeProcessingInstruction(theTarget); } + @CoverageIgnore @Override public void writeProcessingInstruction(String theTarget, String theData) throws XMLStreamException { myTarget.writeProcessingInstruction(theTarget, theData); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ReflectionUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ReflectionUtil.java index 33a4e902adf..4694ef4f34f 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ReflectionUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ReflectionUtil.java @@ -30,10 +30,11 @@ import java.util.LinkedHashSet; import java.util.List; import ca.uhn.fhir.context.ConfigurationException; -import ca.uhn.fhir.model.api.IQueryParameterType; public class ReflectionUtil { + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ReflectionUtil.class); + public static LinkedHashSet getDeclaredMethods(Class theClazz) { LinkedHashSet retVal = new LinkedHashSet(); for (Method next : theClazz.getDeclaredMethods()) { @@ -136,4 +137,20 @@ public class ReflectionUtil { } } + @SuppressWarnings("unchecked") + public static T newInstanceOrReturnNull(String theClassName, Class theType) { + try { + Class clazz = Class.forName(theClassName); + if (!theType.isAssignableFrom(clazz)) { + throw new ConfigurationException(theClassName + " is not assignable to " + theType); + } + return (T) clazz.newInstance(); + } catch (ConfigurationException e) { + throw e; + } catch (Exception e) { + ourLog.info("Failed to instantiate {}: {}", theClassName, e.toString()); + return null; + } + } + } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/jar/DependencyLogFactory.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/jar/DependencyLogFactory.java index 6b5da574bcf..07a0cea4d87 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/jar/DependencyLogFactory.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/jar/DependencyLogFactory.java @@ -1,5 +1,8 @@ package ca.uhn.fhir.util.jar; +import ca.uhn.fhir.util.CoverageIgnore; +import ca.uhn.fhir.util.ReflectionUtil; + /* * #%L * HAPI FHIR - Core Library @@ -21,16 +24,16 @@ package ca.uhn.fhir.util.jar; */ public class DependencyLogFactory { - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DependencyLogFactory.class); - @SuppressWarnings("unchecked") + /** + * Non instantiable + */ + @CoverageIgnore + private DependencyLogFactory() { + // nothing + } + public static IDependencyLog createJarLogger() { - try { - Class clas = (Class) Class.forName("ca.uhn.fhir.util.jar.DependencyLogImpl"); - return clas.newInstance(); - } catch (Exception e) { - ourLog.info("Could not log dependency."); - return null; - } + return ReflectionUtil.newInstanceOrReturnNull("ca.uhn.fhir.util.jar.DependencyLogImpl", IDependencyLog.class); } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/reflection/IBeanUtils.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/reflection/IBeanUtils.java deleted file mode 100644 index ba865b719fc..00000000000 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/reflection/IBeanUtils.java +++ /dev/null @@ -1,31 +0,0 @@ -package ca.uhn.fhir.util.reflection; - -import java.lang.reflect.Method; - -/* - * #%L - * HAPI FHIR - Core Library - * %% - * Copyright (C) 2014 - 2016 University Health Network - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -public interface IBeanUtils { - Method findAccessor(Class theClassToIntrospect, Class theTargetReturnType, String thePropertyName) - throws NoSuchFieldException; - - Method findMutator(Class theClassToIntrospect, Class theTargetReturnType, String thePropertyName) - throws NoSuchFieldException; -} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/reflection/JavaBeansBeanUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/reflection/JavaBeansBeanUtil.java deleted file mode 100644 index a94230a85b5..00000000000 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/reflection/JavaBeansBeanUtil.java +++ /dev/null @@ -1,70 +0,0 @@ -package ca.uhn.fhir.util.reflection; - -/* - * #%L - * HAPI FHIR - Core Library - * %% - * Copyright (C) 2014 - 2016 University Health Network - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -import java.beans.BeanInfo; -import java.beans.IntrospectionException; -import java.beans.Introspector; -import java.beans.PropertyDescriptor; -import java.lang.reflect.Method; - -public class JavaBeansBeanUtil implements IBeanUtils { - - @Override - public Method findAccessor(Class theClassToIntrospect, Class theTargetReturnType, String thePropertyName) throws NoSuchFieldException { - BeanInfo info; - try { - info = Introspector.getBeanInfo(theClassToIntrospect); - } catch (IntrospectionException e) { - throw new NoSuchFieldException(e.getMessage()); - } - for (PropertyDescriptor pd : info.getPropertyDescriptors()) { - if (thePropertyName.equals(pd.getName())) { - if (theTargetReturnType.isAssignableFrom(pd.getPropertyType())) { - return pd.getReadMethod(); - }else { - throw new NoSuchFieldException(theClassToIntrospect + " has an accessor for field " + thePropertyName + " but it does not return type " + theTargetReturnType); - } - } - } - throw new NoSuchFieldException(theClassToIntrospect + " has no accessor for field " + thePropertyName); - } - - @Override - public Method findMutator(Class theClassToIntrospect, Class theTargetReturnType, String thePropertyName) throws NoSuchFieldException { - BeanInfo info; - try { - info = Introspector.getBeanInfo(theClassToIntrospect); - } catch (IntrospectionException e) { - throw new NoSuchFieldException(e.getMessage()); - } - for (PropertyDescriptor pd : info.getPropertyDescriptors()) { - if (thePropertyName.equals(pd.getName())) { - if (theTargetReturnType.isAssignableFrom(pd.getPropertyType())) { - return pd.getWriteMethod(); - }else { - throw new NoSuchFieldException(theClassToIntrospect + " has an mutator for field " + thePropertyName + " but it does not return type " + theTargetReturnType); - } - } - } - throw new NoSuchFieldException(theClassToIntrospect + " has no mutator for field " + thePropertyName); - } -} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/reflection/JavaReflectBeanUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/reflection/JavaReflectBeanUtil.java deleted file mode 100644 index 8487eb3488b..00000000000 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/reflection/JavaReflectBeanUtil.java +++ /dev/null @@ -1,63 +0,0 @@ -package ca.uhn.fhir.util.reflection; - -import java.lang.reflect.Method; - -import org.apache.commons.lang3.text.WordUtils; - -import ca.uhn.fhir.context.ConfigurationException; - -/* - * #%L - * HAPI FHIR - Core Library - * %% - * Copyright (C) 2014 - 2016 University Health Network - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -public class JavaReflectBeanUtil implements IBeanUtils { - - @Override - public Method findAccessor(Class theClassToIntrospect, Class theTargetReturnType, String thePropertyName) - throws NoSuchFieldException { - String methodName = "get" + WordUtils.capitalize(thePropertyName); - try { - Method method = theClassToIntrospect.getMethod(methodName); - if (theTargetReturnType.isAssignableFrom(method.getReturnType())) { - return method; - } - } catch (NoSuchMethodException e) { - // fall through - } catch (SecurityException e) { - throw new ConfigurationException("Failed to scan class '" + theClassToIntrospect + "' because of a security exception", e); - } - throw new NoSuchFieldException(theClassToIntrospect + " has no accessor for field " + thePropertyName); - } - - @Override - public Method findMutator(Class theClassToIntrospect, Class theTargetArgumentType, String thePropertyName) - throws NoSuchFieldException { - String methodName = "set" + WordUtils.capitalize(thePropertyName); - try { - return theClassToIntrospect.getMethod(methodName, theTargetArgumentType); - } catch (NoSuchMethodException e) { - //fall through - } catch (SecurityException e) { - throw new ConfigurationException("Failed to scan class '" + theClassToIntrospect + "' because of a security exception", e); - } - throw new NoSuchFieldException(theClassToIntrospect + " has an mutator for field " + thePropertyName + " but it does not return type " + theTargetArgumentType); - - } - -} diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/BeanUtilTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/BeanUtilTest.java deleted file mode 100644 index bf195717f58..00000000000 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/BeanUtilTest.java +++ /dev/null @@ -1,78 +0,0 @@ -package ca.uhn.fhir.util; - -import static org.junit.Assert.*; - -import java.lang.reflect.Method; - -import org.junit.Test; - -import ca.uhn.fhir.util.reflection.IBeanUtils; -import ca.uhn.fhir.util.reflection.JavaBeansBeanUtil; -import ca.uhn.fhir.util.reflection.JavaReflectBeanUtil; - -public class BeanUtilTest { - - @Test - public void testFindAccessor() throws Exception { - JavaBeansBeanUtil javaBeansBeanUtil = new JavaBeansBeanUtil(); - testBeanUtilsAccessor(javaBeansBeanUtil); - JavaReflectBeanUtil javaReflectBeanUtil = new JavaReflectBeanUtil(); - testBeanUtilsAccessor(javaReflectBeanUtil); - assertNotNull(BeanUtils.findAccessor(BeanUtilTestClass.class, String.class, "field")); - Method jbMGet = javaBeansBeanUtil.findAccessor(BeanUtilTestClass.class, String.class, "field"); - Method jrMGet = javaReflectBeanUtil.findAccessor(BeanUtilTestClass.class, String.class, "field"); - assertNotNull(jbMGet); - assertNotNull(jrMGet); - assertEquals(jbMGet, jrMGet); - } - - @Test - public void testFindMutator() throws Exception { - JavaBeansBeanUtil javaBeansBeanUtil = new JavaBeansBeanUtil(); - testBeanUtilsMutator(javaBeansBeanUtil); - JavaReflectBeanUtil javaReflectBeanUtil = new JavaReflectBeanUtil(); - testBeanUtilsMutator(javaReflectBeanUtil); - assertNotNull(BeanUtils.findMutator(BeanUtilTestClass.class, String.class, "field")); - Method jbMSet = javaBeansBeanUtil.findMutator(BeanUtilTestClass.class, String.class, "field"); - Method jrMSet = javaReflectBeanUtil.findMutator(BeanUtilTestClass.class, String.class, "field"); - assertNotNull(jbMSet); - assertNotNull(jrMSet); - assertEquals(jbMSet, jrMSet); - } - - private void testBeanUtilsAccessor(IBeanUtils util) throws Exception { - assertNotNull(util.findAccessor(BeanUtilTestClass.class, String.class, "field")); - try { - assertNull(util.findAccessor(BeanUtilTestClass.class, String.class, "fieldX")); - fail("Field is not in class"); - } catch (NoSuchFieldException e) { } - try { - assertNull(util.findAccessor(BeanUtilTestClass.class, Integer.class, "field")); - fail("Field is in class, but we expect Integer as return type"); - } catch (NoSuchFieldException e) { } - } - - private void testBeanUtilsMutator(IBeanUtils util) throws Exception { - assertNotNull(util.findMutator(BeanUtilTestClass.class, String.class, "field")); - try { - assertNull(util.findMutator(BeanUtilTestClass.class, String.class, "fieldX")); - fail("Field is not in class"); - } catch (NoSuchFieldException e) { } - try { - assertNull(util.findMutator(BeanUtilTestClass.class, Integer.class, "field")); - fail("Field is in class, but we expect Integer as parameter type"); - } catch (NoSuchFieldException e) { } - } - - public static class BeanUtilTestClass { - private String myField; - - public String getField() { - return myField; - } - - public void setField(String value) { - this.myField = value; - } - } -} diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/ReflectionUtilTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/ReflectionUtilTest.java index 5a0a11ae8ff..5742f042135 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/ReflectionUtilTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/ReflectionUtilTest.java @@ -16,6 +16,26 @@ public class ReflectionUtilTest { assertEquals(ArrayList.class, ReflectionUtil.newInstance(ArrayList.class).getClass()); } + @Test + public void testNewInstanceOrReturnNullString() { + assertEquals(ArrayList.class, ReflectionUtil.newInstanceOrReturnNull(ArrayList.class.getName(), List.class).getClass()); + } + + @Test + public void testNewInstanceOrReturnNullWrong1() { + assertEquals(null, ReflectionUtil.newInstanceOrReturnNull("foo.Foo", List.class)); + } + + @Test + public void testNewInstanceOrReturnNullWrong2() { + try { + ReflectionUtil.newInstanceOrReturnNull("java.lang.String", List.class); + fail(); + } catch (ConfigurationException e) { + assertEquals("java.lang.String is not assignable to interface java.util.List", e.getMessage()); + } + } + @Test public void testNewInstanceFail() { try { diff --git a/hapi-fhir-cobertura/pom.xml b/hapi-fhir-cobertura/pom.xml index ffc166890aa..b2221961abf 100644 --- a/hapi-fhir-cobertura/pom.xml +++ b/hapi-fhir-cobertura/pom.xml @@ -288,6 +288,7 @@ ../hapi-fhir-structures-hl7org-dstu2/src/test/java ../hapi-fhir-structures-dstu3/src/test/java ../hapi-fhir-jpaserver-base/src/test/java + ../hapi-fhir-base/src/test/java @@ -357,6 +358,9 @@ + + ../hapi-fhir-base/src/test/resources + ../hapi-fhir-jpaserver-base/src/test/resources diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/LogicUtil.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/LogicUtil.java index 147d3c6eac8..46d4349bf96 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/LogicUtil.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/LogicUtil.java @@ -1,5 +1,7 @@ package ca.uhn.fhir.jpa.util; +import ca.uhn.fhir.util.CoverageIgnore; + /* * #%L * HAPI FHIR JPA Server @@ -22,6 +24,14 @@ package ca.uhn.fhir.jpa.util; public class LogicUtil { + /** + * Non instantiable + */ + @CoverageIgnore + private LogicUtil() { + // nothing + } + public static boolean multiXor(boolean... theValues) { int count = 0; for (int i = 0; i < theValues.length; i++) { diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/XmlParserDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/XmlParserDstu2Test.java index c0dc4998464..0f3fcfc00e3 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/XmlParserDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/XmlParserDstu2Test.java @@ -45,12 +45,10 @@ import com.google.common.collect.Sets; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.Bundle; import ca.uhn.fhir.model.api.ExtensionDt; -import ca.uhn.fhir.model.api.IDatatype; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; import ca.uhn.fhir.model.api.Tag; import ca.uhn.fhir.model.api.TagList; -import ca.uhn.fhir.model.api.TemporalPrecisionEnum; import ca.uhn.fhir.model.api.annotation.Child; import ca.uhn.fhir.model.api.annotation.ResourceDef; import ca.uhn.fhir.model.base.composite.BaseCodingDt; @@ -139,7 +137,7 @@ public class XmlParserDstu2Test { assertArrayEquals(new byte[] { 1, 2, 3, 4 }, bin.getContent()); } - + @Test public void testChoiceTypeWithProfiledType() { //@formatter:off @@ -1042,6 +1040,25 @@ public class XmlParserDstu2Test { } + @Test + public void testEncodeDivWithPre() { + + Patient p = new Patient(); + p.getText().setDiv("
\n\n

A P TAG

line1\nline2\nline3  BOLD

"); + + String output = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(p); + ourLog.info(output); + + //@formatter:off + assertThat(output, stringContainsInOrder( + " ", + " line1\nline2\nline3 BOLD" + )); + //@formatter:on + + } + @Test public void testEncodeDoesntIncludeUuidId() { Patient p = new Patient(); @@ -2253,16 +2270,6 @@ public class XmlParserDstu2Test { } - @Test - public void testParseInvalidTextualNumber() { - Observation obs = new Observation(); - obs.setValue(new QuantityDt().setValue(1234)); - String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs); - encoded = encoded.replace("1234", "\"1234\""); - ourLog.info(encoded); - ourCtx.newJsonParser().parseResource(encoded); - } - /** * See #366 */ @@ -2277,6 +2284,16 @@ public class XmlParserDstu2Test { ourCtx.newXmlParser().parseResource(resource); } + @Test + public void testParseInvalidTextualNumber() { + Observation obs = new Observation(); + obs.setValue(new QuantityDt().setValue(1234)); + String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs); + encoded = encoded.replace("1234", "\"1234\""); + ourLog.info(encoded); + ourCtx.newJsonParser().parseResource(encoded); + } + /** * See #216 */ 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 becec441442..4f96f461fd6 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 @@ -57,6 +57,7 @@ import ca.uhn.fhir.model.dstu2.resource.Bundle.Link; import ca.uhn.fhir.model.dstu2.resource.Conformance.Rest; import ca.uhn.fhir.model.dstu2.resource.Conformance.RestSecurity; import ca.uhn.fhir.model.dstu2.resource.Conformance; +import ca.uhn.fhir.model.dstu2.resource.Encounter; import ca.uhn.fhir.model.dstu2.resource.Observation; import ca.uhn.fhir.model.dstu2.resource.OperationOutcome; import ca.uhn.fhir.model.dstu2.resource.Parameters; @@ -1804,6 +1805,75 @@ public class GenericClientDstu2Test { } + @Test + public void testSearchByNumber() throws Exception { + final String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; + + ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class); + when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse); + when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK")); + when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8")); + when(myHttpResponse.getEntity().getContent()).then(new Answer() { + @Override + public InputStream answer(InvocationOnMock theInvocation) throws Throwable { + return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")); + }}); + + IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); + int idx = 0; + + //@formatter:off + client.search() + .forResource("Encounter") + .where(Encounter.LENGTH.greaterThan().number(123)) + .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://example.com/fhir/Encounter?length=gt123", capt.getAllValues().get(idx).getURI().toString()); + idx++; + + //@formatter:off + client.search() + .forResource("Encounter") + .where(Encounter.LENGTH.lessThan().number(123)) + .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://example.com/fhir/Encounter?length=lt123", capt.getAllValues().get(idx).getURI().toString()); + idx++; + + //@formatter:off + client.search() + .forResource("Encounter") + .where(Encounter.LENGTH.greaterThanOrEqual().number("123")) + .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://example.com/fhir/Encounter?length=ge123", capt.getAllValues().get(idx).getURI().toString()); + idx++; + + //@formatter:off + client.search() + .forResource("Encounter") + .where(Encounter.LENGTH.lessThanOrEqual().number("123")) + .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://example.com/fhir/Encounter?length=le123", capt.getAllValues().get(idx).getURI().toString()); + idx++; + + //@formatter:off + client.search() + .forResource("Encounter") + .where(Encounter.LENGTH.exactly().number(123)) + .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://example.com/fhir/Encounter?length=123", capt.getAllValues().get(idx).getURI().toString()); + idx++; + + } + @Test public void testSearchByUrl() throws Exception { 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 2e73aa260c0..2cbc238f7c8 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 @@ -119,6 +119,26 @@ public class XmlParserDstu3Test { ourCtx.setNarrativeGenerator(null); } + @Test + public void testEncodeDivWithPre() { + + Patient p = new Patient(); + p.getText().setDivAsString("
\n\n

A P TAG

line1\nline2\nline3  BOLD

"); + + String output = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(p); + ourLog.info(output); + + //@formatter:off + assertThat(output, stringContainsInOrder( + " ", + " line1\nline2\nline3 BOLD" + )); + //@formatter:on + + } + + @Test public void testEncodeContainedWithNonLocalId() throws Exception { diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu3Test.java index 900e3dce854..5ae701b0c78 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu3Test.java @@ -1,19 +1,28 @@ package ca.uhn.fhir.rest.client; import static org.hamcrest.Matchers.containsString; -import static org.junit.Assert.*; +import static org.hamcrest.Matchers.matchesPattern; +import static org.hamcrest.Matchers.startsWith; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.io.IOException; +import java.io.InputStream; import java.io.StringReader; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; +import java.util.Date; import java.util.List; import org.apache.commons.io.IOUtils; import org.apache.commons.io.input.ReaderInputStream; +import org.apache.commons.lang3.time.FastDateParser; import org.apache.http.Header; import org.apache.http.HttpResponse; import org.apache.http.ProtocolVersion; @@ -26,6 +35,8 @@ import org.hl7.fhir.dstu3.model.Binary; import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.Bundle.BundleType; import org.hl7.fhir.dstu3.model.Conformance; +import org.hl7.fhir.dstu3.model.Device; +import org.hl7.fhir.dstu3.model.Observation; import org.hl7.fhir.dstu3.model.OperationOutcome; import org.hl7.fhir.dstu3.model.Parameters; import org.hl7.fhir.dstu3.model.Patient; @@ -41,13 +52,19 @@ import org.mockito.stubbing.Answer; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.model.api.TemporalPrecisionEnum; +import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum; +import ca.uhn.fhir.model.primitive.DateTimeDt; +import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.parser.CustomTypeDstu3Test; import ca.uhn.fhir.parser.CustomTypeDstu3Test.MyCustomPatient; import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.PreferReturnEnum; +import ca.uhn.fhir.rest.param.ParamPrefixEnum; import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.util.TestUtil; +import ca.uhn.fhir.util.UrlUtil; import ca.uhn.fhir.util.VersionUtil; public class GenericClientDstu3Test { @@ -708,4 +725,402 @@ public class GenericClientDstu3Test { public static void beforeClass() { ourCtx = FhirContext.forDstu3(); } + + @SuppressWarnings("deprecation") + @Test + public void testSearchByQuantity() throws Exception { + final String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; + + ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class); + when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse); + when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK")); + when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8")); + when(myHttpResponse.getEntity().getContent()).then(new Answer() { + @Override + public InputStream answer(InvocationOnMock theInvocation) throws Throwable { + return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")); + }}); + + IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); + int idx = 0; + + //@formatter:off + client.search() + .forResource("Observation") + .where(Observation.VALUE_QUANTITY.approximately().number(123).andNoUnits()) + .returnBundle(Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://example.com/fhir/Observation?value-quantity=ap123||", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString())); + idx++; + + //@formatter:off + client.search() + .forResource("Observation") + .where(Observation.VALUE_QUANTITY.approximately().number("123").andUnits("CODE")) + .returnBundle(Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://example.com/fhir/Observation?value-quantity=ap123||CODE", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString())); + idx++; + + //@formatter:off + client.search() + .forResource("Observation") + .where(Observation.VALUE_QUANTITY.approximately().number("123").andUnits("SYSTEM", "CODE")) + .returnBundle(Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://example.com/fhir/Observation?value-quantity=ap123|SYSTEM|CODE", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString())); + idx++; + + //@formatter:off + client.search() + .forResource("Observation") + .where(Observation.VALUE_QUANTITY.exactly().number(123).andNoUnits()) + .returnBundle(Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://example.com/fhir/Observation?value-quantity=123||", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString())); + idx++; + + //@formatter:off + client.search() + .forResource("Observation") + .where(Observation.VALUE_QUANTITY.exactly().number("123").andUnits("CODE")) + .returnBundle(Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://example.com/fhir/Observation?value-quantity=123||CODE", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString())); + idx++; + + //@formatter:off + client.search() + .forResource("Observation") + .where(Observation.VALUE_QUANTITY.exactly().number("123").andUnits("SYSTEM", "CODE")) + .returnBundle(Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://example.com/fhir/Observation?value-quantity=123|SYSTEM|CODE", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString())); + idx++; + + //@formatter:off + client.search() + .forResource("Observation") + .where(Observation.VALUE_QUANTITY.greaterThan().number(123).andNoUnits()) + .returnBundle(Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://example.com/fhir/Observation?value-quantity=gt123||", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString())); + idx++; + + //@formatter:off + client.search() + .forResource("Observation") + .where(Observation.VALUE_QUANTITY.lessThan().number(123).andNoUnits()) + .returnBundle(Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://example.com/fhir/Observation?value-quantity=lt123||", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString())); + idx++; + + //@formatter:off + client.search() + .forResource("Observation") + .where(Observation.VALUE_QUANTITY.greaterThanOrEquals().number(123).andNoUnits()) + .returnBundle(Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://example.com/fhir/Observation?value-quantity=ge123||", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString())); + idx++; + + //@formatter:off + client.search() + .forResource("Observation") + .where(Observation.VALUE_QUANTITY.lessThanOrEquals().number(123).andNoUnits()) + .returnBundle(Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://example.com/fhir/Observation?value-quantity=le123||", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString())); + idx++; + + //@formatter:off + client.search() + .forResource("Observation") + .where(Observation.VALUE_QUANTITY.withComparator(QuantityCompararatorEnum.GREATERTHAN).number(123).andNoUnits()) + .returnBundle(Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://example.com/fhir/Observation?value-quantity=gt123||", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString())); + idx++; + + //@formatter:off + client.search() + .forResource("Observation") + .where(Observation.VALUE_QUANTITY.withPrefix(ParamPrefixEnum.GREATERTHAN).number(123).andNoUnits()) + .returnBundle(Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://example.com/fhir/Observation?value-quantity=gt123||", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString())); + idx++; + + } + + @Test + public void testSearchByUrl() throws Exception { + final String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; + + ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class); + when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse); + when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK")); + when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8")); + when(myHttpResponse.getEntity().getContent()).then(new Answer() { + @Override + public InputStream answer(InvocationOnMock theInvocation) throws Throwable { + return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")); + }}); + + IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); + int idx = 0; + + //@formatter:off + client.search() + .forResource("Device") + .where(Device.URL.matches().value("http://foo.com")) + .returnBundle(Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://example.com/fhir/Device?url=http://foo.com", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString())); + assertEquals("http://example.com/fhir/Device?url=http%3A%2F%2Ffoo.com", capt.getAllValues().get(idx).getURI().toString()); + idx++; + + //@formatter:off + client.search() + .forResource("Device") + .where(Device.URL.matches().value(new StringDt("http://foo.com"))) + .returnBundle(Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://example.com/fhir/Device?url=http://foo.com", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString())); + idx++; + + } + + + @Test + public void testSearchByString() throws Exception { + final String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; + + ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class); + when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse); + when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK")); + when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8")); + when(myHttpResponse.getEntity().getContent()).then(new Answer() { + @Override + public InputStream answer(InvocationOnMock theInvocation) throws Throwable { + return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")); + }}); + + IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); + int idx = 0; + + //@formatter:off + client.search() + .forResource("Patient") + .where(Patient.NAME.matches().value("AAA")) + .returnBundle(Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://example.com/fhir/Patient?name=AAA", capt.getAllValues().get(idx).getURI().toString()); + idx++; + + //@formatter:off + client.search() + .forResource("Patient") + .where(Patient.NAME.matches().value(new StringDt("AAA"))) + .returnBundle(Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://example.com/fhir/Patient?name=AAA", capt.getAllValues().get(idx).getURI().toString()); + idx++; + + //@formatter:off + client.search() + .forResource("Patient") + .where(Patient.NAME.matches().values("AAA", "BBB")) + .returnBundle(Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://example.com/fhir/Patient?name=AAA,BBB", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString())); + idx++; + + //@formatter:off + client.search() + .forResource("Patient") + .where(Patient.NAME.matches().values(Arrays.asList("AAA", "BBB"))) + .returnBundle(Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://example.com/fhir/Patient?name=AAA,BBB", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString())); + idx++; + + + + //@formatter:off + client.search() + .forResource("Patient") + .where(Patient.NAME.matchesExactly().value("AAA")) + .returnBundle(Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://example.com/fhir/Patient?name%3Aexact=AAA", capt.getAllValues().get(idx).getURI().toString()); + idx++; + + //@formatter:off + client.search() + .forResource("Patient") + .where(Patient.NAME.matchesExactly().value(new StringDt("AAA"))) + .returnBundle(Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://example.com/fhir/Patient?name%3Aexact=AAA", capt.getAllValues().get(idx).getURI().toString()); + idx++; + + //@formatter:off + client.search() + .forResource("Patient") + .where(Patient.NAME.matchesExactly().values("AAA", "BBB")) + .returnBundle(Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://example.com/fhir/Patient?name:exact=AAA,BBB", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString())); + idx++; + + //@formatter:off + client.search() + .forResource("Patient") + .where(Patient.NAME.matchesExactly().values(Arrays.asList("AAA", "BBB"))) + .returnBundle(Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://example.com/fhir/Patient?name:exact=AAA,BBB", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString())); + idx++; + + } + + @Test + public void testSearchByDate() throws Exception { + final String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; + + ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class); + when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse); + when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK")); + when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8")); + when(myHttpResponse.getEntity().getContent()).then(new Answer() { + @Override + public InputStream answer(InvocationOnMock theInvocation) throws Throwable { + return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")); + }}); + + IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); + int idx = 0; + + Date date = new DateTimeDt("2011-01-02T22:33:01Z").getValue(); + + //@formatter:off + client.search() + .forResource("Patient") + .where(Patient.BIRTHDATE.after().day("2011-01-02")) + .returnBundle(Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://example.com/fhir/Patient?birthdate=gt2011-01-02", capt.getAllValues().get(idx).getURI().toString()); + idx++; + + //@formatter:off + client.search() + .forResource("Patient") + .where(Patient.BIRTHDATE.after().day(date)) + .returnBundle(Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://example.com/fhir/Patient?birthdate=gt2011-01-02", capt.getAllValues().get(idx).getURI().toString()); + idx++; + + //@formatter:off + client.search() + .forResource("Patient") + .where(Patient.BIRTHDATE.afterOrEquals().day("2011-01-02")) + .returnBundle(Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://example.com/fhir/Patient?birthdate=ge2011-01-02", capt.getAllValues().get(idx).getURI().toString()); + idx++; + + //@formatter:off + client.search() + .forResource("Patient") + .where(Patient.BIRTHDATE.before().day("2011-01-02")) + .returnBundle(Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://example.com/fhir/Patient?birthdate=lt2011-01-02", capt.getAllValues().get(idx).getURI().toString()); + idx++; + + //@formatter:off + client.search() + .forResource("Patient") + .where(Patient.BIRTHDATE.beforeOrEquals().day("2011-01-02")) + .returnBundle(Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://example.com/fhir/Patient?birthdate=le2011-01-02", capt.getAllValues().get(idx).getURI().toString()); + idx++; + + //@formatter:off + client.search() + .forResource("Patient") + .where(Patient.BIRTHDATE.exactly().day("2011-01-02")) + .returnBundle(Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://example.com/fhir/Patient?birthdate=2011-01-02", capt.getAllValues().get(idx).getURI().toString()); + idx++; + + //@formatter:off + client.search() + .forResource("Patient") + .where(Patient.BIRTHDATE.after().second("2011-01-02T22:33:01Z")) + .returnBundle(Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://example.com/fhir/Patient?birthdate=gt2011-01-02T22:33:01Z", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString())); + idx++; + + String expDate = new DateTimeDt(date).getValueAsString(); + + //@formatter:off + client.search() + .forResource("Patient") + .where(Patient.BIRTHDATE.after().second(date)) + .returnBundle(Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://example.com/fhir/Patient?birthdate=gt" + expDate, UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString())); + idx++; + + //@formatter:off + client.search() + .forResource("Patient") + .where(Patient.BIRTHDATE.after().now()) + .returnBundle(Bundle.class) + .execute(); + //@formatter:on + assertThat(capt.getAllValues().get(idx).getURI().toString(), startsWith("http://example.com/fhir/Patient?birthdate=gt2")); + String dateString = UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString()).substring(44); + ourLog.info(dateString); + assertEquals(TemporalPrecisionEnum.SECOND, new DateTimeDt(dateString).getPrecision()); + idx++; + } + } diff --git a/src/changes/changes.xml b/src/changes/changes.xml index a8f7edcf7a9..b79b82cfc6f 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -263,6 +263,14 @@ Update STU3 client and server to use the new sort parameter style (param1,-param2,param). Thanks to GitHub user @euz1e4r for reporting! + + QuantityClientParam#withUnit(String) put the unit into the system part of the + parameter value + + + Fluent client searches with date parameters were not correctly using + new prefix style (e.g. gt) instead of old one (e.g. >) +