diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java index 653199656d6..7d246881f68 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java @@ -721,7 +721,7 @@ public class GenericClient extends BaseClient implements IGenericClient { protected IBaseResource parseResourceBody(String theResourceBody) { EncodingEnum encoding = MethodUtil.detectEncodingNoDefault(theResourceBody); if (encoding == null) { - throw new InvalidRequestException("FHIR client can't determine resource encoding"); + throw new IllegalArgumentException(myContext.getLocalizer().getMessage(GenericClient.class, "cantDetermineRequestType")); } return encoding.newParser(myContext).parseResource(theResourceBody); } @@ -2220,7 +2220,7 @@ public class GenericClient extends BaseClient implements IGenericClient { myRawBundle = theBundle; myRawBundleEncoding = MethodUtil.detectEncodingNoDefault(myRawBundle); if (myRawBundleEncoding == null) { - throw new IllegalArgumentException("Can not determine encoding of raw resource body"); + throw new IllegalArgumentException(myContext.getLocalizer().getMessage(GenericClient.class, "cantDetermineRequestType")); } } @@ -2431,7 +2431,7 @@ public class GenericClient extends BaseClient implements IGenericClient { EncodingEnum enc = MethodUtil.detectEncodingNoDefault(theResourceRaw); if (enc == null) { - throw new IllegalArgumentException("Could not detect encoding (XML/JSON) in string. Is this a valid FHIR resource?"); + throw new IllegalArgumentException(myContext.getLocalizer().getMessage(GenericClient.class, "cantDetermineRequestType")); } switch (enc) { case XML: diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseHttpClientInvocationWithContents.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseHttpClientInvocationWithContents.java index 865d2ed04ea..cb485611393 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseHttpClientInvocationWithContents.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseHttpClientInvocationWithContents.java @@ -25,6 +25,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; import java.util.List; import java.util.Map; +import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.api.IBaseBinary; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -200,9 +201,7 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca if (myParams != null) { return httpClient.createParamRequest(getContext(), myParams, encoding); } else { - if (encoding == null) { - encoding = EncodingEnum.XML; - } + encoding = ObjectUtils.defaultIfNull(encoding, EncodingEnum.XML); String contents = encodeContents(thePrettyPrint, encoding); String contentType = getContentType(encoding); return httpClient.createByteRequest(getContext(), contents, contentType, encoding); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseResourceReturningMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseResourceReturningMethodBinding.java index 829195f5c7c..c2068a85d78 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseResourceReturningMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseResourceReturningMethodBinding.java @@ -36,6 +36,7 @@ import java.util.Set; import java.util.TreeSet; import org.hl7.fhir.instance.model.api.IBaseBundle; +import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; @@ -45,7 +46,6 @@ import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.model.api.Bundle; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.Include; -import ca.uhn.fhir.model.base.resource.BaseOperationOutcome; import ca.uhn.fhir.model.valueset.BundleTypeEnum; import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.rest.api.MethodOutcome; @@ -232,7 +232,7 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi return resource; case METHOD_OUTCOME: MethodOutcome retVal = new MethodOutcome(); - retVal.setOperationOutcome((BaseOperationOutcome) resource); + retVal.setOperationOutcome((IBaseOperationOutcome) resource); return retVal; } break; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java index 6f2b8a00b73..384f92d477c 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java @@ -6,10 +6,8 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; import java.io.IOException; import java.io.PushbackReader; import java.io.Reader; -import java.io.UnsupportedEncodingException; import java.lang.annotation.Annotation; import java.lang.reflect.Method; -import java.net.URLEncoder; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -23,6 +21,7 @@ import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBaseMetaType; @@ -46,7 +45,6 @@ import ca.uhn.fhir.model.api.TagList; import ca.uhn.fhir.model.api.annotation.Description; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.InstantDt; -import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.rest.annotation.At; import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; @@ -89,7 +87,9 @@ import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.IDynamicSearchResourceProvider; import ca.uhn.fhir.rest.server.SearchParameterMap; import ca.uhn.fhir.util.DateUtils; +import ca.uhn.fhir.util.ParametersUtil; import ca.uhn.fhir.util.ReflectionUtil; +import ca.uhn.fhir.util.UrlUtil; /* * #%L @@ -113,6 +113,12 @@ import ca.uhn.fhir.util.ReflectionUtil; @SuppressWarnings("deprecation") public class MethodUtil { + + /** Non instantiable */ + private MethodUtil() { + // nothing + } + private static final String LABEL = "label=\""; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(MethodUtil.class); @@ -132,19 +138,14 @@ public class MethodUtil { } - public static IIdType convertIdToType(IIdType value, Class idParamType) { - if (value != null && !idParamType.isAssignableFrom(value.getClass())) { - try { - IIdType newValue = idParamType.newInstance(); - newValue.setValue(value.getValue()); - value = newValue; - } catch (InstantiationException e) { - throw new ConfigurationException("Failed to instantiate " + idParamType, e); - } catch (IllegalAccessException e) { - throw new ConfigurationException("Failed to instantiate " + idParamType, e); - } + @SuppressWarnings("unchecked") + public static T convertIdToType(IIdType value, Class theIdParamType) { + if (value != null && !theIdParamType.isAssignableFrom(value.getClass())) { + IIdType newValue = ReflectionUtil.newInstance(theIdParamType); + newValue.setValue(value.getValue()); + value = newValue; } - return value; + return (T) value; } public static HttpGetClientInvocation createConformanceInvocation(FhirContext theContext) { @@ -212,13 +213,9 @@ public class MethodUtil { for (String nextValue : nextEntry.getValue()) { b.append(haveQuestionMark ? '&' : '?'); haveQuestionMark = true; - try { - b.append(URLEncoder.encode(nextEntry.getKey(), "UTF-8")); - b.append('='); - b.append(URLEncoder.encode(nextValue, "UTF-8")); - } catch (UnsupportedEncodingException e) { - throw new ConfigurationException("UTF-8 not supported on this platform"); - } + b.append(UrlUtil.escape(nextEntry.getKey())); + b.append('='); + b.append(UrlUtil.escape(nextValue)); } } @@ -287,9 +284,7 @@ public class MethodUtil { public static EncodingEnum detectEncoding(String theBody) { EncodingEnum retVal = detectEncodingNoDefault(theBody); - if (retVal == null) { - retVal = EncodingEnum.XML; - } + retVal = ObjectUtils.defaultIfNull(retVal, EncodingEnum.XML); return retVal; } @@ -321,10 +316,6 @@ public class MethodUtil { } } - public static Integer findConditionalOperationParameterIndex(Method theMethod) { - return MethodUtil.findParamAnnotationIndex(theMethod, ConditionalUrlParam.class); - } - public static Integer findIdParameterIndex(Method theMethod, FhirContext theContext) { Integer index = MethodUtil.findParamAnnotationIndex(theMethod, IdParam.class); if (index != null) { @@ -365,7 +356,7 @@ public class MethodUtil { } @SuppressWarnings("unchecked") - public static List getResourceParameters(FhirContext theContext, Method theMethod, Object theProvider, RestOperationTypeEnum theRestfulOperationTypeEnum) { + public static List getResourceParameters(final FhirContext theContext, Method theMethod, Object theProvider, RestOperationTypeEnum theRestfulOperationTypeEnum) { List parameters = new ArrayList(); Class[] parameterTypes = theMethod.getParameterTypes(); @@ -514,7 +505,7 @@ public class MethodUtil { @Override public Object outgoingClient(Object theObject) { - return new StringDt(((ValidationModeEnum)theObject).getCode()); + return ParametersUtil.createString(theContext, ((ValidationModeEnum)theObject).getCode()); } }); } else if (nextAnnotation instanceof Validate.Profile) { @@ -529,7 +520,7 @@ public class MethodUtil { @Override public Object outgoingClient(Object theObject) { - return new StringDt(theObject.toString()); + return ParametersUtil.createString(theContext, theObject.toString()); } }); } else { diff --git a/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties b/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties index 834ea081e80..26e93c133e5 100644 --- a/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties +++ b/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties @@ -10,6 +10,7 @@ ca.uhn.fhir.context.RuntimeResourceDefinition.nonInstantiableType=Resource type ca.uhn.fhir.rest.client.BaseClient.ioExceptionDuringOperation=Encountered IOException when performing {0} to URL {1} - {2} ca.uhn.fhir.rest.client.BaseClient.failedToParseResponse=Failed to parse response from server when performing {0} to URL {1} - {2} +ca.uhn.fhir.rest.client.GenericClient.cantDetermineRequestType=Unable to determing encoding of request (body does not appear to be valid XML or JSON) ca.uhn.fhir.rest.client.GenericClient.noPagingLinkFoundInBundle=Can not perform paging operation because no link was found in Bundle with relation "{0}" ca.uhn.fhir.rest.client.GenericClient.noVersionIdForVread=No version specified in URL for 'vread' operation: {0} ca.uhn.fhir.rest.client.GenericClient.incompleteUriForRead=The given URI is not an absolute URL and is not usable for this operation: {0} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java index c096b17688d..e6fc5d6e859 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java @@ -51,8 +51,13 @@ public class DaoConfig { // *** // update setter javadoc if default changes // *** + private int myDeferIndexingForCodesystemsOfSize = 100; + // *** + // update setter javadoc if default changes + // *** private long myExpireSearchResultsAfterMillis = DateUtils.MILLIS_PER_HOUR; private int myHardSearchLimit = 1000; + private int myHardTagListLimit = 1000; private int myIncludeLimit = 2000; @@ -61,24 +66,36 @@ public class DaoConfig { // update setter javadoc if default changes // *** private boolean myIndexContainedResources = true; - + private List myInterceptors; + // *** // update setter javadoc if default changes // *** private int myMaximumExpansionSize = 5000; - + private ResourceEncodingEnum myResourceEncoding = ResourceEncodingEnum.JSONC; - private boolean mySchedulingDisabled; - - private boolean mySubscriptionEnabled; - - private long mySubscriptionPollDelay = 1000; - private Long mySubscriptionPurgeInactiveAfterMillis; - private Set myTreatBaseUrlsAsLocal = new HashSet(); + private boolean mySubscriptionEnabled; + + private long mySubscriptionPollDelay = 1000; + private Long mySubscriptionPurgeInactiveAfterMillis; + + private Set myTreatBaseUrlsAsLocal = new HashSet(); + + /** + * When a code system is added that contains more than this number of codes, + * the code system will be indexed later in an incremental process in order to + * avoid overwhelming Lucene with a huge number of codes in a single operation. + *

+ * Defaults to 100 + *

+ */ + public int getDeferIndexingForCodesystemsOfSize() { + return myDeferIndexingForCodesystemsOfSize; + } /** * Sets the number of milliseconds that search results for a given client search * should be preserved before being purged from the database. @@ -117,6 +134,7 @@ public class DaoConfig { } return myInterceptors; } + /** * See {@link #setMaximumExpansionSize(int)} */ @@ -180,7 +198,6 @@ public class DaoConfig { public boolean isAllowInlineMatchUrlReferences() { return myAllowInlineMatchUrlReferences; } - public boolean isAllowMultipleDelete() { return myAllowMultipleDelete; } @@ -251,6 +268,18 @@ public class DaoConfig { myAllowMultipleDelete = theAllowMultipleDelete; } + /** + * When a code system is added that contains more than this number of codes, + * the code system will be indexed later in an incremental process in order to + * avoid overwhelming Lucene with a huge number of codes in a single operation. + *

+ * Defaults to 100 + *

+ */ + public void setDeferIndexingForCodesystemsOfSize(int theDeferIndexingForCodesystemsOfSize) { + myDeferIndexingForCodesystemsOfSize = theDeferIndexingForCodesystemsOfSize; + } + /** * Sets the number of milliseconds that search results for a given client search * should be preserved before being purged from the database. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java index a568c45bb15..b3e8e86bdf0 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java @@ -71,12 +71,11 @@ import ca.uhn.fhir.jpa.search.DeferConceptIndexingInterceptor; //@formatter:on public class TermConcept implements Serializable { private static final int MAX_DESC_LENGTH = 400; - private static final long serialVersionUID = 1L; - + @OneToMany(fetch=FetchType.LAZY, mappedBy="myParent") private Collection myChildren; - + @Column(name="CODE", length=100, nullable=false) @Fields({ @Field(name = "myCode", index = org.hibernate.search.annotations.Index.YES, store = Store.YES, analyze = Analyze.YES, analyzer = @Analyzer(definition = "exactAnalyzer")), @@ -86,7 +85,7 @@ public class TermConcept implements Serializable { @ManyToOne() @JoinColumn(name="CODESYSTEM_PID", referencedColumnName="PID", foreignKey=@ForeignKey(name="FK_CONCEPT_PID_CS_PID")) private TermCodeSystemVersion myCodeSystem; - + @Column(name="CODESYSTEM_PID", insertable=false, updatable=false) @Fields({ @Field(name="myCodeSystemVersionPid") @@ -103,7 +102,7 @@ public class TermConcept implements Serializable { }) private String myDisplay; //@formatter:on - + @Id() @SequenceGenerator(name="SEQ_CONCEPT_PID", sequenceName="SEQ_CONCEPT_PID") @GeneratedValue(strategy=GenerationType.AUTO, generator="SEQ_CONCEPT_PID") @@ -189,6 +188,10 @@ public class TermConcept implements Serializable { return myId; } + public Long getIndexStatus() { + return myIndexStatus; + } + public Collection getParents() { if (myParents == null) { myParents = new ArrayList(); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/DeferConceptIndexingInterceptor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/DeferConceptIndexingInterceptor.java index f01a2df3761..eefe314a686 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/DeferConceptIndexingInterceptor.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/DeferConceptIndexingInterceptor.java @@ -1,9 +1,32 @@ package ca.uhn.fhir.jpa.search; -import org.hibernate.search.indexes.interceptor.DontInterceptEntityInterceptor; +import org.hibernate.search.indexes.interceptor.EntityIndexingInterceptor; +import org.hibernate.search.indexes.interceptor.IndexingOverride; -public class DeferConceptIndexingInterceptor extends DontInterceptEntityInterceptor -//implements EntityIndexingInterceptor -{ - // nothing for now +import ca.uhn.fhir.jpa.entity.TermConcept; + +public class DeferConceptIndexingInterceptor implements EntityIndexingInterceptor { + + @Override + public IndexingOverride onAdd(TermConcept theEntity) { + if (theEntity.getIndexStatus() == null) { + return IndexingOverride.SKIP; + } + return IndexingOverride.APPLY_DEFAULT; + } + + @Override + public IndexingOverride onUpdate(TermConcept theEntity) { + return onAdd(theEntity); + } + + @Override + public IndexingOverride onDelete(TermConcept theEntity) { + return IndexingOverride.APPLY_DEFAULT; + } + + @Override + public IndexingOverride onCollectionUpdate(TermConcept theEntity) { + return IndexingOverride.APPLY_DEFAULT; + } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvc.java index 6349c4af9bc..f163786e3ff 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvc.java @@ -216,7 +216,9 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc { } theConcept.setCodeSystem(theCodeSystem); - theConcept.setIndexStatus(BaseHapiFhirDao.INDEX_STATUS_INDEXED); + if (theTotalConcepts < myDaoConfig.getDeferIndexingForCodesystemsOfSize()) { + theConcept.setIndexStatus(BaseHapiFhirDao.INDEX_STATUS_INDEXED); + } myConceptDao.save(theConcept); for (TermConceptParentChildLink next : theConcept.getChildren()) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java index 0b1918d581a..8a78be41460 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java @@ -174,7 +174,7 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvc implements I } else if (nextFilter.getOp() == FilterOperator.ISA) { if (isNotBlank(nextFilter.getValue())) { TermConcept code = super.findCode(system, nextFilter.getValue()); - bool.must(qb.keyword().onField("myParentPids").matching(code.getId()).createQuery()); + bool.must(qb.keyword().onField("myParentPids").matching("" + code.getId()).createQuery()); } } else { throw new InvalidRequestException("Unknown filter property[" + nextFilter + "] + op[" + nextFilter.getOpElement().getValueAsString() + "]"); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java index cc0da5e0242..55ca56d99f9 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java @@ -9,7 +9,6 @@ import static org.junit.Assert.fail; import java.util.ArrayList; import java.util.List; import java.util.Set; -import java.util.TreeSet; import org.hl7.fhir.dstu3.model.AuditEvent; import org.hl7.fhir.dstu3.model.CodeSystem; @@ -22,11 +21,13 @@ import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.dstu3.model.ValueSet.FilterOperator; import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionContainsComponent; import org.hl7.fhir.instance.model.api.IIdType; +import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; +import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.jpa.dao.SearchParameterMap; import ca.uhn.fhir.jpa.entity.ResourceTable; import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; @@ -339,6 +340,25 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { assertEquals(URL_MY_CODE_SYSTEM, result.getExpansion().getContains().get(idx).getSystem()); } + @Test + public void testIndexingIsDeferredForLargeCodeSystems() { + myDaoConfig.setDeferIndexingForCodesystemsOfSize(1); + + createExternalCsAndLocalVs(); + + ValueSet vs = new ValueSet(); + ConceptSetComponent include = vs.getCompose().addInclude(); + include.setSystem(URL_MY_CODE_SYSTEM); + + include.addFilter().setProperty("display").setOp(FilterOperator.ISA).setValue("ParentA"); + + ValueSet result = myValueSetDao.expand(vs, null); + + String encoded = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result); + ourLog.info(encoded); + + } + @Test public void testExpandWithExcludeInExternalValueSet() { createExternalCsAndLocalVs(); @@ -486,6 +506,11 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { public void before() { myDaoConfig.setMaximumExpansionSize(5000); } + + @After + public void after() { + myDaoConfig.setDeferIndexingForCodesystemsOfSize(new DaoConfig().getDeferIndexingForCodesystemsOfSize()); + } @Test public void testSearchCodeBelowLocalCodesystem() { diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerInvalidDefinitionDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerInvalidDefinitionDstu2Test.java index a479669e19d..d62a583bce3 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerInvalidDefinitionDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerInvalidDefinitionDstu2Test.java @@ -18,6 +18,7 @@ import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.Update; +import ca.uhn.fhir.rest.annotation.Validate; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.util.TestUtil; @@ -62,6 +63,51 @@ public class ServerInvalidDefinitionDstu2Test { } } + @Test + public void testWrongResourceType() { + RestfulServer srv = new RestfulServer(ourCtx); + srv.setFhirContext(ourCtx); + srv.setResourceProviders(new UpdateWithWrongResourceType()); + + try { + srv.init(); + fail(); + } catch (ServletException e) { + assertThat(e.getCause().toString(), StringContains.containsString("ConfigurationException")); + assertThat(e.getCause().toString(), StringContains.containsString("Method 'update' is annotated with @ResourceParam but has a type that is not an implemtation of org.hl7.fhir.instance.model.api.IBaseResource or String or byte[]")); + } + } + + @Test + public void testWrongValidateModeType() { + RestfulServer srv = new RestfulServer(ourCtx); + srv.setFhirContext(ourCtx); + srv.setResourceProviders(new ValidateWithWrongModeType()); + + try { + srv.init(); + fail(); + } catch (ServletException e) { + assertThat(e.getCause().toString(), StringContains.containsString("ConfigurationException")); + assertThat(e.getCause().toString(), StringContains.containsString("Parameter annotated with @Validate.Mode must be of type ca.uhn.fhir.rest.api.ValidationModeEnum")); + } + } + + @Test + public void testWrongValidateProfileType() { + RestfulServer srv = new RestfulServer(ourCtx); + srv.setFhirContext(ourCtx); + srv.setResourceProviders(new ValidateWithWrongProfileType()); + + try { + srv.init(); + fail(); + } catch (ServletException e) { + assertThat(e.getCause().toString(), StringContains.containsString("ConfigurationException")); + assertThat(e.getCause().toString(), StringContains.containsString("Parameter annotated with @Validate.Profile must be of type java.lang.String")); + } + } + public static class OperationReturningOldBundleProvider implements IResourceProvider { @Override @@ -90,4 +136,46 @@ public class ServerInvalidDefinitionDstu2Test { } + public static class UpdateWithWrongResourceType implements IResourceProvider { + + @Override + public Class getResourceType() { + return Patient.class; + } + + @Update + public MethodOutcome update(@ResourceParam Integer theParam2) { + return null; + } + + } + + public static class ValidateWithWrongModeType implements IResourceProvider { + + @Override + public Class getResourceType() { + return Patient.class; + } + + @Validate + public MethodOutcome update(@ResourceParam Patient thePatient, @Validate.Mode Integer theParam2) { + return null; + } + + } + + public static class ValidateWithWrongProfileType implements IResourceProvider { + + @Override + public Class getResourceType() { + return Patient.class; + } + + @Validate + public MethodOutcome update(@ResourceParam Patient thePatient, @Validate.Profile Integer theParam2) { + return null; + } + + } + } 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 bea6313966d..308e52d970b 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 @@ -1147,6 +1147,45 @@ public class GenericClientDstu3Test { //@formatter:on } + @Test + public void testTransactionWithInvalidBody() throws Exception { + IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); + + // Transaction + try { + client.transaction().withBundle("FOO"); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("Unable to determing encoding of request (body does not appear to be valid XML or JSON)", e.getMessage()); + } + + // Create + try { + client.create().resource("FOO").execute(); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("Unable to determing encoding of request (body does not appear to be valid XML or JSON)", e.getMessage()); + } + + // Update + try { + client.update().resource("FOO").execute(); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("Unable to determing encoding of request (body does not appear to be valid XML or JSON)", e.getMessage()); + } + + // Validate + try { + client.validate().resource("FOO").execute(); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("Unable to determing encoding of request (body does not appear to be valid XML or JSON)", e.getMessage()); + } + + + } + @Test public void testUpdateById() throws Exception { IParser p = ourCtx.newXmlParser(); diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/client/NonGenericClientDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/client/NonGenericClientDstu3Test.java new file mode 100644 index 00000000000..11b780fb1b9 --- /dev/null +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/client/NonGenericClientDstu3Test.java @@ -0,0 +1,122 @@ +package ca.uhn.fhir.rest.client; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.io.StringReader; +import java.nio.charset.Charset; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.io.input.ReaderInputStream; +import org.apache.http.HttpResponse; +import org.apache.http.ProtocolVersion; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.message.BasicHeader; +import org.apache.http.message.BasicStatusLine; +import org.hl7.fhir.dstu3.model.OperationOutcome; +import org.hl7.fhir.dstu3.model.Patient; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.parser.IParser; +import ca.uhn.fhir.rest.annotation.ResourceParam; +import ca.uhn.fhir.rest.annotation.Validate; +import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.rest.api.ValidationModeEnum; +import ca.uhn.fhir.rest.client.api.IRestfulClient; +import ca.uhn.fhir.rest.server.Constants; +import ca.uhn.fhir.util.TestUtil; + +public class NonGenericClientDstu3Test { + private static FhirContext ourCtx; + private HttpClient myHttpClient; + private HttpResponse myHttpResponse; + + @Before + public void before() { + myHttpClient = mock(HttpClient.class, new ReturnsDeepStubs()); + ourCtx.getRestfulClientFactory().setHttpClient(myHttpClient); + ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); + myHttpResponse = mock(HttpResponse.class, new ReturnsDeepStubs()); + + System.setProperty(BaseClient.HAPI_CLIENT_KEEPRESPONSES, "true"); + + } + + private String extractBodyAsString(ArgumentCaptor capt, int theIdx) throws IOException { + String body = IOUtils.toString(((HttpEntityEnclosingRequestBase) capt.getAllValues().get(theIdx)).getEntity().getContent(), "UTF-8"); + return body; + } + + @Test + public void testValidateResourceOnly() throws Exception { + IParser p = ourCtx.newXmlParser(); + + OperationOutcome conf = new OperationOutcome(); + conf.getText().setDivAsString("OK!"); + + final String respString = p.encodeResourceToString(conf); + 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_XML + "; charset=UTF-8")); + when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() { + @Override + public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable { + return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8")); + } + }); + + IClient client = ourCtx.newRestfulClient(IClient.class, "http://example.com/fhir"); + + Patient patient = new Patient(); + patient.addName().addFamily("FAM"); + + int idx = 0; + MethodOutcome outcome = client.validate(patient, null, null); + String resp = ourCtx.newXmlParser().encodeResourceToString(outcome.getOperationOutcome()); + assertEquals("
OK!
", resp); + assertEquals("http://example.com/fhir/$validate", capt.getAllValues().get(idx).getURI().toString()); + String request = extractBodyAsString(capt,idx); + assertEquals("", request); + + idx = 1; + outcome = client.validate(patient, ValidationModeEnum.CREATE, "http://foo"); + resp = ourCtx.newXmlParser().encodeResourceToString(outcome.getOperationOutcome()); + assertEquals("
OK!
", resp); + assertEquals("http://example.com/fhir/$validate", capt.getAllValues().get(idx).getURI().toString()); + request = extractBodyAsString(capt,idx); + assertEquals("", request); + } + + + private interface IClient extends IRestfulClient { + + @Validate + MethodOutcome validate(@ResourceParam IBaseResource theResource, @Validate.Mode ValidationModeEnum theMode, @Validate.Profile String theProfile); + + } + + @AfterClass + public static void afterClassClearContext() { + TestUtil.clearAllStaticFieldsForUnitTest(); + } + + @BeforeClass + public static void beforeClass() { + ourCtx = FhirContext.forDstu3(); + } + +} diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/method/MethodUtilTest.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/method/MethodUtilTest.java new file mode 100644 index 00000000000..43c8ef5f0cc --- /dev/null +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/method/MethodUtilTest.java @@ -0,0 +1,19 @@ +package ca.uhn.fhir.rest.method; + +import static org.junit.Assert.*; + +import org.hl7.fhir.dstu3.model.IdType; +import org.junit.Test; + +import ca.uhn.fhir.model.primitive.IdDt; + +public class MethodUtilTest { + + @Test + public void testConvertIdToType() { + IdDt id = new IdDt("Patient/123"); + IdType id2 = MethodUtil.convertIdToType(id, IdType.class); + assertEquals("Patient/123", id2.getValue()); + } + +}