Clean up testing

This commit is contained in:
James Agnew 2015-10-08 17:38:14 -04:00
parent e29e10450c
commit 4dbd180cf6
11 changed files with 106 additions and 256 deletions

View File

@ -1,43 +1,17 @@
package ca.uhn.fhir.context;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import static org.apache.commons.lang3.StringUtils.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.commons.lang3.text.WordUtils;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IBase;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.util.BeanUtils;
public abstract class BaseRuntimeDeclaredChildDefinition extends BaseRuntimeChildDefinition {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseRuntimeDeclaredChildDefinition.class);
private final IAccessor myAccessor;
private final String myElementName;
private final Field myField;
@ -48,22 +22,13 @@ public abstract class BaseRuntimeDeclaredChildDefinition extends BaseRuntimeChil
private final IMutator myMutator;
private final String myShortDefinition;
private boolean mySummary;
private Boolean ourUseMethodAccessors;
BaseRuntimeDeclaredChildDefinition(Field theField, Child theChildAnnotation, Description theDescriptionAnnotation, String theElementName) throws ConfigurationException {
super();
if (theField == null) {
throw new IllegalArgumentException("No field speficied");
}
if (theChildAnnotation.min() < 0) {
throw new ConfigurationException("Min must be >= 0");
}
if (theChildAnnotation.max() != -1 && theChildAnnotation.max() < theChildAnnotation.min()) {
throw new ConfigurationException("Max must be >= Min (unless it is -1 / unlimited)");
}
if (isBlank(theElementName)) {
throw new ConfigurationException("Element name must not be blank");
}
Validate.notNull(theField, "No field speficied");
Validate.inclusiveBetween(0, Integer.MAX_VALUE, theChildAnnotation.min(), "Min must be >= 0");
Validate.isTrue(theChildAnnotation.max() == -1 || theChildAnnotation.max() >= theChildAnnotation.min(), "Max must be >= Min (unless it is -1 / unlimited)");
Validate.notBlank(theElementName, "Element name must not be blank");
myField = theField;
myMin = theChildAnnotation.min();
@ -79,69 +44,14 @@ public abstract class BaseRuntimeDeclaredChildDefinition extends BaseRuntimeChil
myFormalDefinition = null;
}
// TODO: handle lists (max>0), and maybe max=0?
// TODO: finish implementing field level accessors/mutators
if (ourUseMethodAccessors == null) {
try {
myField.setAccessible(true);
ourUseMethodAccessors = false;
} catch (SecurityException e) {
ourLog.info("Can not use field accessors/mutators, going to use methods instead");
ourUseMethodAccessors = true;
}
}
if (ourUseMethodAccessors == false) {
if (List.class.equals(myField.getType())) {
// TODO: verify that generic type is IElement
myAccessor = new FieldListAccessor();
myMutator = new FieldListMutator();
} else {
myAccessor = new FieldPlainAccessor();
myMutator = new FieldPlainMutator();
}
myField.setAccessible(true);
if (List.class.equals(myField.getType())) {
// TODO: verify that generic type is IElement
myAccessor = new FieldListAccessor();
myMutator = new FieldListMutator();
} else {
Class<?> declaringClass = myField.getDeclaringClass();
final Class<?> targetReturnType = myField.getType();
try {
String elementName = myElementName;
if ("class".equals(elementName.toLowerCase())) {
elementName = "classElement"; // because getClass() is reserved
}
final Method accessor = BeanUtils.findAccessor(declaringClass, targetReturnType, elementName);
if (accessor == null) {
StringBuilder b = new StringBuilder();
b.append("Could not find bean accessor/getter for property ");
b.append(elementName);
b.append(" on class ");
b.append(declaringClass.getCanonicalName());
throw new ConfigurationException(b.toString());
}
final Method mutator = findMutator(declaringClass, targetReturnType, elementName);
if (mutator == null) {
StringBuilder b = new StringBuilder();
b.append("Could not find bean mutator/setter for property ");
b.append(elementName);
b.append(" on class ");
b.append(declaringClass.getCanonicalName());
b.append(" (expected return type ");
b.append(targetReturnType.getCanonicalName());
b.append(")");
throw new ConfigurationException(b.toString());
}
if (List.class.isAssignableFrom(targetReturnType)) {
myAccessor = new ListAccessor(accessor);
myMutator = new ListMutator(mutator);
} else {
myAccessor = new PlainAccessor(accessor);
myMutator = new PlainMutator(targetReturnType, mutator);
}
} catch (NoSuchFieldException e) {
throw new ConfigurationException(e);
}
myAccessor = new FieldPlainAccessor();
myMutator = new FieldPlainMutator();
}
}
@ -198,17 +108,6 @@ public abstract class BaseRuntimeDeclaredChildDefinition extends BaseRuntimeChil
return mySummary;
}
private static Method findMutator(Class<?> theDeclaringClass, Class<?> theTargetReturnType, String theElementName) {
String methodName = "set" + WordUtils.capitalize(theElementName);
try {
return theDeclaringClass.getMethod(methodName, theTargetReturnType);
} catch (NoSuchMethodException e) {
return null;
} catch (SecurityException e) {
throw new ConfigurationException("Failed to scan class '" + theDeclaringClass + "' because of a security exception", e);
}
}
private final class FieldListAccessor implements IAccessor {
@SuppressWarnings("unchecked")
@Override
@ -216,11 +115,10 @@ public abstract class BaseRuntimeDeclaredChildDefinition extends BaseRuntimeChil
List<IBase> retVal;
try {
retVal = (List<IBase>) myField.get(theTarget);
} catch (IllegalArgumentException e) {
throw new ConfigurationException("Failed to get value", e);
} catch (IllegalAccessException e) {
} catch (Exception e) {
throw new ConfigurationException("Failed to get value", e);
}
if (retVal == null) {
retVal = Collections.emptyList();
}
@ -246,9 +144,7 @@ public abstract class BaseRuntimeDeclaredChildDefinition extends BaseRuntimeChil
existingList.clear();
}
existingList.add(theValue);
} catch (IllegalArgumentException e) {
throw new ConfigurationException("Failed to set value", e);
} catch (IllegalAccessException e) {
} catch (Exception e) {
throw new ConfigurationException("Failed to set value", e);
}
}
@ -267,11 +163,9 @@ public abstract class BaseRuntimeDeclaredChildDefinition extends BaseRuntimeChil
if (values == null) {
return Collections.emptyList();
}
List<IBase> retVal = Collections.singletonList((IBase)values);
List<IBase> retVal = Collections.singletonList((IBase) values);
return retVal;
} catch (IllegalArgumentException e) {
throw new ConfigurationException("Failed to get value", e);
} catch (IllegalAccessException e) {
} catch (Exception e) {
throw new ConfigurationException("Failed to get value", e);
}
}
@ -282,125 +176,8 @@ public abstract class BaseRuntimeDeclaredChildDefinition extends BaseRuntimeChil
public void addValue(Object theTarget, IBase theValue) {
try {
myField.set(theTarget, theValue);
} catch (IllegalArgumentException e) {
} catch (Exception e) {
throw new ConfigurationException("Failed to set value", e);
} catch (IllegalAccessException e) {
throw new ConfigurationException("Failed to set value", e);
}
}
@Override
public void setValue(Object theTarget, IBase theValue) {
addValue(theTarget, theValue);
}
}
private static final class ListAccessor implements IAccessor {
private final Method myAccessorMethod;
private ListAccessor(Method theAccessor) {
myAccessorMethod = theAccessor;
}
@SuppressWarnings("unchecked")
@Override
public List<IBase> getValues(Object theTarget) {
try {
return (List<IBase>) myAccessorMethod.invoke(theTarget);
} catch (IllegalAccessException e) {
throw new ConfigurationException("Failed to get value", e);
} catch (IllegalArgumentException e) {
throw new ConfigurationException("Failed to get value", e);
} catch (InvocationTargetException e) {
throw new ConfigurationException("Failed to get value", e);
}
}
}
private final class ListMutator implements IMutator {
private final Method myMutatorMethod;
private ListMutator(Method theMutator) {
myMutatorMethod = theMutator;
}
private void addValue(Object theTarget, boolean theClear, IBase theValue) {
List<IBase> existingList = myAccessor.getValues(theTarget);
if (existingList == null) {
existingList = new ArrayList<IBase>();
try {
myMutatorMethod.invoke(theTarget, existingList);
} catch (IllegalAccessException e) {
throw new ConfigurationException("Failed to get value", e);
} catch (IllegalArgumentException e) {
throw new ConfigurationException("Failed to get value", e);
} catch (InvocationTargetException e) {
throw new ConfigurationException("Failed to get value", e);
}
}
if (theClear) {
existingList.clear();
}
existingList.add(theValue);
}
@Override
public void addValue(Object theTarget, IBase theValue) {
addValue(theTarget, false, theValue);
}
@Override
public void setValue(Object theTarget, IBase theValue) {
addValue(theTarget, true, theValue);
}
}
private final class PlainAccessor implements IAccessor {
private final Method myAccessorMethod;
private PlainAccessor(Method theAccessor) {
myAccessorMethod = theAccessor;
}
@Override
public List<IBase> getValues(Object theTarget) {
try {
return Collections.singletonList((IBase)myAccessorMethod.invoke(theTarget));
} catch (IllegalAccessException e) {
throw new ConfigurationException("Failed to get value", e);
} catch (IllegalArgumentException e) {
throw new ConfigurationException("Failed to get value", e);
} catch (InvocationTargetException e) {
throw new ConfigurationException("Failed to get value", e);
}
}
}
private final class PlainMutator implements IMutator {
private final Method myMutatorMethod;
private final Class<?> myTargetReturnType;
private PlainMutator(Class<?> theTargetReturnType, Method theMutator) {
assert theTargetReturnType != null;
assert theMutator != null;
myTargetReturnType = theTargetReturnType;
myMutatorMethod = theMutator;
}
@Override
public void addValue(Object theTarget, IBase theValue) {
try {
if (theValue != null && !myTargetReturnType.isAssignableFrom(theValue.getClass())) {
throw new ConfigurationException("Value for field " + myElementName + " expects type " + myTargetReturnType + " but got " + theValue.getClass());
}
myMutatorMethod.invoke(theTarget, theValue);
} catch (IllegalAccessException e) {
throw new ConfigurationException("Failed to get value", e);
} catch (IllegalArgumentException e) {
throw new ConfigurationException("Failed to get value", e);
} catch (InvocationTargetException e) {
throw new ConfigurationException("Failed to get value", e);
}
}

View File

@ -0,0 +1,4 @@
<div>
<th:block th:if="${not resource.valueElement.empty}" th:text="${resource.valueElement.valueAsString}"/>
<th:block th:if="${not resource.unitsElement.empty}" th:text="${resource.unitsElement.value}"/>
</div>

View File

@ -39,6 +39,9 @@ period.narrative=classpath:ca/uhn/fhir/narrative/datatype/PeriodDt.html
quantity.class=ca.uhn.fhir.model.dstu.composite.QuantityDt
quantity.narrative=classpath:ca/uhn/fhir/narrative/datatype/QuantityDt.html
simplequantity.class=ca.uhn.fhir.model.dstu.composite.SimpleQuantityDt
simplequantity.narrative=classpath:ca/uhn/fhir/narrative/datatype/SimpleQuantityDt.html
################################################
# Resources
################################################

View File

@ -133,7 +133,7 @@ public class ValidationDataUploader extends BaseCommand {
StructureDefinition next;
try {
next = ctx.newXmlParser().parseResource(StructureDefinition.class, IOUtils.toString(i.getInputStream(), "UTF-8"));
} catch (DataFormatException | IOException e) {
} catch (Exception e) {
throw new CommandFailureException(e.toString());
}
next.setId(next.getIdElement().toUnqualifiedVersionless());

View File

@ -64,7 +64,7 @@
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.6.0</version>
<version>${slf4j_target_version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>

View File

@ -41,6 +41,14 @@
<artifactId>hapi-fhir-validation-resources-dstu2</artifactId>
<version>1.3-SNAPSHOT</version>
</dependency>
<!-- Don't force OSGi users to use a newer version of SLF4j than we need -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j_target_version}</version>
</dependency>
</dependencies>
<build>

View File

@ -90,6 +90,7 @@ public class NarrativeDt extends BaseNarrativeDt<NarrativeStatusEnum> {
return ca.uhn.fhir.util.ElementUtil.allPopulatedChildElements( theType, myStatus, myDiv );
}
/**
* Gets the value(s) for <b>status</b> (generated | extensions | additional).
* creating it if it does

View File

@ -98,6 +98,20 @@ public class NarrativeDt extends BaseNarrativeDt {
* <p>
* <b>Definition:</b>
* The status of the narrative - whether it's entirely generated (from just the defined data or the extensions too), or whether a human authored it and it may contain additional data
* </p>
*/
public BoundCodeDt<NarrativeStatusEnum> getStatusElement() {
return getStatus();
}
/**
* Gets the value(s) for <b>status</b> (generated | extensions | additional).
* creating it if it does
* not exist. Will not return <code>null</code>.
*
* <p>
* <b>Definition:</b>
* The status of the narrative - whether it's entirely generated (from just the defined data or the extensions too), or whether a human authored it and it may contain additional data
* </p>
*/
public BoundCodeDt<NarrativeStatusEnum> getStatus() {
@ -132,6 +146,20 @@ public class NarrativeDt extends BaseNarrativeDt {
}
/**
* Gets the value(s) for <b>div</b> (Limited xhtml content).
* creating it if it does
* not exist. Will not return <code>null</code>.
*
* <p>
* <b>Definition:</b>
* The actual narrative content, a stripped down version of XHTML
* </p>
*/
public XhtmlDt getDivElement() {
return getDiv();
}
/**
* Gets the value(s) for <b>div</b> (Limited xhtml content).
* creating it if it does

View File

@ -17,6 +17,7 @@ import org.apache.http.HttpResponse;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
@ -163,7 +164,20 @@ public class SearchDstu2Test {
assertEquals("2002", ourLastDateAndList.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(1).getValueAsString());
assertThat(responseContent, containsString("SYSTEM"));
}
@Test
public void testSearchByPut() throws Exception {
HttpPut httpGet = new HttpPut("http://localhost:" + ourPort + "/Patient/_search");
StringEntity entity = new StringEntity("searchDateAndList=2001,2002&searchDateAndList=2003,2004", ContentType.APPLICATION_FORM_URLENCODED);
httpGet.setEntity(entity);
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info(responseContent);
assertEquals(400, status.getStatusLine().getStatusCode());
}
@Test
public void testSearchByPostWithBodyAndUrlParams() throws Exception {

View File

@ -1,8 +1,13 @@
package ca.uhn.fhir.rest.server;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.Collection;
import java.util.List;
@ -282,7 +287,7 @@ public class ServerConformanceProviderDstu2Test {
assertEquals("1", opDef.getParameter().get(2).getMinElement().getValueAsString());
assertEquals("2", opDef.getParameter().get(2).getMaxElement().getValueAsString());
assertEquals("string", opDef.getParameter().get(2).getTypeElement().getValueAsString());
}
}
@Test
public void testProviderWithRequiredAndOptional() throws Exception {
@ -451,7 +456,7 @@ public class ServerConformanceProviderDstu2Test {
}
@Delete
public MethodOutcome delete(@IdParam IdDt theId, @ConditionalUrlParam(supportsMultiple=true) String theConditionalUrl) {
public MethodOutcome delete(@IdParam IdDt theId, @ConditionalUrlParam(supportsMultiple = true) String theConditionalUrl) {
return null;
}
@ -486,7 +491,8 @@ public class ServerConformanceProviderDstu2Test {
public static class MultiOptionalProvider {
@Search(type = Patient.class)
public Patient findPatient(@Description(shortDefinition = "The patient's identifier") @OptionalParam(name = Patient.SP_IDENTIFIER) IdentifierDt theIdentifier, @Description(shortDefinition = "The patient's name") @OptionalParam(name = Patient.SP_NAME) StringDt theName) {
public Patient findPatient(@Description(shortDefinition = "The patient's identifier") @OptionalParam(name = Patient.SP_IDENTIFIER) IdentifierDt theIdentifier,
@Description(shortDefinition = "The patient's name") @OptionalParam(name = Patient.SP_NAME) StringDt theName) {
return null;
}
@ -518,10 +524,9 @@ public class ServerConformanceProviderDstu2Test {
public static class PlainProviderWithExtendedOperationOnNoType {
@Operation(name = "plain", idempotent = true, returnParameters= {
@OperationParam(min=1, max=2, name="out1", type=StringDt.class)
})
public ca.uhn.fhir.rest.server.IBundleProvider everything(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam ca.uhn.fhir.model.primitive.IdDt theId, @OperationParam(name = "start") DateDt theStart, @OperationParam(name = "end") DateDt theEnd) {
@Operation(name = "plain", idempotent = true, returnParameters = { @OperationParam(min = 1, max = 2, name = "out1", type = StringDt.class) })
public ca.uhn.fhir.rest.server.IBundleProvider everything(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam ca.uhn.fhir.model.primitive.IdDt theId,
@OperationParam(name = "start") DateDt theStart, @OperationParam(name = "end") DateDt theEnd) {
return null;
}
@ -530,7 +535,8 @@ public class ServerConformanceProviderDstu2Test {
public static class ProviderWithExtendedOperationReturningBundle implements IResourceProvider {
@Operation(name = "everything", idempotent = true)
public ca.uhn.fhir.rest.server.IBundleProvider everything(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam ca.uhn.fhir.model.primitive.IdDt theId, @OperationParam(name = "start") DateDt theStart, @OperationParam(name = "end") DateDt theEnd) {
public ca.uhn.fhir.rest.server.IBundleProvider everything(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam ca.uhn.fhir.model.primitive.IdDt theId,
@OperationParam(name = "start") DateDt theStart, @OperationParam(name = "end") DateDt theEnd) {
return null;
}
@ -545,8 +551,9 @@ public class ServerConformanceProviderDstu2Test {
@Description(shortDefinition = "This is a search for stuff!")
@Search
public List<DiagnosticReport> findDiagnosticReportsByPatient(@RequiredParam(name = DiagnosticReport.SP_SUBJECT + '.' + Patient.SP_IDENTIFIER) IdentifierDt thePatientId, @OptionalParam(name = DiagnosticReport.SP_CODE) TokenOrListParam theNames,
@OptionalParam(name = DiagnosticReport.SP_DATE) DateRangeParam theDateRange, @IncludeParam(allow = { "DiagnosticReport.result" }) Set<Include> theIncludes) throws Exception {
public List<DiagnosticReport> findDiagnosticReportsByPatient(@RequiredParam(name = DiagnosticReport.SP_SUBJECT + '.' + Patient.SP_IDENTIFIER) IdentifierDt thePatientId,
@OptionalParam(name = DiagnosticReport.SP_CODE) TokenOrListParam theNames, @OptionalParam(name = DiagnosticReport.SP_DATE) DateRangeParam theDateRange,
@IncludeParam(allow = { "DiagnosticReport.result" }) Set<Include> theIncludes) throws Exception {
return null;
}

View File

@ -237,6 +237,14 @@
<thymeleaf-version>2.1.4.RELEASE</thymeleaf-version>
<ebay_cors_filter_version>1.0.1</ebay_cors_filter_version>
<xmlunit_version>1.6</xmlunit_version>
<!--
We are aiming to still work on a very old version of SLF4j even though
we depend on the newest, just to be nice to users of the API. This version
is tested in the hapi-fhir-cobertura.
-->
<slf4j_target_version>1.6.0</slf4j_target_version>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>