diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementCompositeDefinition.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementCompositeDefinition.java index e8d406a2a40..d8e6a27d4f0 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementCompositeDefinition.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementCompositeDefinition.java @@ -354,11 +354,6 @@ public abstract class BaseRuntimeElementCompositeDefinition ext RuntimeChildChoiceDefinition def = new RuntimeChildChoiceDefinition(next, elementName, childAnnotation, descriptionAnnotation, choiceTypes); orderMap.put(order, def); - } else if (next.getType().equals(ExtensionDt.class)) { - - RuntimeChildExtensionDt def = new RuntimeChildExtensionDt(next, elementName, childAnnotation, descriptionAnnotation); - orderMap.put(order, def); - } else if (extensionAttr != null) { /* * Child is an extension @@ -427,8 +422,6 @@ public abstract class BaseRuntimeElementCompositeDefinition ext } else { def = new RuntimeChildPrimitiveDatatypeDefinition(next, elementName, descriptionAnnotation, childAnnotation, nextDatatype); } - } else if (IBaseXhtml.class.isAssignableFrom(nextElementType)) { - def = new RuntimeChildXhtmlDatatypeDefinition(next, elementName, descriptionAnnotation, childAnnotation, nextDatatype); } else { if (IBoundCodeableConcept.class.isAssignableFrom(nextElementType)) { IValueSetEnumBinder> binder = ModelScanner.getBoundCodeBinder(next); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java index e3a8d64176c..e51a7d198bc 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java @@ -44,6 +44,7 @@ import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; +import org.apache.commons.io.IOUtils; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseBackboneElement; @@ -574,6 +575,8 @@ class ModelScanner { } } catch (IOException e) { throw new ConfigurationException("Failed to load model property file from classpath: " + "/ca/uhn/fhir/model/dstu/model.properties"); + } finally { + IOUtils.closeQuietly(str); } return retVal; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildExtensionDt.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildExtensionDt.java deleted file mode 100644 index 74b93fdc68d..00000000000 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildExtensionDt.java +++ /dev/null @@ -1,40 +0,0 @@ -package ca.uhn.fhir.context; - -/* - * #%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.Field; - -import ca.uhn.fhir.model.api.ExtensionDt; -import ca.uhn.fhir.model.api.IElement; -import ca.uhn.fhir.model.api.annotation.Child; -import ca.uhn.fhir.model.api.annotation.Description; - -public class RuntimeChildExtensionDt extends RuntimeChildAny { - - public RuntimeChildExtensionDt(Field theField, String theElementName, Child theChildAnnotation, Description theDescriptionAnnotation) { - super(theField, theElementName, theChildAnnotation, theDescriptionAnnotation); - } - - public IElement newInstance() { - return new ExtensionDt(); - } - -} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildXhtmlDatatypeDefinition.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildXhtmlDatatypeDefinition.java deleted file mode 100644 index 791fa3787d7..00000000000 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildXhtmlDatatypeDefinition.java +++ /dev/null @@ -1,40 +0,0 @@ -package ca.uhn.fhir.context; - -/* - * #%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.Field; - -import org.hl7.fhir.instance.model.api.IBase; - -import ca.uhn.fhir.model.api.annotation.Child; -import ca.uhn.fhir.model.api.annotation.Description; - -/** - * HL7org XHTML type - */ -public class RuntimeChildXhtmlDatatypeDefinition extends RuntimeChildPrimitiveDatatypeDefinition { - - public RuntimeChildXhtmlDatatypeDefinition(Field theField, String theElementName, Description theDescriptionAnnotation, Child theChildAnnotation, Class theDatatype) { - super(theField, theElementName, theDescriptionAnnotation, theChildAnnotation, theDatatype); - } - - -} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/PathSpecification.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/PathSpecification.java deleted file mode 100644 index b22605a2170..00000000000 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/PathSpecification.java +++ /dev/null @@ -1,45 +0,0 @@ -package ca.uhn.fhir.model.api; - -import ca.uhn.fhir.util.CoverageIgnore; - -/* - * #%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% - */ - - -/** - * Represents a FHIR resource path specification, e.g. - * Patient.gender.coding - *

- * Note on equality: This class uses the {@link PathSpecification#setValue(String) value} - * as the single item used to provide {@link #hashCode()} and {@link #equals(Object)}. - *

- * - * @deprecated {@link Include} should be used instead - */ -@CoverageIgnore -@Deprecated -public class PathSpecification extends Include { - - public PathSpecification(String theInclude) { - super(theInclude); - } - - -} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/dstu/valueset/SecurityEventObjectSensitivityEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/dstu/valueset/SecurityEventObjectSensitivityEnum.java deleted file mode 100644 index bb5fc970c81..00000000000 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/dstu/valueset/SecurityEventObjectSensitivityEnum.java +++ /dev/null @@ -1,125 +0,0 @@ - -package ca.uhn.fhir.model.dstu.valueset; - -/* - * #%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.util.HashMap; -import java.util.Map; - -import ca.uhn.fhir.model.api.IValueSetEnumBinder; -import ca.uhn.fhir.util.CoverageIgnore; - -@CoverageIgnore -public enum SecurityEventObjectSensitivityEnum { - - ; - - /** - * Identifier for this Value Set: - * http://hl7.org/fhir/vs/security-event-sensitivity - */ - public static final String VALUESET_IDENTIFIER = "http://hl7.org/fhir/vs/security-event-sensitivity"; - - /** - * Name for this Value Set: - * Security Event Object Sensitivity - */ - public static final String VALUESET_NAME = "Security Event Object Sensitivity"; - - private static Map CODE_TO_ENUM = new HashMap(); - private static Map> SYSTEM_TO_CODE_TO_ENUM = new HashMap>(); - - private final String myCode; - private final String mySystem; - - static { - for (SecurityEventObjectSensitivityEnum next : SecurityEventObjectSensitivityEnum.values()) { - CODE_TO_ENUM.put(next.getCode(), next); - - if (!SYSTEM_TO_CODE_TO_ENUM.containsKey(next.getSystem())) { - SYSTEM_TO_CODE_TO_ENUM.put(next.getSystem(), new HashMap()); - } - SYSTEM_TO_CODE_TO_ENUM.get(next.getSystem()).put(next.getCode(), next); - } - } - - /** - * Returns the code associated with this enumerated value - */ - public String getCode() { - return myCode; - } - - /** - * Returns the code system associated with this enumerated value - */ - public String getSystem() { - return mySystem; - } - - /** - * Returns the enumerated value associated with this code - */ - public SecurityEventObjectSensitivityEnum forCode(String theCode) { - SecurityEventObjectSensitivityEnum retVal = CODE_TO_ENUM.get(theCode); - return retVal; - } - - /** - * Converts codes to their respective enumerated values - */ - public static final IValueSetEnumBinder VALUESET_BINDER = new IValueSetEnumBinder() { - @Override - public String toCodeString(SecurityEventObjectSensitivityEnum theEnum) { - return theEnum.getCode(); - } - - @Override - public String toSystemString(SecurityEventObjectSensitivityEnum theEnum) { - return theEnum.getSystem(); - } - - @Override - public SecurityEventObjectSensitivityEnum fromCodeString(String theCodeString) { - return CODE_TO_ENUM.get(theCodeString); - } - - @Override - public SecurityEventObjectSensitivityEnum fromCodeString(String theCodeString, String theSystemString) { - Map map = SYSTEM_TO_CODE_TO_ENUM.get(theSystemString); - if (map == null) { - return null; - } - return map.get(theCodeString); - } - - }; - - /** - * Constructor - */ - SecurityEventObjectSensitivityEnum(String theCode, String theSystem) { - myCode = theCode; - mySystem = theSystem; - } - - -} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/dstu/valueset/SecurityEventObjectTypeEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/dstu/valueset/SecurityEventObjectTypeEnum.java deleted file mode 100644 index 67459ba11d5..00000000000 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/dstu/valueset/SecurityEventObjectTypeEnum.java +++ /dev/null @@ -1,153 +0,0 @@ - -package ca.uhn.fhir.model.dstu.valueset; - -/* - * #%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.util.HashMap; -import java.util.Map; - -import ca.uhn.fhir.model.api.IValueSetEnumBinder; -import ca.uhn.fhir.util.CoverageIgnore; - -@CoverageIgnore -public enum SecurityEventObjectTypeEnum { - - /** - * Code Value: 1 - * - * Person. - */ - PERSON("1", "http://hl7.org/fhir/object-type"), - - /** - * Code Value: 2 - * - * System Object. - */ - SYSTEM_OBJECT("2", "http://hl7.org/fhir/object-type"), - - /** - * Code Value: 3 - * - * Organization. - */ - ORGANIZATION("3", "http://hl7.org/fhir/object-type"), - - /** - * Code Value: 4 - * - * Other. - */ - OTHER("4", "http://hl7.org/fhir/object-type"), - - ; - - /** - * Identifier for this Value Set: - * http://hl7.org/fhir/vs/object-type - */ - public static final String VALUESET_IDENTIFIER = "http://hl7.org/fhir/vs/object-type"; - - /** - * Name for this Value Set: - * SecurityEventObjectType - */ - public static final String VALUESET_NAME = "SecurityEventObjectType"; - - private static Map CODE_TO_ENUM = new HashMap(); - private static Map> SYSTEM_TO_CODE_TO_ENUM = new HashMap>(); - - private final String myCode; - private final String mySystem; - - static { - for (SecurityEventObjectTypeEnum next : SecurityEventObjectTypeEnum.values()) { - CODE_TO_ENUM.put(next.getCode(), next); - - if (!SYSTEM_TO_CODE_TO_ENUM.containsKey(next.getSystem())) { - SYSTEM_TO_CODE_TO_ENUM.put(next.getSystem(), new HashMap()); - } - SYSTEM_TO_CODE_TO_ENUM.get(next.getSystem()).put(next.getCode(), next); - } - } - - /** - * Returns the code associated with this enumerated value - */ - public String getCode() { - return myCode; - } - - /** - * Returns the code system associated with this enumerated value - */ - public String getSystem() { - return mySystem; - } - - /** - * Returns the enumerated value associated with this code - */ - public SecurityEventObjectTypeEnum forCode(String theCode) { - SecurityEventObjectTypeEnum retVal = CODE_TO_ENUM.get(theCode); - return retVal; - } - - /** - * Converts codes to their respective enumerated values - */ - public static final IValueSetEnumBinder VALUESET_BINDER = new IValueSetEnumBinder() { - @Override - public String toCodeString(SecurityEventObjectTypeEnum theEnum) { - return theEnum.getCode(); - } - - @Override - public String toSystemString(SecurityEventObjectTypeEnum theEnum) { - return theEnum.getSystem(); - } - - @Override - public SecurityEventObjectTypeEnum fromCodeString(String theCodeString) { - return CODE_TO_ENUM.get(theCodeString); - } - - @Override - public SecurityEventObjectTypeEnum fromCodeString(String theCodeString, String theSystemString) { - Map map = SYSTEM_TO_CODE_TO_ENUM.get(theSystemString); - if (map == null) { - return null; - } - return map.get(theCodeString); - } - - }; - - /** - * Constructor - */ - SecurityEventObjectTypeEnum(String theCode, String theSystem) { - myCode = theCode; - mySystem = theSystem; - } - - -} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/TimeDt.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/TimeDt.java index b761d9d3f37..b5c11540c3c 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/TimeDt.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/TimeDt.java @@ -52,6 +52,7 @@ public class TimeDt extends StringDt implements IQueryParameterType { */ @SimpleSetter public TimeDt(@SimpleSetter.Parameter(name = "theString") String theValue) { + this(); setValue(theValue); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative/ThymeleafResolver.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative/ThymeleafResolver.java deleted file mode 100644 index 365393dbd67..00000000000 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative/ThymeleafResolver.java +++ /dev/null @@ -1,55 +0,0 @@ -package ca.uhn.fhir.narrative; - -/* - * #%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 org.thymeleaf.TemplateProcessingParameters; -import org.thymeleaf.templateresolver.ITemplateResolver; -import org.thymeleaf.templateresolver.TemplateResolution; - -public class ThymeleafResolver implements ITemplateResolver { - - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ThymeleafResolver.class); - - @Override - public String getName() { - return ""; - } - - @Override - public Integer getOrder() { - return 0; - } - - @Override - public void initialize() { - // nothing - } - - @Override - public TemplateResolution resolveTemplate(TemplateProcessingParameters theTemplateProcessingParameters) { - - String templateName = theTemplateProcessingParameters.getTemplateName(); - ourLog.info("Resolving template: {}", templateName); - - return null; - } - -} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java index 909d8c4e6ce..d374e73c21a 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java @@ -314,21 +314,6 @@ public abstract class BaseParser implements IParser { } } - protected String determineResourceBaseUrl(String bundleBaseUrl, BundleEntry theEntry) { - IResource resource = theEntry.getResource(); - if (resource == null) { - return null; - } - - String resourceBaseUrl = null; - if (resource.getId() != null && resource.getId().hasBaseUrl()) { - if (!resource.getId().getBaseUrl().equals(bundleBaseUrl)) { - resourceBaseUrl = resource.getId().getBaseUrl(); - } - } - return resourceBaseUrl; - } - protected abstract void doEncodeBundleToWriter(Bundle theBundle, Writer theWriter) throws IOException, DataFormatException; protected abstract void doEncodeResourceToWriter(IBaseResource theResource, Writer theWriter) throws IOException, DataFormatException; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseClient.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseClient.java index 2a7f1a44258..1da6d932a4a 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseClient.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseClient.java @@ -70,6 +70,13 @@ import ca.uhn.fhir.util.OperationOutcomeUtil; public abstract class BaseClient implements IRestfulClient { + /** + * This property is used by unit tests - do not rely on it in production code + * as it may change at any time. If you want to capture responses in a reliable + * way in your own code, just use client interceptors + */ + static final String HAPI_CLIENT_KEEPRESPONSES = "hapi.client.keepresponses"; + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseClient.class); private final IHttpClient myClient; @@ -89,6 +96,15 @@ public abstract class BaseClient implements IRestfulClient { myClient = theClient; myUrlBase = theUrlBase; myFactory = theFactory; + + /* + * This property is used by unit tests - do not rely on it in production code + * as it may change at any time. If you want to capture responses in a reliable + * way in your own code, just use client interceptors + */ + if ("true".equals(System.getProperty(HAPI_CLIENT_KEEPRESPONSES))) { + setKeepResponses(true); + } } protected Map> createExtraParams() { @@ -193,7 +209,7 @@ public abstract class BaseClient implements IRestfulClient { // TODO: handle non 2xx status codes by throwing the correct exception, // and ensure it's passed upwards - IHttpRequest httpRequest; + IHttpRequest httpRequest = null; IHttpResponse response = null; try { Map> params = createExtraParams(); @@ -339,11 +355,13 @@ public abstract class BaseClient implements IRestfulClient { } } catch (DataFormatException e) { - throw new FhirClientConnectionException(e); + String msg = getFhirContext().getLocalizer().getMessage(BaseClient.class, "failedToParseResponse", httpRequest.getHttpVerbName(), httpRequest.getUri(), e.toString()); + throw new FhirClientConnectionException(msg, e); } catch (IllegalStateException e) { throw new FhirClientConnectionException(e); } catch (IOException e) { - throw new FhirClientConnectionException(e); + String msg = getFhirContext().getLocalizer().getMessage(BaseClient.class, "ioExceptionDuringOperation", httpRequest.getHttpVerbName(), httpRequest.getUri(), e.toString()); + throw new FhirClientConnectionException(msg, e); } catch (RuntimeException e) { throw e; } catch (Exception e) { 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 d805de9da67..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 @@ -76,6 +76,7 @@ import ca.uhn.fhir.rest.api.SummaryEnum; import ca.uhn.fhir.rest.client.api.IHttpClient; import ca.uhn.fhir.rest.client.api.IHttpRequest; import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException; +import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; import ca.uhn.fhir.rest.gclient.IClientExecutable; import ca.uhn.fhir.rest.gclient.ICreate; import ca.uhn.fhir.rest.gclient.ICreateTyped; @@ -136,7 +137,7 @@ import ca.uhn.fhir.rest.method.SearchStyleEnum; import ca.uhn.fhir.rest.method.SortParameter; import ca.uhn.fhir.rest.method.TransactionMethodBinding; import ca.uhn.fhir.rest.method.ValidateMethodBindingDstu1; -import ca.uhn.fhir.rest.method.ValidateMethodBindingDstu2; +import ca.uhn.fhir.rest.method.ValidateMethodBindingDstu2Plus; import ca.uhn.fhir.rest.param.DateParam; import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.TokenParam; @@ -174,8 +175,8 @@ public class GenericClient extends BaseClient implements IGenericClient { @Override public IBaseConformance conformance() { - if (myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU2_HL7ORG)) { - throw new IllegalArgumentException("Must call fetchConformance() instead of conformance() for RI/DSTU3+ structures"); + if (myContext.getVersion().getVersion().isRi()) { + throw new IllegalArgumentException("Must call fetchConformance() instead of conformance() for RI/STU3+ structures"); } HttpGetClientInvocation invocation = MethodUtil.createConformanceInvocation(getFhirContext()); @@ -376,7 +377,7 @@ public class GenericClient extends BaseClient implements IGenericClient { } } - throw new RuntimeException(myContext.getLocalizer().getMessage(I18N_CANNOT_DETEMINE_RESOURCE_TYPE, theUrl.getValueAsString())); + throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_CANNOT_DETEMINE_RESOURCE_TYPE, theUrl.getValueAsString())); } @@ -385,6 +386,12 @@ public class GenericClient extends BaseClient implements IGenericClient { // return doReadOrVRead(theType, theId, false, null, null); // } + /** + * @deprecated Use {@link LoggingInterceptor} as a client interceptor registered to your + * client instead, as this provides much more fine-grained control over what is logged. This + * method will be removed at some point (deprecated in HAPI 1.6 - 2016-06-16) + */ + @Deprecated public boolean isLogRequestAndResponse() { return myLogRequestAndResponse; } @@ -549,7 +556,7 @@ public class GenericClient extends BaseClient implements IGenericClient { if (myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU1)) { invocation = ValidateMethodBindingDstu1.createValidateInvocation(theResource, null, myContext); } else { - invocation = ValidateMethodBindingDstu2.createValidateInvocation(myContext, theResource); + invocation = ValidateMethodBindingDstu2Plus.createValidateInvocation(myContext, theResource); } if (isKeepResponses()) { @@ -714,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); } @@ -2213,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")); } } @@ -2402,7 +2409,7 @@ public class GenericClient extends BaseClient implements IGenericClient { @Override public MethodOutcome execute() { - BaseHttpClientInvocation invocation = ValidateMethodBindingDstu2.createValidateInvocation(myContext, myResource); + BaseHttpClientInvocation invocation = ValidateMethodBindingDstu2Plus.createValidateInvocation(myContext, myResource); ResourceResponseHandler handler = new ResourceResponseHandler(null, null); IBaseOperationOutcome outcome = invoke(null, handler, invocation); MethodOutcome retVal = new MethodOutcome(); @@ -2424,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/client/IGenericClient.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/IGenericClient.java index 4bd785e753b..ac49faf4b8d 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/IGenericClient.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/IGenericClient.java @@ -37,6 +37,7 @@ import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.client.api.IRestfulClient; import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException; import ca.uhn.fhir.rest.client.exceptions.FhirClientInappropriateForServerException; +import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; import ca.uhn.fhir.rest.gclient.ICreate; import ca.uhn.fhir.rest.gclient.IDelete; import ca.uhn.fhir.rest.gclient.IFetchConformanceUntyped; @@ -288,7 +289,11 @@ public interface IGenericClient extends IRestfulClient { * * @param theLogRequestAndResponse * Should requests and responses be logged + * @deprecated Use {@link LoggingInterceptor} as a client interceptor registered to your + * client instead, as this provides much more fine-grained control over what is logged. This + * method will be removed at some point (deprecated in HAPI 1.6 - 2016-06-16) */ + @Deprecated void setLogRequestAndResponse(boolean theLogRequestAndResponse); /** diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/RestfulClientFactory.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/RestfulClientFactory.java index 080e1a63987..2c8cf5a5cd2 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/RestfulClientFactory.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/RestfulClientFactory.java @@ -194,7 +194,7 @@ public abstract class RestfulClientFactory implements IRestfulClientFactory { public void validateServerBaseIfConfiguredToDoSo(String theServerBase, IHttpClient theHttpClient, BaseClient theClient) { String serverBase = normalizeBaseUrlForMap(theServerBase); - switch (myServerValidationMode) { + switch (getServerValidationMode()) { case NEVER: break; case ONCE: @@ -267,22 +267,6 @@ public abstract class RestfulClientFactory implements IRestfulClientFactory { myPoolMaxPerRoute = thePoolMaxPerRoute; resetHttpClient(); } - - /** - * Instantiates a new client invocation handler - * @param theClient - * the client which will invoke the call - * @param theUrlBase - * the url base - * @param theMethodToReturnValue - * @param theBindings - * @param theMethodToLambda - * @return a newly created client invocation handler - */ - ClientInvocationHandler newInvocationHandler(IHttpClient theClient, String theUrlBase, Map theMethodToReturnValue, Map> theBindings, Map theMethodToLambda) { - return new ClientInvocationHandler(theClient, getFhirContext(), theUrlBase.toString(), theMethodToReturnValue, - theBindings, theMethodToLambda, this); - } @SuppressWarnings("unchecked") @Override @@ -300,7 +284,8 @@ public abstract class RestfulClientFactory implements IRestfulClientFactory { Class implementingClass = myContext.getResourceDefinition("Conformance").getImplementingClass(); conformance = (IBaseResource) client.fetchConformance().ofType(implementingClass).execute(); } catch (FhirClientConnectionException e) { - throw new FhirClientConnectionException(myContext.getLocalizer().getMessage(RestfulClientFactory.class, "failedToRetrieveConformance", theServerBase + Constants.URL_TOKEN_METADATA), e); + String msg = myContext.getLocalizer().getMessage(RestfulClientFactory.class, "failedToRetrieveConformance", theServerBase + Constants.URL_TOKEN_METADATA); + throw new FhirClientConnectionException(msg, e); } FhirTerser t = myContext.newTerser(); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpRequest.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpRequest.java index 8a35e334b48..e5cf6bfb4ec 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpRequest.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpRequest.java @@ -44,8 +44,8 @@ import ca.uhn.fhir.rest.client.api.IHttpResponse; */ public class ApacheHttpRequest implements IHttpRequest { - private HttpRequestBase myRequest; private HttpClient myClient; + private HttpRequestBase myRequest; public ApacheHttpRequest(HttpClient theClient, HttpRequestBase theApacheRequest) { this.myClient = theClient; @@ -57,14 +57,6 @@ public class ApacheHttpRequest implements IHttpRequest { myRequest.addHeader(theName, theValue); } - /** - * Get the ApacheRequest - * @return the ApacheRequest - */ - public HttpRequestBase getApacheRequest() { - return myRequest; - } - @Override public IHttpResponse execute() throws IOException { return new ApacheHttpResponse(myClient.execute(myRequest)); @@ -82,9 +74,17 @@ public class ApacheHttpRequest implements IHttpRequest { return result; } + /** + * Get the ApacheRequest + * @return the ApacheRequest + */ + public HttpRequestBase getApacheRequest() { + return myRequest; + } + @Override - public String toString() { - return myRequest.toString(); + public String getHttpVerbName() { + return myRequest.getMethod(); } @Override @@ -98,4 +98,14 @@ public class ApacheHttpRequest implements IHttpRequest { return null; } + @Override + public String getUri() { + return myRequest.getURI().toString(); + } + + @Override + public String toString() { + return myRequest.toString(); + } + } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheRestfulClientFactory.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheRestfulClientFactory.java index ff1fda0e066..61d2c43adc7 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheRestfulClientFactory.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheRestfulClientFactory.java @@ -57,6 +57,7 @@ public class ApacheRestfulClientFactory extends RestfulClientFactory { * Constructor */ public ApacheRestfulClientFactory() { + super(); } /** diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IHttpRequest.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IHttpRequest.java index ceb54ad576f..9e800060dd6 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IHttpRequest.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IHttpRequest.java @@ -56,4 +56,14 @@ public interface IHttpRequest { */ public String getRequestBodyFromStream() throws IOException; + /** + * Return the request URI, or null + */ + public String getUri(); + + /** + * Return the HTTP verb (e.g. "GET") + */ + public String getHttpVerbName(); + } 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/BaseMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseMethodBinding.java index 5b85b4c3549..8b96fa8230e 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseMethodBinding.java @@ -516,7 +516,7 @@ public abstract class BaseMethodBinding implements IClientResponseHandler if (theContext.getVersion().getVersion() == FhirVersionEnum.DSTU1) { return new ValidateMethodBindingDstu1(theMethod, theContext, theProvider); } else { - return new ValidateMethodBindingDstu2(returnType, returnTypeFromRp, theMethod, theContext, theProvider, validate); + return new ValidateMethodBindingDstu2Plus(returnType, returnTypeFromRp, theMethod, theContext, theProvider, validate); } } else if (getTags != null) { return new GetTagsMethodBinding(theMethod, theContext, theProvider, getTags); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseOutcomeReturningMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseOutcomeReturningMethodBinding.java index 25cb667f936..59a22ff5e09 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseOutcomeReturningMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseOutcomeReturningMethodBinding.java @@ -253,7 +253,7 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding summaryMode = Collections.emptySet(); - return restfulResponse.streamResponseAsResource(outcome, prettyPrint, summaryMode, operationStatus, theRequest.isRespondGzip(), true); + return restfulResponse.streamResponseAsResource(outcome, prettyPrint, summaryMode, operationStatus, null, theRequest.isRespondGzip(), true); // return theRequest.getResponse().returnResponse(ParseAction.create(outcome), operationStatus, allowPrefer, response, getResourceName()); } 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 1bce71df52a..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; @@ -260,8 +260,8 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi boolean prettyPrint = RestfulServerUtils.prettyPrintResponse(theServer, theRequest); - return theRequest.getResponse().streamResponseAsResource(responseObject.getResource(), prettyPrint, summaryMode, Constants.STATUS_HTTP_200_OK, theRequest.isRespondGzip(), - isAddContentLocationHeader()); + return theRequest.getResponse().streamResponseAsResource(responseObject.getResource(), prettyPrint, summaryMode, Constants.STATUS_HTTP_200_OK, null, + theRequest.isRespondGzip(), isAddContentLocationHeader()); } else { // Is this request coming from a browser diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/IncludeParameter.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/IncludeParameter.java index 3c33e152755..10cfae2c61a 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/IncludeParameter.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/IncludeParameter.java @@ -31,7 +31,6 @@ import java.util.TreeSet; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.Include; -import ca.uhn.fhir.model.api.PathSpecification; import ca.uhn.fhir.rest.annotation.IncludeParam; import ca.uhn.fhir.rest.param.BaseQueryParameter; import ca.uhn.fhir.rest.server.Constants; @@ -60,7 +59,7 @@ class IncludeParameter extends BaseQueryParameter { } mySpecType = theSpecType; - if (mySpecType != Include.class && mySpecType != PathSpecification.class && mySpecType != String.class) { + if (mySpecType != Include.class && mySpecType != String.class) { throw new ConfigurationException("Invalid @" + IncludeParam.class.getSimpleName() + " parameter type: " + mySpecType); } @@ -72,7 +71,7 @@ class IncludeParameter extends BaseQueryParameter { ArrayList retVal = new ArrayList(); if (myInstantiableCollectionType == null) { - if (mySpecType == Include.class || mySpecType == PathSpecification.class) { + if (mySpecType == Include.class) { convertAndAddIncludeToList(retVal, (Include) theObject); } else { retVal.add(QualifiedParamList.singleton(((String) theObject))); 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 a2350ff976b..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; @@ -40,14 +39,12 @@ import ca.uhn.fhir.model.api.IQueryParameterOr; import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.Include; -import ca.uhn.fhir.model.api.PathSpecification; 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.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; @@ -90,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 @@ -114,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); @@ -133,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) { @@ -213,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)); } } @@ -288,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; } @@ -322,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) { @@ -366,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(); @@ -442,7 +432,7 @@ public class MethodUtil { if (parameterType == String.class) { instantiableCollectionType = null; specType = String.class; - } else if ((parameterType != Include.class && parameterType != PathSpecification.class) || innerCollectionType == null || outerCollectionType != null) { + } else if ((parameterType != Include.class) || innerCollectionType == null || outerCollectionType != null) { throw new ConfigurationException("Method '" + theMethod.getName() + "' is annotated with @" + IncludeParam.class.getSimpleName() + " but has a type other than Collection<" + Include.class.getSimpleName() + ">"); } else { instantiableCollectionType = (Class>) CollectionBinder.getInstantiableCollectionType(innerCollectionType, "Method '" + theMethod.getName() + "'"); @@ -515,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) { @@ -530,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/java/ca/uhn/fhir/rest/method/SearchParameter.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SearchParameter.java index 74ef260db97..0dc52a23b97 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SearchParameter.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SearchParameter.java @@ -59,7 +59,6 @@ import ca.uhn.fhir.rest.param.HasParam; import ca.uhn.fhir.rest.param.NumberAndListParam; import ca.uhn.fhir.rest.param.NumberOrListParam; import ca.uhn.fhir.rest.param.NumberParam; -import ca.uhn.fhir.rest.param.QualifiedDateParam; import ca.uhn.fhir.rest.param.QuantityAndListParam; import ca.uhn.fhir.rest.param.QuantityOrListParam; import ca.uhn.fhir.rest.param.QuantityParam; @@ -81,7 +80,6 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.util.CollectionUtil; import ca.uhn.fhir.util.ReflectionUtil; -@SuppressWarnings("deprecation") public class SearchParameter extends BaseQueryParameter { private static final String EMPTY_STRING = ""; @@ -341,8 +339,6 @@ public class SearchParameter extends BaseQueryParameter { // ok } else if (StringDt.class.isAssignableFrom(type)) { myParamType = RestSearchParameterTypeEnum.STRING; - } else if (QualifiedDateParam.class.isAssignableFrom(type)) { - myParamType = RestSearchParameterTypeEnum.DATE; } else if (BaseIdentifierDt.class.isAssignableFrom(type)) { myParamType = RestSearchParameterTypeEnum.TOKEN; } else if (BaseQuantityDt.class.isAssignableFrom(type)) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/Util.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/Util.java deleted file mode 100644 index 053e74dc4b1..00000000000 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/Util.java +++ /dev/null @@ -1,54 +0,0 @@ -package ca.uhn.fhir.rest.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% - */ - -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import java.util.HashMap; -import java.util.Map; - -/** - * Created by dsotnikov on 2/25/2014. - */ -class Util { -// public static Integer findCountParameterIndex(Method theMethod) { -// return findParamIndex(theMethod, Count.class); -// } - - - public static Map getQueryParams(String query) { - try { - - Map params = new HashMap(); - for (String param : query.split("&")) { - String[] pair = param.split("="); - String key = URLDecoder.decode(pair[0], "UTF-8"); - String value = URLDecoder.decode(pair[1], "UTF-8"); - - params.put(key, value); - } - return params; - } catch (UnsupportedEncodingException ex) { - throw new AssertionError(ex); - } - } - -} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ValidateMethodBindingDstu2.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ValidateMethodBindingDstu2Plus.java similarity index 92% rename from hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ValidateMethodBindingDstu2.java rename to hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ValidateMethodBindingDstu2Plus.java index ae74a088d6f..fdec32451b8 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ValidateMethodBindingDstu2.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ValidateMethodBindingDstu2Plus.java @@ -37,9 +37,9 @@ import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.util.ParametersUtil; -public class ValidateMethodBindingDstu2 extends OperationMethodBinding { +public class ValidateMethodBindingDstu2Plus extends OperationMethodBinding { - public ValidateMethodBindingDstu2(Class theReturnResourceType, Class theReturnTypeFromRp, Method theMethod, FhirContext theContext, Object theProvider, + public ValidateMethodBindingDstu2Plus(Class theReturnResourceType, Class theReturnTypeFromRp, Method theMethod, FhirContext theContext, Object theProvider, Validate theAnnotation) { super(theReturnResourceType, theReturnTypeFromRp, theMethod, theContext, theProvider, true, Constants.EXTOP_VALIDATE, theAnnotation.type(), new OperationParam[0], BundleTypeEnum.COLLECTION); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ValidateMethodBindingDstu3.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ValidateMethodBindingDstu3.java deleted file mode 100644 index fc76589cacb..00000000000 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ValidateMethodBindingDstu3.java +++ /dev/null @@ -1,84 +0,0 @@ -package ca.uhn.fhir.rest.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% - */ - -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; - -import org.hl7.fhir.instance.model.api.IBaseParameters; -import org.hl7.fhir.instance.model.api.IBaseResource; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.model.valueset.BundleTypeEnum; -import ca.uhn.fhir.rest.annotation.OperationParam; -import ca.uhn.fhir.rest.annotation.Validate; -import ca.uhn.fhir.rest.client.BaseHttpClientInvocation; -import ca.uhn.fhir.rest.param.ResourceParameter; -import ca.uhn.fhir.rest.server.Constants; -import ca.uhn.fhir.rest.server.EncodingEnum; -import ca.uhn.fhir.util.ParametersUtil; - -public class ValidateMethodBindingDstu3 extends OperationMethodBinding { - - public ValidateMethodBindingDstu3(Class theReturnResourceType, Class theReturnTypeFromRp, Method theMethod, FhirContext theContext, Object theProvider, - Validate theAnnotation) { - super(theReturnResourceType, theReturnTypeFromRp, theMethod, theContext, theProvider, true, Constants.EXTOP_VALIDATE, theAnnotation.type(), new OperationParam[0], BundleTypeEnum.COLLECTION); - - List newParams = new ArrayList(); - int idx = 0; - for (IParameter next : getParameters()) { - if (next instanceof ResourceParameter) { - if (IBaseResource.class.isAssignableFrom(((ResourceParameter) next).getResourceType())) { - Class parameterType = theMethod.getParameterTypes()[idx]; - if (String.class.equals(parameterType) || EncodingEnum.class.equals(parameterType)) { - newParams.add(next); - } else { - OperationParameter parameter = new OperationParameter(theContext, Constants.EXTOP_VALIDATE, Constants.EXTOP_VALIDATE_RESOURCE, 0, 1); - parameter.initializeTypes(theMethod, null, null, parameterType); - newParams.add(parameter); - } - } else { - newParams.add(next); - } - } else { - newParams.add(next); - } - idx++; - } - setParameters(newParams); - - } - - - public static BaseHttpClientInvocation createValidateInvocation(FhirContext theContext, IBaseResource theResource) { - IBaseParameters parameters = (IBaseParameters) theContext.getResourceDefinition("Parameters").newInstance(); - ParametersUtil.addParameterToParameters(theContext, parameters, theResource, "resource"); - - String resourceName = theContext.getResourceDefinition(theResource).getName(); - String resourceId = theResource.getIdElement().getIdPart(); - - BaseHttpClientInvocation retVal = createOperationInvocation(theContext, resourceName, resourceId, Constants.EXTOP_VALIDATE, parameters, false); - return retVal; - } - - -} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/QualifiedDateParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/QualifiedDateParam.java deleted file mode 100644 index dc55d1b31a1..00000000000 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/QualifiedDateParam.java +++ /dev/null @@ -1,65 +0,0 @@ -package ca.uhn.fhir.rest.param; - -/* - * #%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.util.Date; - -import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum; -import ca.uhn.fhir.util.CoverageIgnore; - -/** - * @deprecated Use {@link DateParam} instead (this class is identical, but was renamed to be less confusing) - */ -@Deprecated -@CoverageIgnore -public class QualifiedDateParam extends DateParam { - /** - * Constructor - */ - public QualifiedDateParam() { - } - - /** - * Constructor - */ - public QualifiedDateParam(QuantityCompararatorEnum theComparator, Date theDate) { - setComparator(theComparator); - setValue(theDate); - } - - /** - * Constructor - */ - public QualifiedDateParam(QuantityCompararatorEnum theComparator, String theDate) { - setComparator(theComparator); - setValueAsString(theDate); - } - - /** - * Constructor which takes a complete [qualifier]{date} string. - * - * @param theString The string - */ - public QualifiedDateParam(String theString) { - setValueAsQueryToken(null, theString); - } - -} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/IRestfulResponse.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/IRestfulResponse.java index fe0509e9f3e..8683baca9b0 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/IRestfulResponse.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/IRestfulResponse.java @@ -38,7 +38,7 @@ import ca.uhn.fhir.rest.method.ParseAction; public interface IRestfulResponse { - Object streamResponseAsResource(IBaseResource theActualResourceToReturn, boolean prettyPrint, Set summaryMode, int operationStatus, boolean respondGzip, boolean addContentLocationHeader) throws IOException; + Object streamResponseAsResource(IBaseResource theActualResourceToReturn, boolean thePrettyPrint, Set theSummaryMode, int theStatusCode, String theStatusMessage, boolean theRespondGzip, boolean theAddContentLocation) throws IOException; Object streamResponseAsBundle(Bundle bundle, Set summaryMode, boolean respondGzip, boolean requestIsBrowser) throws IOException; @@ -48,7 +48,7 @@ public interface IRestfulResponse { */ Object returnResponse(ParseAction outcome, int operationStatus, boolean allowPrefer, MethodOutcome response, String resourceName) throws IOException; - Writer getResponseWriter(int statusCode, String contentType, String charset, boolean respondGzip) throws UnsupportedEncodingException, IOException; + Writer getResponseWriter(int theStatusCode, String theStatusMessage, String theContentType, String theCharset, boolean theRespondGzip) throws UnsupportedEncodingException, IOException; Object sendWriterResponse(int status, String contentType, String charset, Writer writer) throws IOException; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulResponse.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulResponse.java index 5fee3043aba..bb29626acec 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulResponse.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulResponse.java @@ -90,10 +90,10 @@ public abstract class RestfulResponse implements IRest } @Override - public final Object streamResponseAsResource(IBaseResource resource, boolean prettyPrint, Set summaryMode, - int statusCode, boolean respondGzip, boolean addContentLocationHeader) + public final Object streamResponseAsResource(IBaseResource theResource, boolean thePrettyPrint, Set theSummaryMode, + int theStatusCode, String theStatusMessage, boolean theRespondGzip, boolean theAddContentLocation) throws IOException { - return RestfulServerUtils.streamResponseAsResource(theRequestDetails.getServer(), resource, summaryMode, statusCode, addContentLocationHeader, respondGzip, getRequestDetails(), myOperationResourceId, myOperationResourceLastUpdated); + return RestfulServerUtils.streamResponseAsResource(theRequestDetails.getServer(), theResource, theSummaryMode, theStatusCode, theStatusMessage, theAddContentLocation, theRespondGzip, getRequestDetails(), myOperationResourceId, myOperationResourceLastUpdated); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java index fa911faa3bc..4e1c1717f35 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java @@ -537,7 +537,7 @@ public class RestfulServerUtils { String contentType = responseEncoding.getBundleContentType(); String charset = Constants.CHARSET_NAME_UTF8; - Writer writer = theRequestDetails.getResponse().getResponseWriter(status, contentType, charset, respondGzip); + Writer writer = theRequestDetails.getResponse().getResponseWriter(status, null, contentType, charset, respondGzip); try { IParser parser = RestfulServerUtils.getNewParser(theServer.getFhirContext(), theRequestDetails); @@ -552,11 +552,11 @@ public class RestfulServerUtils { } public static Object streamResponseAsResource(IRestfulServerDefaults theServer, IBaseResource theResource, Set theSummaryMode, int stausCode, boolean theAddContentLocationHeader, boolean respondGzip, RequestDetails theRequestDetails) throws IOException { - return streamResponseAsResource(theServer, theResource, theSummaryMode, stausCode, theAddContentLocationHeader, respondGzip, theRequestDetails, null, null); + return streamResponseAsResource(theServer, theResource, theSummaryMode, stausCode, null, theAddContentLocationHeader, respondGzip, theRequestDetails, null, null); } - public static Object streamResponseAsResource(IRestfulServerDefaults theServer, IBaseResource theResource, Set theSummaryMode, int stausCode, boolean theAddContentLocationHeader, boolean respondGzip, RequestDetails theRequestDetails, IIdType theOperationResourceId, IPrimitiveType theOperationResourceLastUpdated) throws IOException { + public static Object streamResponseAsResource(IRestfulServerDefaults theServer, IBaseResource theResource, Set theSummaryMode, int theStausCode, String theStatusMessage, boolean theAddContentLocationHeader, boolean respondGzip, RequestDetails theRequestDetails, IIdType theOperationResourceId, IPrimitiveType theOperationResourceLastUpdated) throws IOException { IRestfulResponse restUtil = theRequestDetails.getResponse(); // Determine response encoding @@ -596,7 +596,7 @@ public class RestfulServerUtils { // malicious images or HTML blocks being served up as content. restUtil.addHeader(Constants.HEADER_CONTENT_DISPOSITION, "Attachment;"); - return restUtil.sendAttachmentResponse(bin, stausCode, contentType); + return restUtil.sendAttachmentResponse(bin, theStausCode, contentType); } // Ok, we're not serving a binary resource, so apply default encoding @@ -656,7 +656,7 @@ public class RestfulServerUtils { } String charset = Constants.CHARSET_NAME_UTF8; - Writer writer = restUtil.getResponseWriter(stausCode, contentType, charset, respondGzip); + Writer writer = restUtil.getResponseWriter(theStausCode, theStatusMessage, contentType, charset, respondGzip); if (theResource == null) { // No response is being returned } else if (encodingDomainResourceAsText && theResource instanceof IResource) { @@ -666,7 +666,7 @@ public class RestfulServerUtils { parser.encodeResourceToWriter(theResource, writer); } - return restUtil.sendWriterResponse(stausCode, contentType, charset, writer); + return restUtil.sendWriterResponse(theStausCode, contentType, charset, writer); } public static IIdType fullyQualifyResourceIdOrReturnNull(IRestfulServerDefaults theServer, IBaseResource theResource, String theServerBase, IIdType theResourceId) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/ExceptionHandlingInterceptor.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/ExceptionHandlingInterceptor.java index 8ca555595d7..f4661151f5d 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/ExceptionHandlingInterceptor.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/ExceptionHandlingInterceptor.java @@ -39,6 +39,7 @@ import ca.uhn.fhir.rest.method.RequestDetails; import ca.uhn.fhir.rest.server.IRestfulResponse; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException; import ca.uhn.fhir.util.OperationOutcomeUtil; public class ExceptionHandlingInterceptor extends InterceptorAdapter { @@ -79,7 +80,15 @@ public class ExceptionHandlingInterceptor extends InterceptorAdapter { } } - return response.streamResponseAsResource(oo, true, Collections.singleton(SummaryEnum.FALSE), statusCode, false, false); + String statusMessage = null; + if (theException instanceof UnclassifiedServerFailureException) { + String sm = theException.getMessage(); + if (isNotBlank(sm) && sm.indexOf('\n') == -1) { + statusMessage = sm; + } + } + + return response.streamResponseAsResource(oo, true, Collections.singleton(SummaryEnum.FALSE), statusCode, statusMessage, false, false); // theResponse.setStatus(statusCode); // theRequestDetails.getServer().addHeadersToResponse(theResponse); // theResponse.setContentType("text/plain"); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlighterInterceptor.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlighterInterceptor.java index a7040d705fc..bfd47b2fd42 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlighterInterceptor.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlighterInterceptor.java @@ -340,19 +340,21 @@ public class ResponseHighlighterInterceptor extends InterceptorAdapter { b.append("

"); b.append("This result is being rendered in HTML for easy viewing. "); - b.append("You may view this content as "); + b.append("You may access this content as "); b.append("Raw JSON, "); + b.append("\">Raw JSON or "); b.append("Raw XML, "); + b.append(" or view this content in "); + b.append("HTML JSON, "); + b.append("\">HTML JSON "); b.append("or "); b.append("server level + */ + IAuthRuleBuilderRuleOpClassifierFinished onServer(); + + /** + * Rule applies to invocations of this operation at the type level + */ + IAuthRuleBuilderRuleOpClassifierFinished onType(Class theType); + + /** + * Rule applies to invocations of this operation at the instance level + */ + IAuthRuleBuilderRuleOpClassifierFinished onInstance(IIdType theInstanceId); + +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleBuilderRule.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleBuilderRule.java index 725aff15ea2..3e324f60895 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleBuilderRule.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleBuilderRule.java @@ -55,4 +55,9 @@ public interface IAuthRuleBuilderRule { */ IAuthRuleBuilderRuleOp write(); + /** + * This rule applies to a FHIR operation (e.g. $validate) + */ + IAuthRuleBuilderOperation operation(); + } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/OperationRule.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/OperationRule.java new file mode 100644 index 00000000000..a4581cb4710 --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/OperationRule.java @@ -0,0 +1,96 @@ +package ca.uhn.fhir.rest.server.interceptor.auth; + +import java.util.HashSet; +import java.util.List; + +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.rest.api.RestOperationTypeEnum; +import ca.uhn.fhir.rest.method.RequestDetails; +import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor.Verdict; + +class OperationRule extends BaseRule implements IAuthRule { + + public OperationRule(String theRuleName) { + super(theRuleName); + } + + private String myOperationName; + private boolean myAppliesToServer; + private HashSet> myAppliesToTypes; + private List myAppliesToIds; + + /** + * Must include the leading $ + */ + public void setOperationName(String theOperationName) { + myOperationName = theOperationName; + } + + public String getOperationName() { + return myOperationName; + } + + @Override + public Verdict applyRule(RestOperationTypeEnum theOperation, RequestDetails theRequestDetails, IBaseResource theInputResource, IBaseResource theOutputResource, IRuleApplier theRuleApplier) { + FhirContext ctx = theRequestDetails.getServer().getFhirContext(); + + boolean applies = false; + switch (theOperation) { + case EXTENDED_OPERATION_SERVER: + if (myAppliesToServer) { + applies = true; + } + break; + case EXTENDED_OPERATION_TYPE: + if (myAppliesToTypes != null) { + for (Class next : myAppliesToTypes) { + String resName = ctx.getResourceDefinition(theRequestDetails.getResourceName()).getName(); + if (resName.equals(theRequestDetails.getResourceName())) { + applies = true; + break; + } + } + } + break; + case EXTENDED_OPERATION_INSTANCE: + if (myAppliesToIds != null) { + String instanceId = theRequestDetails.getId().toUnqualifiedVersionless().getValue(); + for (IIdType next : myAppliesToIds) { + if (next.toUnqualifiedVersionless().getValue().equals(instanceId)) { + applies = true; + break; + } + } + } + break; + default: + return null; + } + + if (!applies) { + return null; + } + + if (myOperationName != null && !myOperationName.equals(theRequestDetails.getOperation())) { + return null; + } + + return newVerdict(); + } + + public void appliesToServer() { + myAppliesToServer = true; + } + + public void appliesToTypes(HashSet> theAppliesToTypes) { + myAppliesToTypes = theAppliesToTypes; + } + + public void appliesToInstances(List theAppliesToIds) { + myAppliesToIds = theAppliesToIds; + } + +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/Rule.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/Rule.java index 26e29efce77..ffd6fad82f6 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/Rule.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/Rule.java @@ -24,7 +24,6 @@ import java.util.Collection; import java.util.List; import java.util.Set; -import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; @@ -40,20 +39,18 @@ import ca.uhn.fhir.util.BundleUtil; import ca.uhn.fhir.util.BundleUtil.BundleEntryParts; import ca.uhn.fhir.util.FhirTerser; -class Rule implements IAuthRule { +class Rule extends BaseRule implements IAuthRule { private AppliesTypeEnum myAppliesTo; private Set myAppliesToTypes; private String myClassifierCompartmentName; private Collection myClassifierCompartmentOwners; private ClassifierTypeEnum myClassifierType; - private PolicyEnum myMode; - private String myName; private RuleOpEnum myOp; private TransactionAppliesToEnum myTransactionAppliesToOp; public Rule(String theRuleName) { - myName = theRuleName; + super(theRuleName); } @Override @@ -77,7 +74,7 @@ class Rule implements IAuthRule { case DELETE: if (theOperation == RestOperationTypeEnum.DELETE) { if (theInputResource == null) { - return new Verdict(myMode, this); + return newVerdict(); } else { appliesTo = theInputResource; } @@ -88,13 +85,13 @@ class Rule implements IAuthRule { case BATCH: case TRANSACTION: if (theInputResource != null && requestAppliesToTransaction(ctx, myOp, theInputResource)) { - if (myMode == PolicyEnum.DENY) { + if (getMode() == PolicyEnum.DENY) { return new Verdict(PolicyEnum.DENY, this); - } else { + } else { List inputResources = BundleUtil.toListOfEntries(ctx, (IBaseBundle) theInputResource); Verdict verdict = null; for (BundleEntryParts nextPart : inputResources) { - + IBaseResource inputResource = nextPart.getResource(); RestOperationTypeEnum operation = null; if (nextPart.getRequestType() == RequestTypeEnum.GET) { @@ -111,13 +108,13 @@ class Rule implements IAuthRule { /* * This is basically just being conservative - Be careful of transactions containing * nested operations and nested transactions. We block the by default. At some point - * it would be nice to be more nuanced here. + * it would be nice to be more nuanced here. */ RuntimeResourceDefinition resourceDef = ctx.getResourceDefinition(nextPart.getResource()); if ("Parameters".equals(resourceDef.getName()) || "Bundle".equals(resourceDef.getName())) { - throw new InvalidRequestException("Can not handle transaction with nested resource of type " + resourceDef.getName()); + throw new InvalidRequestException("Can not handle transaction with nested resource of type " + resourceDef.getName()); } - + Verdict newVerdict = theRuleApplier.applyRulesAndReturnDecision(operation, theRequestDetails, inputResource, null); if (newVerdict == null) { continue; @@ -155,7 +152,7 @@ class Rule implements IAuthRule { return new Verdict(PolicyEnum.DENY, this); case METADATA: if (theOperation == RestOperationTypeEnum.METADATA) { - return new Verdict(myMode, this); + return newVerdict(); } else { return null; } @@ -196,14 +193,15 @@ class Rule implements IAuthRule { throw new IllegalStateException("Unable to apply security to event of applies to type " + myAppliesTo); } - return new Verdict(myMode, this); + return newVerdict(); } + private boolean requestAppliesToTransaction(FhirContext theContext, RuleOpEnum theOp, IBaseResource theInputResource) { if (!"Bundle".equals(theContext.getResourceDefinition(theInputResource).getName())) { return false; } - + IBaseBundle request = (IBaseBundle) theInputResource; String bundleType = BundleUtil.getBundleType(theContext, request); switch (theOp) { @@ -216,11 +214,6 @@ class Rule implements IAuthRule { } } - @Override - public String getName() { - return myName; - } - public TransactionAppliesToEnum getTransactionAppliesToOp() { return myTransactionAppliesToOp; } @@ -245,9 +238,6 @@ class Rule implements IAuthRule { myClassifierType = theClassifierType; } - public void setMode(PolicyEnum theRuleMode) { - myMode = theRuleMode; - } public Rule setOp(RuleOpEnum theRuleOp) { myOp = theRuleOp; @@ -257,4 +247,5 @@ class Rule implements IAuthRule { public void setTransactionAppliesToOp(TransactionAppliesToEnum theOp) { myTransactionAppliesToOp = theOp; } + } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleBuilder.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleBuilder.java index 3f1027dd15f..7ffeeaa92c1 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleBuilder.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleBuilder.java @@ -24,6 +24,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Set; @@ -243,6 +244,80 @@ public class RuleBuilder implements IAuthRuleBuilder { } + private class RuleBuilderRuleOperation implements IAuthRuleBuilderOperation { + + private class RuleBuilderRuleOperationNamed implements IAuthRuleBuilderOperationNamed { + + private String myOperationName; + + public RuleBuilderRuleOperationNamed(String theOperationName) { + if (theOperationName != null && !theOperationName.startsWith("$")) { + myOperationName = '$' + theOperationName; + } else { + myOperationName = theOperationName; + } + } + + @Override + public IAuthRuleBuilderRuleOpClassifierFinished onServer() { + OperationRule rule = createRule(); + rule.appliesToServer(); + myRules.add(rule); + return new RuleBuilderFinished(); + } + + private OperationRule createRule() { + OperationRule rule = new OperationRule(myRuleName); + rule.setOperationName(myOperationName); + rule.setMode(myRuleMode); + return rule; + } + + @Override + public IAuthRuleBuilderRuleOpClassifierFinished onType(Class theType) { + Validate.notNull(theType, "theType must not be null"); + + OperationRule rule = createRule(); + HashSet> appliesToTypes = new HashSet>(); + appliesToTypes.add(theType); + rule.appliesToTypes(appliesToTypes); + myRules.add(rule); + return new RuleBuilderFinished(); + } + + @Override + public IAuthRuleBuilderRuleOpClassifierFinished onInstance(IIdType theInstanceId) { + Validate.notNull(theInstanceId, "theInstanceId must not be null"); + Validate.notBlank(theInstanceId.getResourceType(), "theInstanceId does not have a resource type"); + Validate.notBlank(theInstanceId.getIdPart(), "theInstanceId does not have an ID part"); + + OperationRule rule = createRule(); + ArrayList ids = new ArrayList(); + ids.add(theInstanceId); + rule.appliesToInstances(ids); + myRules.add(rule); + return new RuleBuilderFinished(); + } + + } + + @Override + public IAuthRuleBuilderOperationNamed named(String theOperationName) { + Validate.notBlank(theOperationName, "theOperationName must not be null or empty"); + return new RuleBuilderRuleOperationNamed(theOperationName); + } + + @Override + public IAuthRuleBuilderOperationNamed withAnyName() { + return new RuleBuilderRuleOperationNamed(null); + } + + } + + @Override + public IAuthRuleBuilderOperation operation() { + return new RuleBuilderRuleOperation(); + } } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleOpEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleOpEnum.java index 3d6296424c5..b9ce9f8839f 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleOpEnum.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleOpEnum.java @@ -28,5 +28,6 @@ enum RuleOpEnum { TRANSACTION, METADATA, BATCH, - DELETE + DELETE, + OPERATION } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRestfulResponse.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRestfulResponse.java index ffbc8281481..668d4277bb1 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRestfulResponse.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRestfulResponse.java @@ -1,5 +1,7 @@ package ca.uhn.fhir.rest.server.servlet; +import static org.apache.commons.lang3.StringUtils.isNotBlank; + /* * #%L * HAPI FHIR - Core Library @@ -61,12 +63,12 @@ public class ServletRestfulResponse extends RestfulResponse summaryMode = Collections.emptySet(); - return (Response) response.streamResponseAsResource(conformance, false, summaryMode, Constants.STATUS_HTTP_200_OK, true, false); + return (Response) response.streamResponseAsResource(conformance, false, summaryMode, Constants.STATUS_HTTP_200_OK, null, true, false); } return (Response) response.returnResponse(null, Constants.STATUS_HTTP_500_INTERNAL_ERROR, true, null, getResourceType().getSimpleName()); } diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponse.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponse.java index c6b0a729fb0..5b1c0c32471 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponse.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponse.java @@ -65,7 +65,7 @@ public class JaxRsResponse extends RestfulResponse { * by the server. */ @Override - public Writer getResponseWriter(int statusCode, String contentType, String charset, boolean respondGzip) + public Writer getResponseWriter(int theStatusCode, String theStatusMessage, String theContentType, String theCharset, boolean theRespondGzip) throws UnsupportedEncodingException, IOException { return new StringWriter(); } diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/GenericJaxRsClientDstu3Test.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/GenericJaxRsClientDstu3Test.java index dc9a5259345..504f4425fb8 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/GenericJaxRsClientDstu3Test.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/GenericJaxRsClientDstu3Test.java @@ -65,6 +65,7 @@ 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.api.SummaryEnum; +import ca.uhn.fhir.rest.client.BaseClient; import ca.uhn.fhir.rest.client.IGenericClient; import ca.uhn.fhir.rest.client.ServerValidationModeEnum; import ca.uhn.fhir.rest.client.api.Header; @@ -94,6 +95,7 @@ public class GenericJaxRsClientDstu3Test { ourCtx.setRestfulClientFactory(clientFactory); ourResponseCount = 0; + System.setProperty("hapi.client.keepresponses", "true"); } private String getPatientFeedWithOneResult() { @@ -211,7 +213,6 @@ public class GenericJaxRsClientDstu3Test { } @Test - @SuppressWarnings("deprecation") public void testConformance() throws Exception { IParser p = ourCtx.newXmlParser(); @@ -228,7 +229,7 @@ public class GenericJaxRsClientDstu3Test { //@formatter:off - Conformance resp = (Conformance)client.conformance(); + Conformance resp = (Conformance)client.fetchConformance().ofType(Conformance.class).execute(); //@formatter:on assertEquals("http://localhost:" + ourPort + "/fhir/metadata", ourRequestUri); 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..b92f62b22cc 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 = 2000; + // *** + // 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 2000 + *

+ */ + 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 2000 + *

+ */ + 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/dao/HistoryTuple.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/HistoryTuple.java deleted file mode 100644 index 4e632dd30cc..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/HistoryTuple.java +++ /dev/null @@ -1,79 +0,0 @@ -package ca.uhn.fhir.jpa.dao; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * 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.util.Date; - -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; - -class HistoryTuple implements Comparable { - - private Long myId; - private boolean myIsHistory; - private Date myUpdated; - - public HistoryTuple(boolean theIsHistory, Date theUpdated, Long theId) { - super(); - myIsHistory = theIsHistory; - myUpdated = theUpdated; - myId = theId; - } - - @Override - public int compareTo(HistoryTuple theO) { - return myUpdated.compareTo(theO.myUpdated); - } - - public Long getId() { - return myId; - } - - public boolean isHistory() { - return myIsHistory; - } - - public Date getUpdated() { - return myUpdated; - } - - public void setId(Long theId) { - myId = theId; - } - - public void setIsHistory(boolean theIsHistory) { - myIsHistory = theIsHistory; - } - - public void setUpdated(Date theUpdated) { - myUpdated = theUpdated; - } - - @Override - public String toString() { - ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE); - b.append("id", myId); - b.append("history", myIsHistory); - b.append("updated", myUpdated); - return b.build(); - } - -} 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..db13d0ee3df 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 @@ -55,14 +55,12 @@ import org.hibernate.search.annotations.Field; import org.hibernate.search.annotations.Fields; import org.hibernate.search.annotations.Indexed; import org.hibernate.search.annotations.Store; -import org.hibernate.search.indexes.interceptor.DontInterceptEntityInterceptor; import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum; -import ca.uhn.fhir.jpa.search.DeferConceptIndexingInterceptor; //@formatter:off @Entity -@Indexed(interceptor=DeferConceptIndexingInterceptor.class) +@Indexed() @Table(name="TRM_CONCEPT", uniqueConstraints= { @UniqueConstraint(name="IDX_CONCEPT_CS_CODE", columnNames= {"CODESYSTEM_PID", "CODE"}) }, indexes= { @@ -71,12 +69,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 +83,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 +100,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 +186,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/entity/TermConceptParentChildLink.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptParentChildLink.java index 36174b791e1..36c15af5e9c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptParentChildLink.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptParentChildLink.java @@ -66,6 +66,10 @@ public class TermConceptParentChildLink implements Serializable { return myChild; } + public RelationshipTypeEnum getRelationshipType() { + return myRelationshipType; + } + public TermCodeSystemVersion getCodeSystem() { return myCodeSystem; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaPlainProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaPlainProvider.java deleted file mode 100644 index 3df224f2bc7..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaPlainProvider.java +++ /dev/null @@ -1,44 +0,0 @@ -package ca.uhn.fhir.jpa.provider; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * 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 org.springframework.beans.factory.annotation.Required; - -import ca.uhn.fhir.jpa.dao.IFhirSystemDao; - -public class BaseJpaPlainProvider extends BaseJpaProvider { - - private IFhirSystemDao myDao; - - public BaseJpaPlainProvider() { - // nothing - } - - @Required - public void setDao(IFhirSystemDao theDao) { - myDao = theDao; - } - - public IFhirSystemDao getDao() { - return myDao; - } - -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderQuestionnaireResponseDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderQuestionnaireResponseDstu2.java deleted file mode 100644 index b42885adfb4..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderQuestionnaireResponseDstu2.java +++ /dev/null @@ -1,29 +0,0 @@ -package ca.uhn.fhir.jpa.provider; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * 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 ca.uhn.fhir.model.dstu2.resource.QuestionnaireResponse; - -public class BaseJpaResourceProviderQuestionnaireResponseDstu2 extends JpaResourceProviderDstu2 { - - // nothing yet - -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderQuestionnaireResponseDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderQuestionnaireResponseDstu3.java deleted file mode 100644 index e7e4f53b5a5..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderQuestionnaireResponseDstu3.java +++ /dev/null @@ -1,29 +0,0 @@ -package ca.uhn.fhir.jpa.provider.dstu3; - -import org.hl7.fhir.dstu3.model.QuestionnaireResponse; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * 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 BaseJpaResourceProviderQuestionnaireResponseDstu3 extends JpaResourceProviderDstu3 { - - // nothing yet - -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/TerminologyUploaderProviderDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/TerminologyUploaderProviderDstu3.java index 78f38b93a39..06162186e2d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/TerminologyUploaderProviderDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/TerminologyUploaderProviderDstu3.java @@ -26,7 +26,6 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; import java.io.FileInputStream; import java.io.IOException; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import javax.servlet.http.HttpServletRequest; @@ -85,7 +84,9 @@ public class TerminologyUploaderProviderDstu3 extends BaseJpaProvider { } else if (thePackage == null || thePackage.getData() == null || thePackage.getData().length == 0) { throw new InvalidRequestException("No 'localfile' or 'package' parameter, or package had no data"); } else { - data = Arrays.asList(thePackage.getData()); + data = new ArrayList(); + data.add(thePackage.getData()); + thePackage.setData(null); } String url = theUrl != null ? theUrl.getValueAsString() : null; 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 deleted file mode 100644 index f01a2df3761..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/DeferConceptIndexingInterceptor.java +++ /dev/null @@ -1,9 +0,0 @@ -package ca.uhn.fhir.jpa.search; - -import org.hibernate.search.indexes.interceptor.DontInterceptEntityInterceptor; - -public class DeferConceptIndexingInterceptor extends DontInterceptEntityInterceptor -//implements EntityIndexingInterceptor -{ - // nothing for now -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/SubscriptionWebsocketHandlerFactoryDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/SubscriptionWebsocketHandlerFactoryDstu2.java deleted file mode 100644 index f4bacf4e6b0..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/SubscriptionWebsocketHandlerFactoryDstu2.java +++ /dev/null @@ -1,42 +0,0 @@ -package ca.uhn.fhir.jpa.subscription; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * 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 org.springframework.beans.factory.FactoryBean; - -public class SubscriptionWebsocketHandlerFactoryDstu2 implements FactoryBean { - static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SubscriptionWebsocketHandlerDstu2.class); - - @Override - public ISubscriptionWebsocketHandler getObject() throws Exception { - return new SubscriptionWebsocketHandlerDstu2(); - } - - @Override - public Class getObjectType() { - return ISubscriptionWebsocketHandler.class; - } - - @Override - public boolean isSingleton() { - return false; - } -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/SubscriptionWebsocketHandlerFactoryDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/SubscriptionWebsocketHandlerFactoryDstu3.java deleted file mode 100644 index f43e36f0835..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/SubscriptionWebsocketHandlerFactoryDstu3.java +++ /dev/null @@ -1,42 +0,0 @@ -package ca.uhn.fhir.jpa.subscription; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * 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 org.springframework.beans.factory.FactoryBean; - -public class SubscriptionWebsocketHandlerFactoryDstu3 implements FactoryBean { - static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SubscriptionWebsocketHandlerDstu3.class); - - @Override - public ISubscriptionWebsocketHandler getObject() throws Exception { - return new SubscriptionWebsocketHandlerDstu3(); - } - - @Override - public Class getObjectType() { - return ISubscriptionWebsocketHandler.class; - } - - @Override - public boolean isSingleton() { - return false; - } -} 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..6953964590e 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 @@ -33,6 +33,7 @@ import javax.persistence.PersistenceContext; import javax.persistence.PersistenceContextType; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @@ -67,9 +68,13 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc { @Autowired protected ITermConceptDao myConceptDao; + private List myConceptLinksToSaveLater = new ArrayList(); + @Autowired private ITermConceptParentChildLinkDao myConceptParentChildLinkDao; + private List myConceptsToSaveLater = new ArrayList(); + @Autowired protected FhirContext myContext; @@ -78,6 +83,8 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc { @PersistenceContext(type = PersistenceContextType.TRANSACTION) protected EntityManager myEntityManager; + + private boolean myProcessDeferred = true; private boolean addToSet(Set theSetToPopulate, TermConcept theConcept) { boolean retVal = theSetToPopulate.add(theConcept); @@ -197,35 +204,101 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc { TermCodeSystemVersion csv = cs.getCurrentVersion(); return csv; } - private TermCodeSystem getCodeSystem(String theSystem) { TermCodeSystem cs = myCodeSystemDao.findByCodeSystemUri(theSystem); return cs; } + + private void parentPids(TermConcept theNextConcept, Set theParentPids) { + for (TermConceptParentChildLink nextParentLink : theNextConcept.getParents()){ + TermConcept parent = nextParentLink.getParent(); + if (parent != null && theParentPids.add(parent.getId())) { + parentPids(parent, theParentPids); + } + } + } private void persistChildren(TermConcept theConcept, TermCodeSystemVersion theCodeSystem, IdentityHashMap theConceptsStack, int theTotalConcepts) { if (theConceptsStack.put(theConcept, PLACEHOLDER_OBJECT) != null) { return; } - if (theConceptsStack.size() % 1000 == 0) { + if (theConceptsStack.size() == 1 || theConceptsStack.size() % 10000 == 0) { float pct = (float) theConceptsStack.size() / (float) theTotalConcepts; - ourLog.info("Have saved {}/{} concepts ({}%), flushing", theConceptsStack.size(), theTotalConcepts, (int)( pct*100.0f)); - myConceptDao.flush(); - myConceptParentChildLinkDao.flush(); + ourLog.info("Have processed {}/{} concepts ({}%)", theConceptsStack.size(), theTotalConcepts, (int)( pct*100.0f)); } theConcept.setCodeSystem(theCodeSystem); theConcept.setIndexStatus(BaseHapiFhirDao.INDEX_STATUS_INDEXED); - myConceptDao.save(theConcept); + Set parentPids = new HashSet(); + parentPids(theConcept, parentPids); + theConcept.setParentPids(parentPids); + + if (theConceptsStack.size() <= myDaoConfig.getDeferIndexingForCodesystemsOfSize()) { + myConceptDao.save(theConcept); + } else { + myConceptsToSaveLater.add(theConcept); + } + for (TermConceptParentChildLink next : theConcept.getChildren()) { persistChildren(next.getChild(), theCodeSystem, theConceptsStack, theTotalConcepts); } for (TermConceptParentChildLink next : theConcept.getChildren()) { - myConceptParentChildLinkDao.save(next); + if (theConceptsStack.size() <= myDaoConfig.getDeferIndexingForCodesystemsOfSize()) { + myConceptParentChildLinkDao.save(next); + } else { + myConceptLinksToSaveLater.add(next); + } } + + } + + private void populateVersion(TermConcept theNext, TermCodeSystemVersion theCodeSystemVersion) { + if (theNext.getCodeSystem() != null) { + return; + } + theNext.setCodeSystem(theCodeSystemVersion); + for (TermConceptParentChildLink next : theNext.getChildren()) { + populateVersion(next.getChild(), theCodeSystemVersion); + } + } + + @Scheduled(fixedRate=5000) + @Transactional(propagation=Propagation.REQUIRED) + @Override + public synchronized void saveDeferred() { + if (!myProcessDeferred || ((myConceptsToSaveLater.isEmpty() && myConceptLinksToSaveLater.isEmpty()))) { + return; + } + + int codeCount = 0, relCount = 0; + + int count = Math.min(myDaoConfig.getDeferIndexingForCodesystemsOfSize(), myConceptsToSaveLater.size()); + ourLog.info("Saving {} deferred concepts...", count); + while (codeCount < count && myConceptsToSaveLater.size() > 0) { + TermConcept next = myConceptsToSaveLater.remove(0); + myConceptDao.save(next); + codeCount++; + } + + if (codeCount == 0) { + count = Math.min(myDaoConfig.getDeferIndexingForCodesystemsOfSize(), myConceptLinksToSaveLater.size()); + ourLog.info("Saving {} deferred concept relationships...", count); + while (relCount < count && myConceptLinksToSaveLater.size() > 0) { + TermConceptParentChildLink next = myConceptLinksToSaveLater.remove(0); + myConceptParentChildLinkDao.save(next); + relCount++; + } + } + + ourLog.info("Saved {} deferred concepts ({} remain) and {} deferred relationships ({} remain)", new Object[] {codeCount, myConceptsToSaveLater.size(), relCount, myConceptLinksToSaveLater.size()}); + } + + @Override + public void setProcessDeferred(boolean theProcessDeferred) { + myProcessDeferred = theProcessDeferred; } @Override @@ -263,7 +336,7 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc { ourLog.info("Validating all codes in CodeSystem for storage (this can take some time for large sets)"); // Validate the code system - IdentityHashMap conceptsStack = new IdentityHashMap(); + ArrayList conceptsStack = new ArrayList(); IdentityHashMap allConcepts = new IdentityHashMap(); int totalCodeCount = 0; for (TermConcept next : theCodeSystemVersion.getConcepts()) { @@ -279,11 +352,17 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc { codeSystem.setCurrentVersion(theCodeSystemVersion); codeSystem = myCodeSystemDao.saveAndFlush(codeSystem); - ourLog.info("Saving {} concepts...", totalCodeCount); + ourLog.info("Setting codesystemversion on {} concepts...", totalCodeCount); - conceptsStack = new IdentityHashMap(); for (TermConcept next : theCodeSystemVersion.getConcepts()) { - persistChildren(next, codeSystemVersion, conceptsStack, totalCodeCount); + populateVersion(next, codeSystemVersion); + } + + ourLog.info("Saving {} concepts...", totalCodeCount); + + IdentityHashMap conceptsStack2 = new IdentityHashMap(); + for (TermConcept next : theCodeSystemVersion.getConcepts()) { + persistChildren(next, codeSystemVersion, conceptsStack2, totalCodeCount); } ourLog.info("Done saving concepts, flushing to database"); @@ -291,27 +370,6 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc { myConceptDao.flush(); myConceptParentChildLinkDao.flush(); - ourLog.info("Building multi-axial hierarchy..."); - - int index = 0; - int totalParents = 0; - for (TermConcept nextConcept : conceptsStack.keySet()) { - - if (index++ % 1000 == 0) { - float pct = (float) index / (float) totalCodeCount; - ourLog.info("Have built hierarchy for {}/{} concepts - {}%", index, totalCodeCount, (int)( pct*100.0f)); - } - - Set parentPids = new HashSet(); - parentPids(nextConcept, parentPids); - nextConcept.setParentPids(parentPids); - totalParents += parentPids.size(); - - myConceptDao.save(nextConcept); - } - - ourLog.info("Done building hierarchy, found {} parents", totalParents); - /* * For now we always delete old versions.. At some point it would be nice to allow configuration to keep old versions */ @@ -324,17 +382,12 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc { } ourLog.info("Done deleting old code system versions"); - } - - private void parentPids(TermConcept theNextConcept, Set theParentPids) { - for (TermConceptParentChildLink nextParentLink : theNextConcept.getParents()){ - TermConcept parent = nextParentLink.getParent(); - if (parent != null && theParentPids.add(parent.getId())) { - parentPids(parent, theParentPids); - } + + if (myConceptsToSaveLater.size() > 0 || myConceptLinksToSaveLater.size() > 0) { + ourLog.info("Note that some concept saving was deferred - still have {} concepts and {} relationships", myConceptsToSaveLater.size(), myConceptLinksToSaveLater.size()); } } - + @Override public boolean supportsSystem(String theSystem) { TermCodeSystem cs = getCodeSystem(theSystem); @@ -349,16 +402,17 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc { return retVal; } - private int validateConceptForStorage(TermConcept theConcept, TermCodeSystemVersion theCodeSystem, IdentityHashMap theConceptsStack, + private int validateConceptForStorage(TermConcept theConcept, TermCodeSystemVersion theCodeSystem, ArrayList theConceptsStack, IdentityHashMap theAllConcepts) { ValidateUtil.isTrueOrThrowInvalidRequest(theConcept.getCodeSystem() != null, "CodesystemValue is null"); ValidateUtil.isTrueOrThrowInvalidRequest(theConcept.getCodeSystem() == theCodeSystem, "CodeSystems are not equal"); ValidateUtil.isNotBlankOrThrowInvalidRequest(theConcept.getCode(), "Codesystem contains a code with no code value"); - if (theConceptsStack.put(theConcept, PLACEHOLDER_OBJECT) != null) { + if (theConceptsStack.contains(theConcept.getCode())) { throw new InvalidRequestException("CodeSystem contains circular reference around code " + theConcept.getCode()); } - + theConceptsStack.add(theConcept.getCode()); + int retVal = 0; if (theAllConcepts.put(theConcept, theAllConcepts) == null) { if (theAllConcepts.size() % 1000 == 0) { @@ -372,7 +426,7 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc { retVal += validateConceptForStorage(next.getChild(), theCodeSystem, theConceptsStack, theAllConcepts); } - theConceptsStack.remove(theConcept); + theConceptsStack.remove(theConceptsStack.size() - 1); return retVal; } 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/main/java/ca/uhn/fhir/jpa/term/IHapiTerminologySvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IHapiTerminologySvc.java index 233a3d1d05a..a7c8f6815d5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IHapiTerminologySvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IHapiTerminologySvc.java @@ -48,4 +48,12 @@ public interface IHapiTerminologySvc { List findCodes(String theSystem); + void saveDeferred(); + + /** + * This is mostly for unit tests - we can disable processing of deferred concepts + * by changing this flag + */ + void setProcessDeferred(boolean theProcessDeferred); + } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvc.java index c338118269f..7f4b6b18c03 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvc.java @@ -1,6 +1,5 @@ package ca.uhn.fhir.jpa.term; -import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; /* @@ -24,20 +23,19 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; */ import java.io.BufferedInputStream; -import java.io.BufferedReader; import java.io.ByteArrayInputStream; -import java.io.File; import java.io.FileOutputStream; -import java.io.FileReader; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.io.OutputStream; import java.io.Reader; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; -import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -56,18 +54,20 @@ import org.apache.commons.lang3.Validate; import org.springframework.beans.factory.annotation.Autowired; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Charsets; import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink; import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum; -import ca.uhn.fhir.jpa.term.TerminologyLoaderSvc.LoincHierarchyHandler; +import ca.uhn.fhir.jpa.util.Counter; import ca.uhn.fhir.rest.method.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; -import ca.uhn.fhir.util.CoverageIgnore; public class TerminologyLoaderSvc implements IHapiTerminologyLoaderSvc { + private static final int LOG_INCREMENT = 100000; + public static final String LOINC_FILE = "loinc.csv"; public static final String LOINC_HIERARCHY_FILE = "MULTI-AXIAL_HIERARCHY.CSV"; @@ -79,15 +79,8 @@ public class TerminologyLoaderSvc implements IHapiTerminologyLoaderSvc { @Autowired private IHapiTerminologySvc myTermSvc; - private void cleanUpTemporaryFiles(Map filenameToFile) { - ourLog.info("Finished terminology file import, cleaning up temporary files"); - for (File nextFile : filenameToFile.values()) { - nextFile.delete(); - } - } - - private void dropCircularRefs(TermConcept theConcept, LinkedHashSet theChain, Map theCode2concept) { - + private void dropCircularRefs(TermConcept theConcept, ArrayList theChain, Map theCode2concept, Counter theCircularCounter) { + theChain.add(theConcept.getCode()); for (Iterator childIter = theConcept.getChildren().iterator(); childIter.hasNext();) { TermConceptParentChildLink next = childIter.next(); @@ -110,46 +103,25 @@ public class TerminologyLoaderSvc implements IHapiTerminologyLoaderSvc { ourLog.info(b.toString(), theConcept.getCode()); childIter.remove(); } else { - dropCircularRefs(nextChild, theChain, theCode2concept); + dropCircularRefs(nextChild, theChain, theCode2concept, theCircularCounter); } } - theChain.remove(theConcept.getCode()); + theChain.remove(theChain.size() - 1); } - private Map extractFiles(List theZipBytes, List theExpectedFilenameFragments) { - Map filenameToFile = new HashMap(); + private void extractFiles(List theZipBytes, List theExpectedFilenameFragments) { + Set foundFragments = new HashSet(); for (byte[] nextZipBytes : theZipBytes) { ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new ByteArrayInputStream(nextZipBytes))); try { for (ZipEntry nextEntry; (nextEntry = zis.getNextEntry()) != null;) { - ZippedFileInputStream inputStream = new ZippedFileInputStream(zis); - - boolean want = false; for (String next : theExpectedFilenameFragments) { if (nextEntry.getName().contains(next)) { - want = true; + foundFragments.add(next); } } - - if (!want) { - ourLog.info("Ignoring zip entry: {}", nextEntry.getName()); - continue; - } - - ourLog.info("Streaming ZIP entry {} into temporary file", nextEntry.getName()); - - File nextOutFile = File.createTempFile("hapi_fhir", ".csv"); - nextOutFile.deleteOnExit(); - OutputStream outputStream = new SinkOutputStream(new FileOutputStream(nextOutFile, false), nextEntry.getName()); - try { - IOUtils.copyLarge(inputStream, outputStream); - } finally { - IOUtils.closeQuietly(outputStream); - } - - filenameToFile.put(nextEntry.getName(), nextOutFile); } } catch (IOException e) { throw new InternalErrorException(e); @@ -158,10 +130,12 @@ public class TerminologyLoaderSvc implements IHapiTerminologyLoaderSvc { } } - if (filenameToFile.size() != theExpectedFilenameFragments.size()) { - throw new InvalidRequestException("Invalid input zip file, expected zip to contain the following name fragments: " + theExpectedFilenameFragments + " but found: " + filenameToFile.keySet()); + for (String next : theExpectedFilenameFragments) { + if (!foundFragments.contains(next)) { + throw new InvalidRequestException("Invalid input zip file, expected zip to contain the following name fragments: " + theExpectedFilenameFragments + " but found: " + foundFragments); + } } - return filenameToFile; + } public String firstNonBlank(String... theStrings) { @@ -185,49 +159,54 @@ public class TerminologyLoaderSvc implements IHapiTerminologyLoaderSvc { return concept; } - private void iterateOverZipFile(Map theFilenameToFile, String fileNamePart, IRecordHandler handler, char theDelimiter, QuoteMode theQuoteMode) { + private void iterateOverZipFile(List theZipBytes, String fileNamePart, IRecordHandler handler, char theDelimiter, QuoteMode theQuoteMode) { boolean found = false; - for (Entry nextEntry : new HashMap(theFilenameToFile).entrySet()) { - if (nextEntry.getKey().contains(fileNamePart)) { - ourLog.info("Processing file {}", nextEntry.getKey()); - found = true; + for (byte[] nextZipBytes : theZipBytes) { + ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new ByteArrayInputStream(nextZipBytes))); + try { + for (ZipEntry nextEntry; (nextEntry = zis.getNextEntry()) != null;) { + ZippedFileInputStream inputStream = new ZippedFileInputStream(zis); - Reader reader = null; - CSVParser parsed = null; - try { - reader = new BufferedReader(new FileReader(nextEntry.getValue())); - CSVFormat format = CSVFormat.newFormat(theDelimiter).withFirstRecordAsHeader(); - if (theQuoteMode != null) { - format = format.withQuote('"').withQuoteMode(theQuoteMode); - } - parsed = new CSVParser(reader, format); - Iterator iter = parsed.iterator(); - ourLog.debug("Header map: {}", parsed.getHeaderMap()); + String nextFilename = nextEntry.getName(); + if (nextFilename.contains(fileNamePart)) { + ourLog.info("Processing file {}", nextFilename); + found = true; - int count = 0; - int logIncrement = 100000; - int nextLoggedCount = logIncrement; - while (iter.hasNext()) { - CSVRecord nextRecord = iter.next(); - handler.accept(nextRecord); - count++; - if (count >= nextLoggedCount) { - ourLog.info(" * Processed {} records in {}", count, fileNamePart); - nextLoggedCount += logIncrement; + Reader reader = null; + CSVParser parsed = null; + try { + reader = new InputStreamReader(zis, Charsets.UTF_8); + CSVFormat format = CSVFormat.newFormat(theDelimiter).withFirstRecordAsHeader(); + if (theQuoteMode != null) { + format = format.withQuote('"').withQuoteMode(theQuoteMode); + } + parsed = new CSVParser(reader, format); + Iterator iter = parsed.iterator(); + ourLog.debug("Header map: {}", parsed.getHeaderMap()); + + int count = 0; + int logIncrement = LOG_INCREMENT; + int nextLoggedCount = 0; + while (iter.hasNext()) { + CSVRecord nextRecord = iter.next(); + handler.accept(nextRecord); + count++; + if (count >= nextLoggedCount) { + ourLog.info(" * Processed {} records in {}", count, nextFilename); + nextLoggedCount += logIncrement; + } + } + + } catch (IOException e) { + throw new InternalErrorException(e); } } - - ourLog.info("Deleting temporary file: {}", nextEntry.getValue()); - nextEntry.getValue().delete(); - theFilenameToFile.remove(nextEntry.getKey()); - - } catch (IOException e) { - throw new InternalErrorException(e); - } finally { - IOUtils.closeQuietly(parsed); - IOUtils.closeQuietly(reader); } + } catch (IOException e) { + throw new InternalErrorException(e); + } finally { + IOUtils.closeQuietly(zis); } } @@ -239,42 +218,36 @@ public class TerminologyLoaderSvc implements IHapiTerminologyLoaderSvc { public UploadStatistics loadLoinc(List theZipBytes, RequestDetails theRequestDetails) { List expectedFilenameFragments = Arrays.asList(LOINC_FILE, LOINC_HIERARCHY_FILE); - Map filenameToFile = extractFiles(theZipBytes, expectedFilenameFragments); + extractFiles(theZipBytes, expectedFilenameFragments); ourLog.info("Beginning LOINC processing"); - try { - return processLoincFiles(filenameToFile, theRequestDetails); - } finally { - cleanUpTemporaryFiles(filenameToFile); - } + return processLoincFiles(theZipBytes, theRequestDetails); } @Override public UploadStatistics loadSnomedCt(List theZipBytes, RequestDetails theRequestDetails) { List expectedFilenameFragments = Arrays.asList(SCT_FILE_DESCRIPTION, SCT_FILE_RELATIONSHIP, SCT_FILE_CONCEPT); - Map filenameToFile = extractFiles(theZipBytes, expectedFilenameFragments); + extractFiles(theZipBytes, expectedFilenameFragments); ourLog.info("Beginning SNOMED CT processing"); - try { - return processSnomedCtFiles(filenameToFile, theRequestDetails); - } finally { - cleanUpTemporaryFiles(filenameToFile); - } + return processSnomedCtFiles(theZipBytes, theRequestDetails); } - UploadStatistics processLoincFiles(Map filenameToFile, RequestDetails theRequestDetails) { + UploadStatistics processLoincFiles(List theZipBytes, RequestDetails theRequestDetails) { final TermCodeSystemVersion codeSystemVersion = new TermCodeSystemVersion(); final Map code2concept = new HashMap(); IRecordHandler handler = new LoincHandler(codeSystemVersion, code2concept); - iterateOverZipFile(filenameToFile, LOINC_FILE, handler, ',', QuoteMode.NON_NUMERIC); + iterateOverZipFile(theZipBytes, LOINC_FILE, handler, ',', QuoteMode.NON_NUMERIC); handler = new LoincHierarchyHandler(codeSystemVersion, code2concept); - iterateOverZipFile(filenameToFile, LOINC_HIERARCHY_FILE, handler, ',', QuoteMode.NON_NUMERIC); + iterateOverZipFile(theZipBytes, LOINC_HIERARCHY_FILE, handler, ',', QuoteMode.NON_NUMERIC); + theZipBytes.clear(); + for (Iterator> iter = code2concept.entrySet().iterator(); iter.hasNext();) { Entry next = iter.next(); // if (isBlank(next.getKey())) { @@ -290,39 +263,60 @@ public class TerminologyLoaderSvc implements IHapiTerminologyLoaderSvc { ourLog.info("Have {} total concepts, {} root concepts", code2concept.size(), codeSystemVersion.getConcepts().size()); - myTermSvc.storeNewCodeSystemVersion(LOINC_URL, codeSystemVersion, theRequestDetails); + String url = LOINC_URL; + storeCodeSystem(theRequestDetails, codeSystemVersion, url); return new UploadStatistics(code2concept.size()); } - UploadStatistics processSnomedCtFiles(Map filenameToFile, RequestDetails theRequestDetails) { + private void storeCodeSystem(RequestDetails theRequestDetails, final TermCodeSystemVersion codeSystemVersion, String url) { + myTermSvc.setProcessDeferred(false); + myTermSvc.storeNewCodeSystemVersion(url, codeSystemVersion, theRequestDetails); + myTermSvc.setProcessDeferred(true); + } + + UploadStatistics processSnomedCtFiles(List theZipBytes, RequestDetails theRequestDetails) { final TermCodeSystemVersion codeSystemVersion = new TermCodeSystemVersion(); final Map id2concept = new HashMap(); final Map code2concept = new HashMap(); final Set validConceptIds = new HashSet(); IRecordHandler handler = new SctHandlerConcept(validConceptIds); - iterateOverZipFile(filenameToFile, SCT_FILE_CONCEPT, handler, '\t', null); + iterateOverZipFile(theZipBytes, SCT_FILE_CONCEPT, handler, '\t', null); ourLog.info("Have {} valid concept IDs", validConceptIds.size()); handler = new SctHandlerDescription(validConceptIds, code2concept, id2concept, codeSystemVersion); - iterateOverZipFile(filenameToFile, SCT_FILE_DESCRIPTION, handler, '\t', null); + iterateOverZipFile(theZipBytes, SCT_FILE_DESCRIPTION, handler, '\t', null); ourLog.info("Got {} concepts, cloning map", code2concept.size()); final HashMap rootConcepts = new HashMap(code2concept); handler = new SctHandlerRelationship(codeSystemVersion, rootConcepts, code2concept); - iterateOverZipFile(filenameToFile, SCT_FILE_RELATIONSHIP, handler, '\t', null); + iterateOverZipFile(theZipBytes, SCT_FILE_RELATIONSHIP, handler, '\t', null); + theZipBytes.clear(); + + ourLog.info("Looking for root codes"); + for (Iterator> iter = rootConcepts.entrySet().iterator(); iter.hasNext(); ) { + if (iter.next().getValue().getParents().isEmpty() == false) { + iter.remove(); + } + } + ourLog.info("Done loading SNOMED CT files - {} root codes, {} total codes", rootConcepts.size(), code2concept.size()); + Counter circularCounter = new Counter(); for (TermConcept next : rootConcepts.values()) { - dropCircularRefs(next, new LinkedHashSet(), code2concept); + long count = circularCounter.getThenAdd(); + float pct = ((float)count / rootConcepts.size()) * 100.0f; + ourLog.info(" * Scanning for circular refs - have scanned {} / {} codes ({}%)", count, rootConcepts.size(), pct); + dropCircularRefs(next, new ArrayList(), code2concept, circularCounter); } codeSystemVersion.getConcepts().addAll(rootConcepts.values()); - myTermSvc.storeNewCodeSystemVersion(SCT_URL, codeSystemVersion, theRequestDetails); + String url = SCT_URL; + storeCodeSystem(theRequestDetails, codeSystemVersion, url); return new UploadStatistics(code2concept.size()); } @@ -332,20 +326,6 @@ public class TerminologyLoaderSvc implements IHapiTerminologyLoaderSvc { myTermSvc = theTermSvc; } - @CoverageIgnore - public static void main(String[] args) throws Exception { - TerminologyLoaderSvc svc = new TerminologyLoaderSvc(); - - // byte[] bytes = IOUtils.toByteArray(new FileInputStream("/Users/james/Downloads/SnomedCT_Release_INT_20160131_Full.zip")); - // svc.loadSnomedCt(bytes); - - Map files = new HashMap(); - files.put(SCT_FILE_CONCEPT, new File("/Users/james/tmp/sct/SnomedCT_Release_INT_20160131_Full/Terminology/sct2_Concept_Full_INT_20160131.txt")); - files.put(SCT_FILE_DESCRIPTION, new File("/Users/james/tmp/sct/SnomedCT_Release_INT_20160131_Full/Terminology/sct2_Description_Full-en_INT_20160131.txt")); - files.put(SCT_FILE_RELATIONSHIP, new File("/Users/james/tmp/sct/SnomedCT_Release_INT_20160131_Full/Terminology/sct2_Relationship_Full_INT_20160131.txt")); - svc.processSnomedCtFiles(files, null); - } - private interface IRecordHandler { void accept(CSVRecord theRecord); } @@ -506,23 +486,33 @@ public class TerminologyLoaderSvc implements IHapiTerminologyLoaderSvc { String destinationId = theRecord.get("destinationId"); String typeId = theRecord.get("typeId"); boolean active = "1".equals(theRecord.get("active")); - if (!active) { - return; - } + TermConcept typeConcept = myCode2concept.get(typeId); TermConcept sourceConcept = myCode2concept.get(sourceId); TermConcept targetConcept = myCode2concept.get(destinationId); if (sourceConcept != null && targetConcept != null && typeConcept != null) { if (typeConcept.getDisplay().equals("Is a (attribute)")) { + RelationshipTypeEnum relationshipType = RelationshipTypeEnum.ISA; if (!sourceId.equals(destinationId)) { - TermConceptParentChildLink link = new TermConceptParentChildLink(); - link.setChild(sourceConcept); - link.setParent(targetConcept); - link.setRelationshipType(TermConceptParentChildLink.RelationshipTypeEnum.ISA); - link.setCodeSystem(myCodeSystemVersion); - myRootConcepts.remove(link.getChild().getCode()); - - targetConcept.addChild(sourceConcept, RelationshipTypeEnum.ISA); + if (active) { + TermConceptParentChildLink link = new TermConceptParentChildLink(); + link.setChild(sourceConcept); + link.setParent(targetConcept); + link.setRelationshipType(relationshipType); + link.setCodeSystem(myCodeSystemVersion); + + targetConcept.addChild(sourceConcept, relationshipType); + } else { + // not active, so we're removing any existing links + for (TermConceptParentChildLink next : new ArrayList(targetConcept.getChildren())) { + if (next.getRelationshipType() == relationshipType) { + if (next.getChild().getCode().equals(sourceConcept.getCode())) { + next.getParent().getChildren().remove(next); + next.getChild().getParents().remove(next); + } + } + } + } } } else if (ignoredTypes.contains(typeConcept.getDisplay())) { // ignore @@ -534,57 +524,6 @@ public class TerminologyLoaderSvc implements IHapiTerminologyLoaderSvc { } - private static class SinkOutputStream extends OutputStream { - - private static final long LOG_INCREMENT = 10 * FileUtils.ONE_MB; - private int myBytes; - private String myFilename; - private long myNextLogCount = LOG_INCREMENT; - private FileOutputStream myWrap; - - public SinkOutputStream(FileOutputStream theWrap, String theFilename) { - myWrap = theWrap; - myFilename = theFilename; - } - - private void addCount(int theCount) { - myBytes += theCount; - if (myBytes > myNextLogCount) { - ourLog.info(" * Wrote {} of {}", FileUtils.byteCountToDisplaySize(myBytes), myFilename); - myNextLogCount = myBytes + LOG_INCREMENT; - } - } - - @Override - public void close() throws IOException { - myWrap.close(); - } - - @Override - public void flush() throws IOException { - myWrap.flush(); - } - - @Override - public void write(byte[] theB) throws IOException { - myWrap.write(theB); - addCount(theB.length); - } - - @Override - public void write(byte[] theB, int theOff, int theLen) throws IOException { - myWrap.write(theB, theOff, theLen); - addCount(theLen); - } - - @Override - public void write(int theB) throws IOException { - myWrap.write(theB); - addCount(1); - } - - } - private static class ZippedFileInputStream extends InputStream { private ZipInputStream is; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/Counter.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/Counter.java new file mode 100644 index 00000000000..f0552bf25cf --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/Counter.java @@ -0,0 +1,11 @@ +package ca.uhn.fhir.jpa.util; + +public class Counter { + + private long myCount; + + public long getThenAdd() { + return myCount++; + } + +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/CustomNamingStrategy.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/CustomNamingStrategy.java deleted file mode 100644 index 2fe5cc1cd99..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/CustomNamingStrategy.java +++ /dev/null @@ -1,65 +0,0 @@ -package ca.uhn.fhir.jpa.util; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * 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 org.hibernate.cfg.ImprovedNamingStrategy; - -public class CustomNamingStrategy extends ImprovedNamingStrategy { - - private static final long serialVersionUID = 1L; - private static final String PREFIX = "FR_"; - -// @Override -// public String classToTableName(final String className) { -// return this.addPrefix(super.classToTableName(className)); -// } -// -// @Override -// public String collectionTableName(final String ownerEntity, -// final String ownerEntityTable, final String associatedEntity, -// final String associatedEntityTable, final String propertyName) { -// return this.addPrefix(super.collectionTableName(ownerEntity, -// ownerEntityTable, associatedEntity, associatedEntityTable, -// propertyName)); -// } -// -// @Override -// public String foreignKeyColumnName(String thePropertyName, String thePropertyEntityName, String thePropertyTableName, String theReferencedColumnName) { -// String foreignKeyColumnName = super.foreignKeyColumnName(thePropertyName, thePropertyEntityName, thePropertyTableName, theReferencedColumnName); -// return foreignKeyColumnName; -// } -// -// @Override -// public String logicalCollectionTableName(final String tableName, -// final String ownerEntityTable, final String associatedEntityTable, -// final String propertyName) { -// return this.addPrefix(super.logicalCollectionTableName(tableName, -// ownerEntityTable, associatedEntityTable, propertyName)); -// } -// -// private String addPrefix(final String composedTableName) { -// -// return PREFIX -// + composedTableName.toUpperCase().replace("_", ""); -// -// } - -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/HapiDerbyTenSevenDialect.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/HapiDerbyTenSevenDialect.java deleted file mode 100644 index 8e45651122d..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/HapiDerbyTenSevenDialect.java +++ /dev/null @@ -1,36 +0,0 @@ -package ca.uhn.fhir.jpa.util; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * 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 org.hibernate.dialect.DerbyTenSevenDialect; - -/** - * As of Hibernate 5.0.1, DerbyTenSevenDialect doesn't seem to work when updating - * the schema, as it tries to create a duplicate schema - */ -public class HapiDerbyTenSevenDialect extends DerbyTenSevenDialect { - - @Override - public String getQuerySequencesString() { - return "select SEQUENCENAME from sys.syssequences"; - } - -} 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..509b270e921 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,42 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { assertEquals(URL_MY_CODE_SYSTEM, result.getExpansion().getContains().get(idx).getSystem()); } + @Test + public void testIndexingIsDeferredForLargeCodeSystems() { + myDaoConfig.setDeferIndexingForCodesystemsOfSize(1); + + myTermSvc.setProcessDeferred(false); + + 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); + + assertEquals(0, result.getExpansion().getContains().size()); + + myTermSvc.setProcessDeferred(true); + myTermSvc.saveDeferred(); + myTermSvc.saveDeferred(); + myTermSvc.saveDeferred(); + myTermSvc.saveDeferred(); + myTermSvc.saveDeferred(); + myTermSvc.saveDeferred(); + myTermSvc.saveDeferred(); + + result = myValueSetDao.expand(vs, null); + encoded = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result); + ourLog.info(encoded); + assertEquals(4, result.getExpansion().getContains().size()); + + } + @Test public void testExpandWithExcludeInExternalValueSet() { createExternalCsAndLocalVs(); @@ -486,6 +523,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-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java index ba7d09e430f..01d6dbfcb9b 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java @@ -185,22 +185,22 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test { List idValues; idValues = searchAndReturnUnqualifiedIdValues(ourServerBase + "/Patient/" + id.getIdPart() + "/_history?_at=gt" + toStr(preDates.get(0)) + "&_at=lt" + toStr(preDates.get(3))); - assertThat(idValues, contains(ids.get(2), ids.get(1), ids.get(0))); + assertThat(idValues.toString(), idValues, contains(ids.get(2), ids.get(1), ids.get(0))); idValues = searchAndReturnUnqualifiedIdValues(ourServerBase + "/Patient/_history?_at=gt" + toStr(preDates.get(0)) + "&_at=lt" + toStr(preDates.get(3))); - assertThat(idValues, contains(ids.get(2), ids.get(1), ids.get(0))); + assertThat(idValues.toString(), idValues, contains(ids.get(2), ids.get(1), ids.get(0))); idValues = searchAndReturnUnqualifiedIdValues(ourServerBase + "/_history?_at=gt" + toStr(preDates.get(0)) + "&_at=lt" + toStr(preDates.get(3))); - assertThat(idValues, contains(ids.get(2), ids.get(1), ids.get(0))); + assertThat(idValues.toString(), idValues, contains(ids.get(2), ids.get(1), ids.get(0))); idValues = searchAndReturnUnqualifiedIdValues(ourServerBase + "/_history?_at=gt2060"); - assertThat(idValues, empty()); + assertThat(idValues.toString(), idValues, empty()); idValues = searchAndReturnUnqualifiedIdValues(ourServerBase + "/_history?_at=" + InstantDt.withCurrentTime().getYear()); - assertThat(idValues, hasSize(10)); // 10 is the page size + assertThat(idValues.toString(), idValues, hasSize(10)); // 10 is the page size idValues = searchAndReturnUnqualifiedIdValues(ourServerBase + "/_history?_at=ge" + InstantDt.withCurrentTime().getYear()); - assertThat(idValues, hasSize(10)); + assertThat(idValues.toString(), idValues, hasSize(10)); idValues = searchAndReturnUnqualifiedIdValues(ourServerBase + "/_history?_at=gt" + InstantDt.withCurrentTime().getYear()); assertThat(idValues, hasSize(0)); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcIntegrationTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcIntegrationTest.java index 4b6bfaab01c..5d1e08af0db 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcIntegrationTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcIntegrationTest.java @@ -34,7 +34,7 @@ public class TerminologyLoaderSvcIntegrationTest extends BaseJpaDstu3Test { files.put(TerminologyLoaderSvc.SCT_FILE_CONCEPT, new File("/Users/james/tmp/sct/SnomedCT_Release_INT_20160131_Full/Terminology/sct2_Concept_Full_INT_20160131.txt")); files.put(TerminologyLoaderSvc.SCT_FILE_DESCRIPTION, new File("/Users/james/tmp/sct/SnomedCT_Release_INT_20160131_Full/Terminology/sct2_Description_Full-en_INT_20160131.txt")); files.put(TerminologyLoaderSvc.SCT_FILE_RELATIONSHIP, new File("/Users/james/tmp/sct/SnomedCT_Release_INT_20160131_Full/Terminology/sct2_Relationship_Full_INT_20160131.txt")); - myLoader.processSnomedCtFiles(files, mySrd); +// myLoader.processSnomedCtFiles(files, mySrd); } } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcTest.java index 668c10c185d..f8f9f9b1f3c 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcTest.java @@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.term; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInRelativeOrder; +import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.not; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; @@ -12,8 +13,10 @@ import static org.mockito.Mockito.verify; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.List; import java.util.TreeSet; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; @@ -40,19 +43,19 @@ import ca.uhn.fhir.util.TestUtil; public class TerminologyLoaderSvcTest { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TerminologyLoaderSvcTest.class); private TerminologyLoaderSvc mySvc; - + @Mock private IHapiTerminologySvc myTermSvc; @Captor private ArgumentCaptor myCsvCaptor; - + @Before public void before() { mySvc = new TerminologyLoaderSvc(); mySvc.setTermSvcForUnitTests(myTermSvc); } - + @AfterClass public static void afterClassClearContext() { TestUtil.clearAllStaticFieldsForUnitTest(); @@ -62,18 +65,18 @@ public class TerminologyLoaderSvcTest { public void testLoadLoinc() throws Exception { ByteArrayOutputStream bos1 = new ByteArrayOutputStream(); ZipOutputStream zos1 = new ZipOutputStream(bos1); - addEntry(zos1,"/loinc/", "loinc.csv"); + addEntry(zos1, "/loinc/", "loinc.csv"); zos1.close(); ourLog.info("ZIP file has {} bytes", bos1.toByteArray().length); - + ByteArrayOutputStream bos2 = new ByteArrayOutputStream(); ZipOutputStream zos2 = new ZipOutputStream(bos2); - addEntry(zos2,"/loinc/", "LOINC_2.54_MULTI-AXIAL_HIERARCHY.CSV"); + addEntry(zos2, "/loinc/", "LOINC_2.54_MULTI-AXIAL_HIERARCHY.CSV"); zos2.close(); ourLog.info("ZIP file has {} bytes", bos2.toByteArray().length); - + RequestDetails details = mock(RequestDetails.class); - mySvc.loadLoinc(Arrays.asList(bos1.toByteArray(), bos2.toByteArray()), details); + mySvc.loadLoinc(list(bos1.toByteArray(), bos2.toByteArray()), details); } @Test @@ -84,38 +87,48 @@ public class TerminologyLoaderSvcTest { addEntry(zos, "/sct/", "sct2_Concept_Full-en_INT_20160131.txt"); addEntry(zos, "/sct/", "sct2_Description_Full-en_INT_20160131.txt"); addEntry(zos, "/sct/", "sct2_Identifier_Full_INT_20160131.txt"); - addEntry(zos,"/sct/", "sct2_Relationship_Full_INT_20160131.txt"); - addEntry(zos,"/sct/", "sct2_StatedRelationship_Full_INT_20160131.txt"); + addEntry(zos, "/sct/", "sct2_Relationship_Full_INT_20160131.txt"); + addEntry(zos, "/sct/", "sct2_StatedRelationship_Full_INT_20160131.txt"); addEntry(zos, "/sct/", "sct2_TextDefinition_Full-en_INT_20160131.txt"); zos.close(); - + ourLog.info("ZIP file has {} bytes", bos.toByteArray().length); - + RequestDetails details = mock(RequestDetails.class); - mySvc.loadSnomedCt(Collections.singletonList(bos.toByteArray()), details); - + mySvc.loadSnomedCt(list(bos.toByteArray()), details); + verify(myTermSvc).storeNewCodeSystemVersion(any(String.class), myCsvCaptor.capture(), any(RequestDetails.class)); - + TermCodeSystemVersion csv = myCsvCaptor.getValue(); - TreeSet allCodes = toCodes(csv); + TreeSet allCodes = toCodes(csv, true); ourLog.info(allCodes.toString()); - + assertThat(allCodes, containsInRelativeOrder("116680003")); assertThat(allCodes, not(containsInRelativeOrder("207527008"))); + + allCodes = toCodes(csv, false); + ourLog.info(allCodes.toString()); + assertThat(allCodes, hasItem("126816002")); } - private TreeSet toCodes(TermCodeSystemVersion theCsv) { + private List list(byte[]... theByteArray) { + return new ArrayList(Arrays.asList(theByteArray)); + } + + private TreeSet toCodes(TermCodeSystemVersion theCsv, boolean theAddChildren) { TreeSet retVal = new TreeSet(); for (TermConcept next : theCsv.getConcepts()) { - toCodes(retVal, next); + toCodes(retVal, next, theAddChildren); } return retVal; } - private void toCodes(TreeSet theCodes, TermConcept theConcept) { + private void toCodes(TreeSet theCodes, TermConcept theConcept, boolean theAddChildren) { theCodes.add(theConcept.getCode()); - for (TermConceptParentChildLink next : theConcept.getChildren()) { - toCodes(theCodes, next.getChild()); + if (theAddChildren) { + for (TermConceptParentChildLink next : theConcept.getChildren()) { + toCodes(theCodes, next.getChild(), theAddChildren); + } } } @@ -125,9 +138,9 @@ public class TerminologyLoaderSvcTest { ZipOutputStream zos = new ZipOutputStream(bos); addEntry(zos, "/sct/", "sct2_StatedRelationship_Full_INT_20160131.txt"); zos.close(); - + ourLog.info("ZIP file has {} bytes", bos.toByteArray().length); - + RequestDetails details = mock(RequestDetails.class); try { mySvc.loadSnomedCt(Collections.singletonList(bos.toByteArray()), details); @@ -145,6 +158,5 @@ public class TerminologyLoaderSvcTest { zos.write(byteArray); zos.closeEntry(); } - } diff --git a/hapi-fhir-jpaserver-base/src/test/resources/sct/sct2_Concept_Full_INT_20160131.txt b/hapi-fhir-jpaserver-base/src/test/resources/sct/sct2_Concept_Full_INT_20160131.txt index 83d9d806fc6..43be7c43d81 100644 --- a/hapi-fhir-jpaserver-base/src/test/resources/sct/sct2_Concept_Full_INT_20160131.txt +++ b/hapi-fhir-jpaserver-base/src/test/resources/sct/sct2_Concept_Full_INT_20160131.txt @@ -13,6 +13,7 @@ id effectiveTime active moduleId definitionStatusId 126813005 20020131 1 900000000000207008 900000000000074008 126813006 20020131 1 900000000000207008 900000000000074008 126817006 20020131 1 900000000000207008 900000000000074008 +126816002 20020131 1 900000000000207008 900000000000074008 207527008 20020131 1 900000000000207008 900000000000074008 207527008 20040731 1 900000000000207008 900000000000073002 207527008 20090731 0 900000000000207008 900000000000074008 diff --git a/hapi-fhir-jpaserver-base/src/test/resources/sct/sct2_Relationship_Full_INT_20160131.txt b/hapi-fhir-jpaserver-base/src/test/resources/sct/sct2_Relationship_Full_INT_20160131.txt index 98bb0b2d991..7132f3dbfe5 100644 --- a/hapi-fhir-jpaserver-base/src/test/resources/sct/sct2_Relationship_Full_INT_20160131.txt +++ b/hapi-fhir-jpaserver-base/src/test/resources/sct/sct2_Relationship_Full_INT_20160131.txt @@ -1,5 +1,6 @@ id effectiveTime active moduleId sourceId destinationId relationshipGroup typeId characteristicTypeId modifierId 100022 20020131 1 900000000000207008 126815003 126813005 0 116680003 900000000000011006 900000000000451002 -100022 20090731 0 900000000000207008 126816002 126813005 0 116680003 900000000000011006 900000000000451002 +100025 20020131 1 900000000000207008 126816002 126813005 0 116680003 900000000000011006 900000000000451002 +100025 20090731 0 900000000000207008 126816002 126813005 0 116680003 900000000000011006 900000000000451002 101021 20020131 1 900000000000207008 126817006 126815003 0 116680003 900000000000011006 900000000000451002 101021 20020131 1 900000000000207008 126815003 126817006 0 116680003 900000000000011006 900000000000451002 diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/audit/IResourceAuditor.java b/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/rest/server/audit/IResourceAuditor.java similarity index 100% rename from hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/audit/IResourceAuditor.java rename to hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/rest/server/audit/IResourceAuditor.java diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java index d56a5084f38..f7be2650868 100644 --- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java @@ -44,6 +44,7 @@ import ca.uhn.fhir.model.dstu.composite.HumanNameDt; import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt; import ca.uhn.fhir.model.dstu.resource.AllergyIntolerance; import ca.uhn.fhir.model.dstu.resource.Binary; +import ca.uhn.fhir.model.dstu.resource.CarePlan; import ca.uhn.fhir.model.dstu.resource.Composition; import ca.uhn.fhir.model.dstu.resource.Condition; import ca.uhn.fhir.model.dstu.resource.Conformance; @@ -67,6 +68,7 @@ import ca.uhn.fhir.model.primitive.DateDt; import ca.uhn.fhir.model.primitive.DateTimeDt; import ca.uhn.fhir.model.primitive.DecimalDt; import ca.uhn.fhir.model.primitive.IdDt; +import ca.uhn.fhir.model.primitive.IdrefDt; import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator; @@ -95,6 +97,21 @@ public class XmlParserTest { } + @Test + public void testEncodeAndParseIdref() { + CarePlan cp = new CarePlan(); + cp.addGoal().setNotes("Goal Notes").setElementSpecificId("goalId0"); + + IdrefDt idref = new IdrefDt(); + idref.setValue("#goalId0"); + idref.setTarget(cp); + idref.getTarget(); + cp.addActivity().getGoal().add(idref); + + String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(cp); + ourLog.info(encoded); + } + /** * Test for #82 - Not yet enabled because the test won't pass */ diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/ClientServerValidationTestDstu.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/ClientServerValidationDstu1Test.java similarity index 99% rename from hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/ClientServerValidationTestDstu.java rename to hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/ClientServerValidationDstu1Test.java index 78ca4feb8bc..c4df0dcf03f 100644 --- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/ClientServerValidationTestDstu.java +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/ClientServerValidationDstu1Test.java @@ -32,7 +32,7 @@ import ca.uhn.fhir.rest.client.exceptions.FhirClientInappropriateForServerExcept import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.util.TestUtil; -public class ClientServerValidationTestDstu { +public class ClientServerValidationDstu1Test { private FhirContext myCtx; private HttpClient myHttpClient; diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/GenericClientTest.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/GenericClientTest.java index 5626c7ea13d..966a608a569 100644 --- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/GenericClientTest.java +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/GenericClientTest.java @@ -15,7 +15,9 @@ import java.io.InputStream; import java.io.StringReader; import java.net.URLEncoder; import java.nio.charset.Charset; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import org.apache.commons.io.IOUtils; import org.apache.commons.io.input.ReaderInputStream; @@ -33,6 +35,7 @@ import org.apache.http.message.BasicHeader; import org.apache.http.message.BasicStatusLine; import org.hamcrest.Matchers; import org.hamcrest.core.StringContains; +import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; @@ -82,6 +85,8 @@ public class GenericClientTest { ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); myHttpResponse = mock(HttpResponse.class, new ReturnsDeepStubs()); + + System.setProperty(BaseClient.HAPI_CLIENT_KEEPRESPONSES, "true"); } private String extractBody(ArgumentCaptor capt, int count) throws IOException { @@ -89,6 +94,24 @@ public class GenericClientTest { return body; } + @Test + public void testInvalidCalls() { + IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); + + try { + client.meta(); + fail(); + } catch (IllegalStateException e) { + assertEquals("Can not call $meta operations on a DSTU1 client", e.getMessage()); + } + try { + client.operation(); + fail(); + } catch (IllegalStateException e) { + assertEquals("Operations are only supported in FHIR DSTU2 and later. This client was created using a context configured for DSTU1", e.getMessage()); + } + } + private String getPatientFeedWithOneResult() { //@formatter:off String msg = "\n" + @@ -155,6 +178,9 @@ public class GenericClientTest { resp = client.create().resource(ourCtx.newXmlParser().encodeResourceToString(p1)).execute(); assertNull(resp.getCreated()); + ourLog.info("lastRequest: {}", ((GenericClient)client).getLastRequest()); + ourLog.info("lastResponse: {}", ((GenericClient)client).getLastResponse()); + ourLog.info("lastResponseBody: {}", ((GenericClient)client).getLastResponseBody()); } @Test @@ -1471,6 +1497,25 @@ public class GenericClientTest { } + @SuppressWarnings("deprecation") + @Test + public void testTransactionOldStyle() throws Exception { + String bundleStr = IOUtils.toString(getClass().getResourceAsStream("/bundle.json")); + Bundle bundle = ourCtx.newJsonParser().parseBundle(bundleStr); + + 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()).thenReturn(new ReaderInputStream(new StringReader(bundleStr), Charset.forName("UTF-8"))); + + IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); + + List input = new ArrayList(); + input.addAll(bundle.toListOfResources()); + client.transaction(input); + } + @Test public void testTransactionJson() throws Exception { String bundleStr = IOUtils.toString(getClass().getResourceAsStream("/bundle.json")); diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/exceptions/ExceptionPropertiesTest.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/exceptions/ExceptionPropertiesTest.java index b4dab407480..3154aa79e8f 100644 --- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/exceptions/ExceptionPropertiesTest.java +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/exceptions/ExceptionPropertiesTest.java @@ -10,11 +10,25 @@ import com.google.common.collect.ImmutableSet; import com.google.common.reflect.ClassPath; import com.google.common.reflect.ClassPath.ClassInfo; +import ca.uhn.fhir.model.dstu.resource.OperationOutcome; +import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException; +import ca.uhn.fhir.rest.client.exceptions.FhirClientInappropriateForServerException; import ca.uhn.fhir.util.TestUtil; public class ExceptionPropertiesTest { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExceptionPropertiesTest.class); + @Test + public void testConstructors() { + new FhirClientConnectionException(""); + new FhirClientConnectionException("", new Exception()); + new FhirClientConnectionException(new Exception()); + new NotImplementedOperationException(""); + new NotImplementedOperationException(null, new OperationOutcome()); + new FhirClientInappropriateForServerException(new Exception()); + new FhirClientInappropriateForServerException("", new Exception()); + } + @SuppressWarnings("deprecation") @Test public void testExceptionsAreGood() throws Exception { diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/ctx/FhirContextDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/ctx/FhirContextDstu2Test.java index 41a2ce65721..a5032c6a712 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/ctx/FhirContextDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/ctx/FhirContextDstu2Test.java @@ -11,6 +11,7 @@ import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.model.dstu2.valueset.AdministrativeGenderEnum; import ca.uhn.fhir.model.dstu2.valueset.MaritalStatusCodesEnum; +import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.util.TestUtil; public class FhirContextDstu2Test { @@ -23,6 +24,12 @@ public class FhirContextDstu2Test { TestUtil.clearAllStaticFieldsForUnitTest(); } + @Test(expected=DataFormatException.class) + public void testScanInvalid() { + FhirContext ctx = FhirContext.forDstu2(); + ctx.getResourceDefinition("InvalidResource"); + } + @Test public void testQueryBoundCode() { diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/model/primitive/TimeDtTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/model/primitive/TimeDtTest.java new file mode 100644 index 00000000000..06383c56f74 --- /dev/null +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/model/primitive/TimeDtTest.java @@ -0,0 +1,15 @@ +package ca.uhn.fhir.model.primitive; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class TimeDtTest { + + @Test + public void testEncode() { + TimeDt dt = new TimeDt("11:33:01.123"); + assertEquals("11:33:01.123", dt.getValue()); + } + +} diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGeneratorTestDstu2.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGeneratorDstu2Test.java similarity index 99% rename from hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGeneratorTestDstu2.java rename to hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGeneratorDstu2Test.java index efc92ec448e..5cd631ee8aa 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGeneratorTestDstu2.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGeneratorDstu2Test.java @@ -37,9 +37,9 @@ import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.util.TestUtil; -public class DefaultThymeleafNarrativeGeneratorTestDstu2 { +public class DefaultThymeleafNarrativeGeneratorDstu2Test { private static FhirContext ourCtx = FhirContext.forDstu2(); - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DefaultThymeleafNarrativeGeneratorTestDstu2.class); + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DefaultThymeleafNarrativeGeneratorDstu2Test.class); private DefaultThymeleafNarrativeGenerator myGen; @AfterClass diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/ClientServerValidationTestDstu2.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/ClientServerValidationDstu2Test.java similarity index 97% rename from hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/ClientServerValidationTestDstu2.java rename to hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/ClientServerValidationDstu2Test.java index 15dd008f31d..89a00e31bcd 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/ClientServerValidationTestDstu2.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/ClientServerValidationDstu2Test.java @@ -1,7 +1,12 @@ package ca.uhn.fhir.rest.client; import static org.hamcrest.Matchers.containsString; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -32,16 +37,15 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.dstu2.resource.Conformance; import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.model.primitive.UriDt; -import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException; import ca.uhn.fhir.rest.client.exceptions.FhirClientInappropriateForServerException; import ca.uhn.fhir.rest.client.interceptor.BasicAuthInterceptor; import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; import ca.uhn.fhir.util.TestUtil; -public class ClientServerValidationTestDstu2 { +public class ClientServerValidationDstu2Test { - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ClientServerValidationTestDstu2.class); + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ClientServerValidationDstu2Test.class); private FhirContext myCtx; private boolean myFirstResponse; private HttpClient myHttpClient; 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 4f96f461fd6..e8bca87683d 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 @@ -19,6 +19,7 @@ import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; +import java.util.HashMap; import java.util.List; import org.apache.commons.io.IOUtils; @@ -45,18 +46,20 @@ import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; +import ca.uhn.fhir.context.ConfigurationException; 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.IQueryParameterType; import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; import ca.uhn.fhir.model.dstu2.composite.IdentifierDt; import ca.uhn.fhir.model.dstu2.composite.MetaDt; import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry; import ca.uhn.fhir.model.dstu2.resource.Bundle.Link; +import ca.uhn.fhir.model.dstu2.resource.Conformance; 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; @@ -68,15 +71,19 @@ 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.model.primitive.UriDt; +import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.parser.XmlParserDstu2Test; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.PreferReturnEnum; import ca.uhn.fhir.rest.api.SummaryEnum; import ca.uhn.fhir.rest.client.apache.ApacheRestfulClientFactory; +import ca.uhn.fhir.rest.client.api.IHttpClient; +import ca.uhn.fhir.rest.client.api.IRestfulClient; import ca.uhn.fhir.rest.client.exceptions.InvalidResponseException; import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; import ca.uhn.fhir.rest.method.SearchStyleEnum; +import ca.uhn.fhir.rest.param.DateParam; import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.EncodingEnum; @@ -84,28 +91,29 @@ import ca.uhn.fhir.util.TestUtil; public class GenericClientDstu2Test { private static FhirContext ourCtx; - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(GenericClientDstu2Test.class); + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(GenericClientDstu2Test.class); private HttpClient myHttpClient; private HttpResponse myHttpResponse; private int myResponseCount = 0; - @AfterClass - public static void afterClassClearContext() { - TestUtil.clearAllStaticFieldsForUnitTest(); - } - - @Before public void before() { myHttpClient = mock(HttpClient.class, new ReturnsDeepStubs()); ourCtx.setRestfulClientFactory(new ApacheRestfulClientFactory(ourCtx)); + ourCtx.getRestfulClientFactory().setConnectionRequestTimeout(10000); + ourCtx.getRestfulClientFactory().setConnectTimeout(10000); + ourCtx.getRestfulClientFactory().setPoolMaxPerRoute(100); + ourCtx.getRestfulClientFactory().setPoolMaxTotal(100); + ourCtx.getRestfulClientFactory().setHttpClient(myHttpClient); ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); myHttpResponse = mock(HttpResponse.class, new ReturnsDeepStubs()); myResponseCount = 0; + + System.setProperty(BaseClient.HAPI_CLIENT_KEEPRESPONSES, "true"); } private String extractBody(ArgumentCaptor capt, int count) throws IOException { @@ -113,7 +121,6 @@ public class GenericClientDstu2Test { return body; } - private String getPatientFeedWithOneResult() { //@formatter:off String msg = "\n" + @@ -134,7 +141,7 @@ public class GenericClientDstu2Test { //@formatter:on return msg; } - + @Test public void testAcceptHeaderFetchConformance() throws Exception { IParser p = ourCtx.newXmlParser(); @@ -177,70 +184,6 @@ public class GenericClientDstu2Test { idx++; } - /** - * See #322 - */ - @Test - public void testFetchConformanceWithSmartExtensions() throws Exception { - final String respString = IOUtils.toString(GenericClientDstu2Test.class.getResourceAsStream("/conformance_322.json")); - 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()).thenAnswer(new Answer() { - @Override - public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable { - return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8")); - } - }); - - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:8080/fhir"); - Conformance conf = client.fetchConformance().ofType(Conformance.class).execute(); - - Rest rest = conf.getRest().get(0); - RestSecurity security = rest.getSecurity(); - - List ext = security.getUndeclaredExtensionsByUrl("http://fhir-registry.smarthealthit.org/StructureDefinition/oauth-uris"); - List tokenExts = ext.get(0).getUndeclaredExtensionsByUrl("token"); - ExtensionDt tokenExt = tokenExts.get(0); - UriDt value = (UriDt) tokenExt.getValue(); - assertEquals("https://my-server.org/token", value.getValueAsString()); - - } - - - - /** - * See #322 - */ - @Test - public void testFetchConformanceWithSmartExtensionsAltCase() throws Exception { - final String respString = IOUtils.toString(GenericClientDstu2Test.class.getResourceAsStream("/conformance_322.json")).replace("valueuri", "valueUri"); - 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()).thenAnswer(new Answer() { - @Override - public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable { - return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8")); - } - }); - - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:8080/fhir"); - Conformance conf = client.fetchConformance().ofType(Conformance.class).execute(); - - Rest rest = conf.getRest().get(0); - RestSecurity security = rest.getSecurity(); - - List ext = security.getUndeclaredExtensionsByUrl("http://fhir-registry.smarthealthit.org/StructureDefinition/oauth-uris"); - List tokenExts = ext.get(0).getUndeclaredExtensionsByUrl("token"); - ExtensionDt tokenExt = tokenExts.get(0); - UriDt value = (UriDt) tokenExt.getValue(); - assertEquals("https://my-server.org/token", value.getValueAsString()); - - } - @Test public void testAcceptHeaderPreflightConformance() throws Exception { String methodName = "testAcceptHeaderPreflightConformance"; @@ -540,6 +483,30 @@ public class GenericClientDstu2Test { assertEquals("Patient/123", output.getResource().getIdElement().toUnqualifiedVersionless().getValue()); } + @Test + public void testDeleteByResource() throws Exception { + ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class); + when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse); + when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), Constants.STATUS_HTTP_204_NO_CONTENT, "")); + when(myHttpResponse.getEntity().getContent()).then(new Answer() { + @Override + public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable { + return new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8")); + } + }); + + IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); + + int idx = 0; + + Patient pat = new Patient(); + pat.setId("Patient/123"); + + client.delete().resource(pat).execute(); + assertEquals("DELETE", capt.getAllValues().get(idx).getMethod()); + assertEquals("http://example.com/fhir/Patient/123", capt.getAllValues().get(idx).getURI().toString()); + } + @Test public void testDeleteConditional() throws Exception { ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class); @@ -575,6 +542,49 @@ public class GenericClientDstu2Test { } + @Test + public void testDeleteInvalidRequest() throws Exception { + Patient pat = new Patient(); + pat.setId("123"); + + IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); + + try { + client.delete().resource(pat).execute(); + fail(); + } catch (IllegalArgumentException e){ + assertEquals("theResource.getId() must contain a resource type and logical ID at a minimum (e.g. Patient/1234), found: 123", e.getMessage()); + } + + try { + client.delete().resourceById(new IdDt("123")).execute(); + fail(); + } catch (IllegalArgumentException e){ + assertEquals("theId must contain a resource type and logical ID at a minimum (e.g. Patient/1234)found: 123", e.getMessage()); + } + + try { + client.delete().resourceById("", "123").execute(); + fail(); + } catch (IllegalArgumentException e){ + assertEquals("theResourceType can not be blank/null", e.getMessage()); + } + + try { + client.delete().resourceById("Patient", "").execute(); + fail(); + } catch (IllegalArgumentException e){ + assertEquals("theLogicalId can not be blank/null", e.getMessage()); + } + + try { + client.delete().resourceConditionalByType("InvalidType"); + fail(); + } catch (DataFormatException e){ + assertEquals("Unknown resource name \"InvalidType\" (this name is not known in FHIR version \"DSTU2\")", e.getMessage()); + } + } + @SuppressWarnings("deprecation") @Test public void testDeleteNonFluent() throws Exception { @@ -604,6 +614,68 @@ public class GenericClientDstu2Test { } + /** + * See #322 + */ + @Test + public void testFetchConformanceWithSmartExtensions() throws Exception { + final String respString = IOUtils.toString(GenericClientDstu2Test.class.getResourceAsStream("/conformance_322.json")); + 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()).thenAnswer(new Answer() { + @Override + public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable { + return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8")); + } + }); + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:8080/fhir"); + Conformance conf = client.fetchConformance().ofType(Conformance.class).execute(); + + Rest rest = conf.getRest().get(0); + RestSecurity security = rest.getSecurity(); + + List ext = security.getUndeclaredExtensionsByUrl("http://fhir-registry.smarthealthit.org/StructureDefinition/oauth-uris"); + List tokenExts = ext.get(0).getUndeclaredExtensionsByUrl("token"); + ExtensionDt tokenExt = tokenExts.get(0); + UriDt value = (UriDt) tokenExt.getValue(); + assertEquals("https://my-server.org/token", value.getValueAsString()); + + } + + /** + * See #322 + */ + @Test + public void testFetchConformanceWithSmartExtensionsAltCase() throws Exception { + final String respString = IOUtils.toString(GenericClientDstu2Test.class.getResourceAsStream("/conformance_322.json")).replace("valueuri", "valueUri"); + 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()).thenAnswer(new Answer() { + @Override + public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable { + return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8")); + } + }); + + IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:8080/fhir"); + Conformance conf = client.fetchConformance().ofType(Conformance.class).execute(); + + Rest rest = conf.getRest().get(0); + RestSecurity security = rest.getSecurity(); + + List ext = security.getUndeclaredExtensionsByUrl("http://fhir-registry.smarthealthit.org/StructureDefinition/oauth-uris"); + List tokenExts = ext.get(0).getUndeclaredExtensionsByUrl("token"); + ExtensionDt tokenExt = tokenExts.get(0); + UriDt value = (UriDt) tokenExt.getValue(); + assertEquals("https://my-server.org/token", value.getValueAsString()); + + } + @Test public void testHistory() throws Exception { @@ -709,6 +781,16 @@ public class GenericClientDstu2Test { idx++; } + @Test + public void testInvalidClient() { + try { + ourCtx.getRestfulClientFactory().newClient(RestfulClientInstance.class, "http://foo"); + fail(); + } catch (ConfigurationException e) { + assertEquals("ca.uhn.fhir.context.ConfigurationException: ca.uhn.fhir.rest.client.GenericClientDstu2Test.RestfulClientInstance is not an interface", e.toString()); + } + } + @Test public void testMetaAdd() throws Exception { IParser p = ourCtx.newXmlParser(); @@ -1504,10 +1586,10 @@ public class GenericClientDstu2Test { } @Test - public void testProviderWhereWeForgotToSetTheContext() throws Exception { + public void testProviderWhereWeForgotToSetTheContext() throws Exception { ApacheRestfulClientFactory clientFactory = new ApacheRestfulClientFactory(); // no ctx clientFactory.setServerValidationMode(ServerValidationModeEnum.NEVER); - + ourCtx.setRestfulClientFactory(clientFactory); try { @@ -1576,6 +1658,24 @@ public class GenericClientDstu2Test { assertEquals("FAM", response.getName().get(0).getFamily().get(0).getValue()); } + @Test + public void testReadForUnknownType() throws Exception { + IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); + try { + client.read(new UriDt("1")); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("The given URI is not an absolute URL and is not usable for this operation: 1", e.getMessage()); + } + + try { + client.read(new UriDt("http://example.com/InvalidResource/1")); + fail(); + } catch (DataFormatException e) { + assertEquals("Unknown resource name \"InvalidResource\" (this name is not known in FHIR version \"DSTU2\")", e.getMessage()); + } + } + @Test public void testReadUpdatedHeaderDoesntOverwriteResourceValue() throws Exception { @@ -1699,112 +1799,6 @@ public class GenericClientDstu2Test { } - @Test - public void testSearchByPost() throws Exception { - 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()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"))); - - IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); - - //@formatter:off - Bundle response = client.search() - .forResource("Patient") - .where(Patient.NAME.matches().value("james")) - .elementsSubset("name", "identifier") - .usingStyle(SearchStyleEnum.POST) - .execute(); - //@formatter:on - - assertEquals("http://example.com/fhir/Patient/_search?_elements=identifier%2Cname", capt.getValue().getURI().toString()); - - // assertThat(capt.getValue().getURI().toString(), - // either(equalTo("http://example.com/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo("http://example.com/fhir/Patient?name=james&_elements=identifier%2Cname"))); - - assertEquals(Patient.class, response.getEntries().get(0).getResource().getClass()); - - ourLog.info(Arrays.asList(capt.getValue().getAllHeaders()).toString()); - ourLog.info(capt.getValue().toString()); - - HttpEntityEnclosingRequestBase v = (HttpEntityEnclosingRequestBase) capt.getValue(); - String req = IOUtils.toString(v.getEntity().getContent(), "UTF-8"); - assertEquals("name=james", req); - - assertEquals("application/x-www-form-urlencoded;charset=utf-8", v.getEntity().getContentType().getValue().replace(" ", "").toLowerCase()); - assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON, capt.getValue().getFirstHeader("accept").getValue()); - assertThat(capt.getValue().getFirstHeader("user-agent").getValue(), not(emptyString())); - } - - @Test - public void testSearchByPostUseJson() throws Exception { - 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()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"))); - - IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); - - //@formatter:off - Bundle response = client.search() - .forResource("Patient") - .where(Patient.NAME.matches().value("james")) - .elementsSubset("name", "identifier") - .usingStyle(SearchStyleEnum.POST) - .encodedJson() - .execute(); - //@formatter:on - - assertThat(capt.getValue().getURI().toString(), containsString("http://example.com/fhir/Patient/_search?")); - assertThat(capt.getValue().getURI().toString(), containsString("_elements=identifier%2Cname")); - assertThat(capt.getValue().getURI().toString(), containsString("_format=json")); - - // assertThat(capt.getValue().getURI().toString(), - // either(equalTo("http://example.com/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo("http://example.com/fhir/Patient?name=james&_elements=identifier%2Cname"))); - - assertEquals(Patient.class, response.getEntries().get(0).getResource().getClass()); - - ourLog.info(Arrays.asList(capt.getValue().getAllHeaders()).toString()); - ourLog.info(capt.getValue().toString()); - - HttpEntityEnclosingRequestBase v = (HttpEntityEnclosingRequestBase) capt.getValue(); - String req = IOUtils.toString(v.getEntity().getContent(), "UTF-8"); - assertEquals("name=james", req); - - assertEquals("application/x-www-form-urlencoded;charset=utf-8", v.getEntity().getContentType().getValue().replace(" ", "").toLowerCase()); - assertEquals(Constants.CT_FHIR_JSON, capt.getValue().getFirstHeader("accept").getValue()); - } - - @Test - public void testSearchByString() throws Exception { - 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()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"))); - - IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); - - //@formatter:off - Bundle response = client.search() - .forResource("Patient") - .where(Patient.NAME.matches().value("james")) - .execute(); - //@formatter:on - - assertEquals("http://example.com/fhir/Patient?name=james", capt.getValue().getURI().toString()); - assertEquals(Patient.class, response.getEntries().get(0).getResource().getClass()); - - } - @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\"}]}}]}"; @@ -1817,11 +1811,12 @@ public class GenericClientDstu2Test { @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") @@ -1874,6 +1869,112 @@ public class GenericClientDstu2Test { } + @Test + public void testSearchByPost() throws Exception { + 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()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"))); + + IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); + + //@formatter:off + Bundle response = client.search() + .forResource("Patient") + .where(Patient.NAME.matches().value("james")) + .elementsSubset("name", "identifier") + .usingStyle(SearchStyleEnum.POST) + .execute(); + //@formatter:on + + assertEquals("http://example.com/fhir/Patient/_search?_elements=identifier%2Cname", capt.getValue().getURI().toString()); + + // assertThat(capt.getValue().getURI().toString(), + // either(equalTo("http://example.com/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo("http://example.com/fhir/Patient?name=james&_elements=identifier%2Cname"))); + + assertEquals(Patient.class, response.getEntries().get(0).getResource().getClass()); + + ourLog.info(Arrays.asList(capt.getValue().getAllHeaders()).toString()); + ourLog.info(capt.getValue().toString()); + + HttpEntityEnclosingRequestBase v = (HttpEntityEnclosingRequestBase) capt.getValue(); + String req = IOUtils.toString(v.getEntity().getContent(), "UTF-8"); + assertEquals("name=james", req); + + assertEquals("application/x-www-form-urlencoded;charset=utf-8", v.getEntity().getContentType().getValue().replace(" ", "").toLowerCase()); + assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON, capt.getValue().getFirstHeader("accept").getValue()); + assertThat(capt.getValue().getFirstHeader("user-agent").getValue(), not(emptyString())); + } + + @Test + public void testSearchByPostUseJson() throws Exception { + 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()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"))); + + IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); + + //@formatter:off + Bundle response = client.search() + .forResource("Patient") + .where(Patient.NAME.matches().value("james")) + .elementsSubset("name", "identifier") + .usingStyle(SearchStyleEnum.POST) + .encodedJson() + .execute(); + //@formatter:on + + assertThat(capt.getValue().getURI().toString(), containsString("http://example.com/fhir/Patient/_search?")); + assertThat(capt.getValue().getURI().toString(), containsString("_elements=identifier%2Cname")); + assertThat(capt.getValue().getURI().toString(), containsString("_format=json")); + + // assertThat(capt.getValue().getURI().toString(), + // either(equalTo("http://example.com/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo("http://example.com/fhir/Patient?name=james&_elements=identifier%2Cname"))); + + assertEquals(Patient.class, response.getEntries().get(0).getResource().getClass()); + + ourLog.info(Arrays.asList(capt.getValue().getAllHeaders()).toString()); + ourLog.info(capt.getValue().toString()); + + HttpEntityEnclosingRequestBase v = (HttpEntityEnclosingRequestBase) capt.getValue(); + String req = IOUtils.toString(v.getEntity().getContent(), "UTF-8"); + assertEquals("name=james", req); + + assertEquals("application/x-www-form-urlencoded;charset=utf-8", v.getEntity().getContentType().getValue().replace(" ", "").toLowerCase()); + assertEquals(Constants.CT_FHIR_JSON, capt.getValue().getFirstHeader("accept").getValue()); + } + + @Test + public void testSearchByString() throws Exception { + 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()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"))); + + IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); + + //@formatter:off + Bundle response = client.search() + .forResource("Patient") + .where(Patient.NAME.matches().value("james")) + .execute(); + //@formatter:on + + assertEquals("http://example.com/fhir/Patient?name=james", capt.getValue().getURI().toString()); + assertEquals(Patient.class, response.getEntries().get(0).getResource().getClass()); + + } + @Test public void testSearchByUrl() throws Exception { @@ -2035,6 +2136,27 @@ public class GenericClientDstu2Test { } + @Test + public void testSearchWithMap() throws Exception { + 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()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"))); + + IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); + + HashMap> params = new HashMap>(); + params.put("foo", Arrays.asList((IQueryParameterType)new DateParam("2001"))); + Bundle response = client.search(Patient.class, params); + + assertEquals("http://example.com/fhir/Patient?foo=2001", capt.getValue().getURI().toString()); + assertEquals(Patient.class, response.getEntries().get(0).getResource().getClass()); + + } + @Test public void testSearchWithProfileAndSecurity() throws Exception { 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\"}]}}]}"; @@ -2521,9 +2643,61 @@ public class GenericClientDstu2Test { return (OperationOutcome) theOperationOutcome; } + @AfterClass + public static void afterClassClearContext() { + TestUtil.clearAllStaticFieldsForUnitTest(); + } + @BeforeClass public static void beforeClass() { ourCtx = FhirContext.forDstu2(); } + public final static class RestfulClientInstance implements IRestfulClient { + @Override + public T fetchResourceFromUrl(Class theResourceType, String theUrl) { + return null; + } + + @Override + public FhirContext getFhirContext() { + return null; + } + + @Override + public IHttpClient getHttpClient() { + return null; + } + + @Override + public String getServerBase() { + return null; + } + + @Override + public void registerInterceptor(IClientInterceptor theInterceptor) { + //nothing + } + + @Override + public void setEncoding(EncodingEnum theEncoding) { + //nothing + } + + @Override + public void setPrettyPrint(Boolean thePrettyPrint) { + //nothing + } + + @Override + public void setSummary(SummaryEnum theSummary) { + //nothing + } + + @Override + public void unregisterInterceptor(IClientInterceptor theInterceptor) { + //nothing + } + } + } diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/apache/ApacheRestfulClientFactoryTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/apache/ApacheRestfulClientFactoryTest.java new file mode 100644 index 00000000000..d26affcbe6e --- /dev/null +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/apache/ApacheRestfulClientFactoryTest.java @@ -0,0 +1,40 @@ +package ca.uhn.fhir.rest.client.apache; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import org.junit.Test; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.rest.client.BaseClient; +import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException; + +public class ApacheRestfulClientFactoryTest { + + @Test + public void testSetContext() { + ApacheRestfulClientFactory factory = new ApacheRestfulClientFactory(); + factory.getServerValidationModeEnum(); + factory.setFhirContext(FhirContext.forDstu2()); + try { + factory.setFhirContext(FhirContext.forDstu2()); + fail(); + } catch (IllegalStateException e) { + assertEquals("java.lang.IllegalStateException: RestfulClientFactory instance is already associated with one FhirContext. RestfulClientFactory instances can not be shared.", e.toString()); + } + } + + @Test + public void testValidatateBase() { + FhirContext ctx = FhirContext.forDstu2(); + ApacheRestfulClientFactory factory = new ApacheRestfulClientFactory(); + factory.setFhirContext(ctx); + factory.setConnectTimeout(1); + try { + factory.validateServerBase("http://127.0.0.1:22225", factory.getHttpClient("http://foo"), (BaseClient) ctx.newRestfulGenericClient("http://foo")); + fail(); + } catch (FhirClientConnectionException e) { + assertEquals("Failed to retrieve the server metadata statement during client initialization. URL used was http://127.0.0.1:22225metadata", e.getMessage()); + } + } +} 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-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptorDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptorDstu2Test.java index f7c96863059..307e3da1217 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptorDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptorDstu2Test.java @@ -40,6 +40,7 @@ import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt; import ca.uhn.fhir.model.dstu2.resource.Bundle; import ca.uhn.fhir.model.dstu2.resource.Observation; +import ca.uhn.fhir.model.dstu2.resource.Parameters; import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.model.dstu2.valueset.BundleTypeEnum; import ca.uhn.fhir.model.dstu2.valueset.HTTPVerbEnum; @@ -47,6 +48,7 @@ import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.annotation.Create; import ca.uhn.fhir.rest.annotation.Delete; import ca.uhn.fhir.rest.annotation.IdParam; +import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.Search; @@ -158,6 +160,215 @@ public class AuthorizationInterceptorDstu2Test { assertTrue(ourHitMethod); } + @Test + public void testOperationServerLevel() throws Exception { + ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { + @Override + public List buildRuleList(RequestDetails theRequestDetails) { + //@formatter:off + return new RuleBuilder() + .allow("RULE 1").operation().named("opName").onServer().andThen() + .build(); + //@formatter:on + } + }); + + HttpGet httpGet; + HttpResponse status; + String response; + + // Server + ourHitMethod = false; + ourReturn = Arrays.asList(createObservation(10, "Patient/2")); + httpGet = new HttpGet("http://localhost:" + ourPort + "/$opName"); + status = ourClient.execute(httpGet); + extractResponseAndClose(status); + assertEquals(200, status.getStatusLine().getStatusCode()); + assertTrue(ourHitMethod); + + // Type + ourHitMethod = false; + ourReturn = Arrays.asList(createPatient(2)); + httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$opName"); + status = ourClient.execute(httpGet); + response = extractResponseAndClose(status); + ourLog.info(response); + assertThat(response, containsString("Access denied by default policy")); + assertEquals(403, status.getStatusLine().getStatusCode()); + assertFalse(ourHitMethod); + + // Instance + ourHitMethod = false; + ourReturn = Arrays.asList(createPatient(2)); + httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$opName"); + status = ourClient.execute(httpGet); + response = extractResponseAndClose(status); + ourLog.info(response); + assertThat(response, containsString("Access denied by default policy")); + assertEquals(403, status.getStatusLine().getStatusCode()); + assertFalse(ourHitMethod); + } + + @Test + public void testOperationInstanceLevel() throws Exception { + ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { + @Override + public List buildRuleList(RequestDetails theRequestDetails) { + //@formatter:off + return new RuleBuilder() + .allow("RULE 1").operation().named("opName").onInstance(new IdDt("http://example.com/Patient/1/_history/2")).andThen() + .build(); + //@formatter:on + } + }); + + HttpGet httpGet; + HttpResponse status; + String response; + + // Server + ourHitMethod = false; + ourReturn = Arrays.asList(createObservation(10, "Patient/2")); + httpGet = new HttpGet("http://localhost:" + ourPort + "/$opName"); + status = ourClient.execute(httpGet); + response = extractResponseAndClose(status); + assertThat(response, containsString("Access denied by default policy")); + assertEquals(403, status.getStatusLine().getStatusCode()); + assertFalse(ourHitMethod); + + // Type + ourHitMethod = false; + ourReturn = Arrays.asList(createPatient(2)); + httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$opName"); + status = ourClient.execute(httpGet); + response = extractResponseAndClose(status); + ourLog.info(response); + assertThat(response, containsString("Access denied by default policy")); + assertEquals(403, status.getStatusLine().getStatusCode()); + assertFalse(ourHitMethod); + + // Instance + ourHitMethod = false; + ourReturn = Arrays.asList(createPatient(2)); + httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$opName"); + status = ourClient.execute(httpGet); + response = extractResponseAndClose(status); + ourLog.info(response); + assertEquals(200, status.getStatusLine().getStatusCode()); + assertTrue(ourHitMethod); + + // Wrong instance + ourHitMethod = false; + ourReturn = Arrays.asList(createPatient(2)); + httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2/$opName"); + status = ourClient.execute(httpGet); + response = extractResponseAndClose(status); + ourLog.info(response); + assertThat(response, containsString("Access denied by default policy")); + assertEquals(403, status.getStatusLine().getStatusCode()); + assertFalse(ourHitMethod); + + } + + @Test + public void testOperationAnyName() throws Exception { + ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { + @Override + public List buildRuleList(RequestDetails theRequestDetails) { + //@formatter:off + return new RuleBuilder() + .allow("RULE 1").operation().withAnyName().onServer().andThen() + .build(); + //@formatter:on + } + }); + + HttpGet httpGet; + HttpResponse status; + String response; + + // Server + ourHitMethod = false; + ourReturn = Arrays.asList(createObservation(10, "Patient/2")); + httpGet = new HttpGet("http://localhost:" + ourPort + "/$opName"); + status = ourClient.execute(httpGet); + response = extractResponseAndClose(status); + assertEquals(200, status.getStatusLine().getStatusCode()); + assertTrue(ourHitMethod); + + } + + @Test + public void testOperationTypeLevel() throws Exception { + ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { + @Override + public List buildRuleList(RequestDetails theRequestDetails) { + //@formatter:off + return new RuleBuilder() + .allow("RULE 1").operation().named("opName").onType(Patient.class).andThen() + .build(); + //@formatter:on + } + }); + + HttpGet httpGet; + HttpResponse status; + String response; + + // Server + ourHitMethod = false; + ourReturn = Arrays.asList(createObservation(10, "Patient/2")); + httpGet = new HttpGet("http://localhost:" + ourPort + "/$opName"); + status = ourClient.execute(httpGet); + response = extractResponseAndClose(status); + assertThat(response, containsString("Access denied by default policy")); + assertEquals(403, status.getStatusLine().getStatusCode()); + assertFalse(ourHitMethod); + + // Type + ourHitMethod = false; + ourReturn = Arrays.asList(createPatient(2)); + httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$opName"); + status = ourClient.execute(httpGet); + response = extractResponseAndClose(status); + ourLog.info(response); + assertEquals(200, status.getStatusLine().getStatusCode()); + assertTrue(ourHitMethod); + + // Wrong type + ourHitMethod = false; + ourReturn = Arrays.asList(createPatient(2)); + httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation/1/$opName"); + status = ourClient.execute(httpGet); + response = extractResponseAndClose(status); + ourLog.info(response); + assertThat(response, containsString("Access denied by default policy")); + assertEquals(403, status.getStatusLine().getStatusCode()); + assertFalse(ourHitMethod); + + // Wrong name + ourHitMethod = false; + ourReturn = Arrays.asList(createPatient(2)); + httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$opName2"); + status = ourClient.execute(httpGet); + response = extractResponseAndClose(status); + ourLog.info(response); + assertThat(response, containsString("Access denied by default policy")); + assertEquals(403, status.getStatusLine().getStatusCode()); + assertFalse(ourHitMethod); + + // Instance + ourHitMethod = false; + ourReturn = Arrays.asList(createPatient(2)); + httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$opName"); + status = ourClient.execute(httpGet); + response = extractResponseAndClose(status); + ourLog.info(response); + assertThat(response, containsString("Access denied by default policy")); + assertEquals(403, status.getStatusLine().getStatusCode()); + assertFalse(ourHitMethod); + } + @Test public void testDenyAll() throws Exception { ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { @@ -762,6 +973,17 @@ public class AuthorizationInterceptorDstu2Test { retVal.setResource(theResource); return retVal; } + @Operation(name="opName", idempotent=true) + public Parameters operation() { + ourHitMethod = true; + return (Parameters) new Parameters().setId("1"); + } + @Operation(name="opName", idempotent=true) + public Parameters operation(@IdParam IdDt theId) { + ourHitMethod = true; + return (Parameters) new Parameters().setId("1"); + } + } @@ -813,11 +1035,37 @@ public class AuthorizationInterceptorDstu2Test { retVal.setResource(theResource); return retVal; } + + @Operation(name="opName", idempotent=true) + public Parameters operation() { + ourHitMethod = true; + return (Parameters) new Parameters().setId("1"); + } + + @Operation(name="opName", idempotent=true) + public Parameters operation(@IdParam IdDt theId) { + ourHitMethod = true; + return (Parameters) new Parameters().setId("1"); + } + + @Operation(name="opName2", idempotent=true) + public Parameters operation2(@IdParam IdDt theId) { + ourHitMethod = true; + return (Parameters) new Parameters().setId("1"); + } } public static class PlainProvider { + + @Operation(name="opName", idempotent=true) + public Parameters operation() { + ourHitMethod = true; + return (Parameters) new Parameters().setId("1"); + } + + @Transaction() public Bundle search(@TransactionParam Bundle theInput) { ourHitMethod = true; diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/context/FhirContextDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/context/FhirContextDstu3Test.java index 2f6f7c76434..f188b0bbf4d 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/context/FhirContextDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/context/FhirContextDstu3Test.java @@ -24,7 +24,7 @@ public class FhirContextDstu3Test { public static void afterClassClearContext() { TestUtil.clearAllStaticFieldsForUnitTest(); } - + /** * See #344 */ diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/Problem.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/ErrorHandlerAdapterTest.java similarity index 57% rename from hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/Problem.java rename to hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/ErrorHandlerAdapterTest.java index c9da66db11d..61140664466 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/Problem.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/ErrorHandlerAdapterTest.java @@ -1,4 +1,9 @@ -package ca.uhn.fhir.validation; +package ca.uhn.fhir.parser; + +import org.junit.Test; + +import ca.uhn.fhir.parser.ErrorHandlerAdapter; +import ca.uhn.fhir.parser.IParserErrorHandler; /* * #%L @@ -20,16 +25,15 @@ package ca.uhn.fhir.validation; * #L% */ -public class Problem { - - private String myDescription; - - public Problem(String theDescription) { - myDescription=theDescription; +/** + * Adapter implementation with NOP implementations of all {@link IParserErrorHandler} methods. + */ +public class ErrorHandlerAdapterTest { + + @Test + public void testMethods() { + new ErrorHandlerAdapter().unexpectedRepeatingElement(null, null); + new ErrorHandlerAdapter().unknownAttribute(null, null); + new ErrorHandlerAdapter().unknownElement(null, null); } - - public String getDescription() { - return myDescription; - } - } 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 5ae701b0c78..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 @@ -1,13 +1,13 @@ package ca.uhn.fhir.rest.client; import static org.hamcrest.Matchers.containsString; -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.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -22,7 +22,6 @@ 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; @@ -50,19 +49,29 @@ import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; +import com.google.common.base.Charsets; +import com.phloc.commons.io.streams.StringInputStream; + 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.model.primitive.UriDt; 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.client.exceptions.FhirClientConnectionException; +import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException; +import ca.uhn.fhir.rest.client.interceptor.CookieInterceptor; +import ca.uhn.fhir.rest.client.interceptor.UserInfoInterceptor; import ca.uhn.fhir.rest.param.ParamPrefixEnum; import ca.uhn.fhir.rest.server.Constants; +import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException; +import ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException; import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.UrlUtil; import ca.uhn.fhir.util.VersionUtil; @@ -81,6 +90,9 @@ public class GenericClientDstu3Test { ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); myHttpResponse = mock(HttpResponse.class, new ReturnsDeepStubs()); myAnswerCount = 0; + + System.setProperty(BaseClient.HAPI_CLIENT_KEEPRESPONSES, "true"); + } private String expectedUserAgent() { @@ -176,6 +188,67 @@ public class GenericClientDstu3Test { } + @SuppressWarnings("unchecked") + @Test + public void testClientFailures() throws Exception { + 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()).thenThrow(IllegalStateException.class, RuntimeException.class, Exception.class); + + IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); + + try { + client.read().resource(Patient.class).withId("1").execute(); + fail(); + } catch (FhirClientConnectionException e) { + assertEquals("java.lang.IllegalStateException", e.getMessage()); + } + + try { + client.read().resource(Patient.class).withId("1").execute(); + fail(); + } catch (RuntimeException e) { + assertEquals("java.lang.RuntimeException", e.toString()); + } + + try { + client.read().resource(Patient.class).withId("1").execute(); + fail(); + } catch (FhirClientConnectionException e) { + assertEquals("java.lang.Exception", e.getMessage()); + } + } + + @Test + public void testCookieInterceptor() throws Exception { + final String respString = CustomTypeDstu3Test.createBundle(CustomTypeDstu3Test.createResource(false)); + 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")); + } + }); + + IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); + client.registerInterceptor(new CookieInterceptor("foo=bar")); + + //@formatter:off + Bundle resp = client + .history() + .onType(Patient.class) + .andReturnBundle(Bundle.class) + .execute(); + //@formatter:on + + assertEquals("foo=bar", capt.getAllValues().get(0).getFirstHeader("Cookie").getValue()); + } + @Test public void testCreateWithPreferRepresentationServerReturnsOO() throws Exception { final IParser p = ourCtx.newXmlParser(); @@ -420,186 +493,147 @@ public class GenericClientDstu3Test { } @Test - public void testUpdateById() 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")); - } - }); - - IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); - - Patient pt = new Patient(); - pt.setId("222"); - pt.getText().setDivAsString("A PATIENT"); - - client.update().resource(pt).withId("111").execute(); - - ourLog.info(Arrays.asList(capt.getAllValues().get(0).getAllHeaders()).toString()); - - assertEquals("http://example.com/fhir/Patient/111", capt.getAllValues().get(0).getURI().toASCIIString()); - validateUserAgent(capt); - - assertEquals("application/xml+fhir;charset=utf-8", capt.getAllValues().get(0).getHeaders("Content-Type")[0].getValue().toLowerCase().replace(" ", "")); - assertEquals(Constants.CT_FHIR_XML, capt.getAllValues().get(0).getHeaders("Accept")[0].getValue()); - String body = extractBodyAsString(capt); - assertThat(body, containsString("")); - } - - @Test - public void testUpdateWithPreferRepresentationServerReturnsOO() throws Exception { + public void testForceConformance() throws Exception { final IParser p = ourCtx.newXmlParser(); - final OperationOutcome resp0 = new OperationOutcome(); - resp0.getText().setDivAsString("OK!"); - - final Patient resp1 = new Patient(); - resp1.getText().setDivAsString("FINAL VALUE"); - - 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.getAllHeaders()).thenAnswer(new Answer() { - @Override - public Header[] answer(InvocationOnMock theInvocation) throws Throwable { - return new Header[] { new BasicHeader(Constants.HEADER_LOCATION, "http://foo.com/base/Patient/222/_history/3") }; - } - }); - 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 { - if (myAnswerCount++ == 0) { - return new ReaderInputStream(new StringReader(p.encodeResourceToString(resp0)), Charset.forName("UTF-8")); - } else { - return new ReaderInputStream(new StringReader(p.encodeResourceToString(resp1)), Charset.forName("UTF-8")); - } - } - }); - - IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); - - Patient pt = new Patient(); - pt.setId("Patient/222"); - pt.getText().setDivAsString("A PATIENT"); - - MethodOutcome outcome = client.update().resource(pt).prefer(PreferReturnEnum.REPRESENTATION).execute(); - - assertEquals(2, myAnswerCount); - assertNotNull(outcome.getOperationOutcome()); - assertNotNull(outcome.getResource()); - - assertEquals("
OK!
", ((OperationOutcome) outcome.getOperationOutcome()).getText().getDivAsString()); - assertEquals("
FINAL VALUE
", ((Patient) outcome.getResource()).getText().getDivAsString()); - - assertEquals(myAnswerCount, capt.getAllValues().size()); - assertEquals("http://example.com/fhir/Patient/222", capt.getAllValues().get(0).getURI().toASCIIString()); - assertEquals("http://foo.com/base/Patient/222/_history/3", capt.getAllValues().get(1).getURI().toASCIIString()); - } - - @Test - public void testValidate() throws Exception { - final IParser p = ourCtx.newXmlParser(); - - final OperationOutcome resp0 = new OperationOutcome(); - resp0.getText().setDivAsString("OK!"); - - 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.getAllHeaders()).thenAnswer(new Answer() { - @Override - public Header[] answer(InvocationOnMock theInvocation) throws Throwable { - return new Header[] { }; - } - }); - 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(p.encodeResourceToString(resp0)), Charset.forName("UTF-8")); - } - }); - - IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); - - Patient pt = new Patient(); - pt.setId("Patient/222"); - pt.getText().setDivAsString("A PATIENT"); - - MethodOutcome outcome = client.validate().resource(pt).execute(); - - assertNotNull(outcome.getOperationOutcome()); - assertEquals("
OK!
", ((OperationOutcome) outcome.getOperationOutcome()).getText().getDivAsString()); - - } - - @Test - public void testUpdateWithPreferRepresentationServerReturnsResource() throws Exception { - final IParser p = ourCtx.newXmlParser(); - - final Patient resp1 = new Patient(); - resp1.getText().setDivAsString("FINAL VALUE"); - - 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.getAllHeaders()).thenAnswer(new Answer() { - @Override - public Header[] answer(InvocationOnMock theInvocation) throws Throwable { - return new Header[] { new BasicHeader(Constants.HEADER_LOCATION, "http://foo.com/base/Patient/222/_history/3") }; - } - }); - 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 { - myAnswerCount++; - return new ReaderInputStream(new StringReader(p.encodeResourceToString(resp1)), Charset.forName("UTF-8")); - } - }); - - IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); - - Patient pt = new Patient(); - pt.setId("Patient/222"); - pt.getText().setDivAsString("A PATIENT"); - - MethodOutcome outcome = client.update().resource(pt).prefer(PreferReturnEnum.REPRESENTATION).execute(); - - assertEquals(1, myAnswerCount); - assertNull(outcome.getOperationOutcome()); - assertNotNull(outcome.getResource()); - - assertEquals("
FINAL VALUE
", ((Patient) outcome.getResource()).getText().getDivAsString()); - - assertEquals(myAnswerCount, capt.getAllValues().size()); - assertEquals("http://example.com/fhir/Patient/222", capt.getAllValues().get(0).getURI().toASCIIString()); - } - - @Test - public void testUserAgentForBinary() throws Exception { - IParser p = ourCtx.newXmlParser(); - - Conformance conf = new Conformance(); + final Conformance conf = new Conformance(); conf.setCopyright("COPY"); - final String respString = p.encodeResourceToString(conf); + final Patient patient = new Patient(); + patient.addName().addFamily("FAM"); + 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() { + private int myCount = 0; + + @Override + public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable { + final String respString; + if (myCount == 1 || myCount == 2) { + ourLog.info("Encoding patient"); + respString = p.encodeResourceToString(patient); + } else { + ourLog.info("Encoding conformance"); + respString = p.encodeResourceToString(conf); + } + myCount++; + return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8")); + } + }); + + ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE); + IGenericClient client = ourCtx.newRestfulGenericClient("http://testForceConformance.com/fhir"); + + client.read().resource("Patient").withId("1").execute(); + assertEquals(2, capt.getAllValues().size()); + assertEquals("http://testForceConformance.com/fhir/metadata", capt.getAllValues().get(0).getURI().toASCIIString()); + assertEquals("http://testForceConformance.com/fhir/Patient/1", capt.getAllValues().get(1).getURI().toASCIIString()); + + client.read().resource("Patient").withId("1").execute(); + assertEquals(3, capt.getAllValues().size()); + assertEquals("http://testForceConformance.com/fhir/Patient/1", capt.getAllValues().get(2).getURI().toASCIIString()); + + client.forceConformanceCheck(); + assertEquals(4, capt.getAllValues().size()); + assertEquals("http://testForceConformance.com/fhir/metadata", capt.getAllValues().get(3).getURI().toASCIIString()); + } + + @Test + public void testHttp499() throws Exception { + ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class); + when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse); + when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 499, "Wacky Message")); + 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 StringInputStream answer(InvocationOnMock theInvocation) throws Throwable { + return new StringInputStream("HELLO", Charsets.UTF_8); + } + }); + + IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); + + try { + client.read().resource(Patient.class).withId("1").execute(); + fail(); + } catch (UnclassifiedServerFailureException e) { + assertEquals("ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException: HTTP 499 Wacky Message", e.toString()); + assertEquals("HELLO", e.getResponseBody()); + } + + } + + @Test + public void testHttp501() throws Exception { + ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class); + when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse); + when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 501, "Not Implemented")); + 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 StringInputStream answer(InvocationOnMock theInvocation) throws Throwable { + return new StringInputStream("not implemented", Charsets.UTF_8); + } + }); + + IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); + + try { + client.read().resource(Patient.class).withId("1").execute(); + fail(); + } catch (NotImplementedOperationException e) { + assertEquals("HTTP 501 Not Implemented", e.getMessage()); + } + + } + + @SuppressWarnings("deprecation") + @Test + public void testInvalidConformanceCall() { + IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); + try { + client.conformance(); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("Must call fetchConformance() instead of conformance() for RI/STU3+ structures", e.getMessage()); + } + } + + @Test + public void testReadWithUnparseableResponse() throws Exception { + String msg = "{\"resourceTypeeeee\":\"Patient\"}"; + + 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()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"))); + + IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); + + try { + client.read().resource("Patient").withId("123").elementsSubset("name", "identifier").execute(); + fail(); + } catch (FhirClientConnectionException e) { + assertEquals("Failed to parse response from server when performing GET to URL http://example.com/fhir/Patient/123?_elements=identifier%2Cname - ca.uhn.fhir.parser.DataFormatException: Invalid JSON content detected, missing required element: 'resourceType'", e.getMessage()); + } + } + + @Test + public void testResponseHasContentTypeMissing() throws Exception { + IParser p = ourCtx.newXmlParser(); + Patient patient = new Patient(); + patient.addName().addFamily("FAM"); + final String respString = p.encodeResourceToString(patient); + + 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().getContentType()).thenReturn(null); when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() { @Override public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable { @@ -608,38 +642,29 @@ public class GenericClientDstu3Test { }); IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); + try { + client.read().resource(Patient.class).withId("1").execute(); + fail(); + } catch (NonFhirResponseException e) { + assertEquals("Response contains no Content-Type", e.getMessage()); + } - Binary bin = new Binary(); - bin.setContentType("application/foo"); - bin.setContent(new byte[] { 0, 1, 2, 3, 4 }); - client.create().resource(bin).execute(); - - ourLog.info(Arrays.asList(capt.getAllValues().get(0).getAllHeaders()).toString()); - - assertEquals("http://example.com/fhir/Binary", capt.getAllValues().get(0).getURI().toASCIIString()); - validateUserAgent(capt); - - assertEquals("application/foo", capt.getAllValues().get(0).getHeaders("Content-Type")[0].getValue()); - assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON, capt.getAllValues().get(0).getHeaders("Accept")[0].getValue()); - assertArrayEquals(new byte[] { 0, 1, 2, 3, 4 }, extractBodyAsByteArray(capt)); - + // Patient resp = client.read().resource(Patient.class).withId("1").execute(); + // assertEquals("FAM", resp.getNameFirstRep().getFamilyAsSingleString()); } - /** - * See #371 - */ @Test - public void testSortDstu3Test() throws Exception { + public void testResponseHasContentTypeNonFhir() throws Exception { IParser p = ourCtx.newXmlParser(); + Patient patient = new Patient(); + patient.addName().addFamily("FAM"); + final String respString = p.encodeResourceToString(patient); - Bundle b = new Bundle(); - b.setType(BundleType.SEARCHSET); - - final String respString = p.encodeResourceToString(b); 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().getContentType()).thenReturn(new BasicHeader("content-type", "text/plain")); + // when(myHttpResponse.getEntity().getContentType()).thenReturn(null); when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() { @Override public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable { @@ -647,85 +672,134 @@ public class GenericClientDstu3Test { } }); + IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); + try { + client.read().resource(Patient.class).withId("1").execute(); + fail(); + } catch (NonFhirResponseException e) { + assertEquals("Response contains non FHIR Content-Type 'text/plain' : ", e.getMessage()); + } + + // Patient resp = client.read().resource(Patient.class).withId("1").execute(); + // assertEquals("FAM", resp.getNameFirstRep().getFamilyAsSingleString()); + } + + @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.class) - .sort().ascending("address") + client.search() + .forResource("Patient") + .where(Patient.BIRTHDATE.after().day("2011-01-02")) .returnBundle(Bundle.class) .execute(); - assertEquals("http://example.com/fhir/Patient?_sort=address", capt.getAllValues().get(idx++).getURI().toASCIIString()); + //@formatter:on + assertEquals("http://example.com/fhir/Patient?birthdate=gt2011-01-02", capt.getAllValues().get(idx).getURI().toString()); + idx++; - client - .search() - .forResource(Patient.class) - .sort().descending("address") + //@formatter:off + client.search() + .forResource("Patient") + .where(Patient.BIRTHDATE.after().day(date)) .returnBundle(Bundle.class) .execute(); - assertEquals("http://example.com/fhir/Patient?_sort=-address", capt.getAllValues().get(idx++).getURI().toASCIIString()); - - client - .search() - .forResource(Patient.class) - .sort().descending("address") - .sort().ascending("name") - .sort().descending(Patient.BIRTHDATE) + //@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(); - assertEquals("http://example.com/fhir/Patient?_sort=-address%2Cname%2C-birthdate", capt.getAllValues().get(idx++).getURI().toASCIIString()); - //@formatter:on + //@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++; } - @Test - public void testUserAgentForConformance() throws Exception { - IParser p = ourCtx.newXmlParser(); - - Conformance conf = new Conformance(); - conf.setCopyright("COPY"); - - 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")); - } - }); - - IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); - - client.fetchConformance().ofType(Conformance.class).execute(); - assertEquals("http://example.com/fhir/metadata", capt.getAllValues().get(0).getURI().toASCIIString()); - validateUserAgent(capt); - } - - private List> toTypeList(Class theClass) { - ArrayList> retVal = new ArrayList>(); - retVal.add(theClass); - return retVal; - } - - private void validateUserAgent(ArgumentCaptor capt) { - assertEquals(1, capt.getAllValues().get(0).getHeaders("User-Agent").length); - assertEquals(expectedUserAgent(), capt.getAllValues().get(0).getHeaders("User-Agent")[0].getValue()); - } - - @AfterClass - public static void afterClassClearContext() { - TestUtil.clearAllStaticFieldsForUnitTest(); - } - - @BeforeClass - public static void beforeClass() { - ourCtx = FhirContext.forDstu3(); - } - @SuppressWarnings("deprecation") @Test public void testSearchByQuantity() throws Exception { @@ -739,11 +813,12 @@ public class GenericClientDstu3Test { @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") @@ -866,47 +941,6 @@ public class GenericClientDstu3Test { } - @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\"}]}}]}"; @@ -919,11 +953,12 @@ public class GenericClientDstu3Test { @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") @@ -963,9 +998,7 @@ public class GenericClientDstu3Test { //@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") @@ -1009,7 +1042,7 @@ public class GenericClientDstu3Test { } @Test - public void testSearchByDate() throws Exception { + 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); @@ -1020,107 +1053,417 @@ public class GenericClientDstu3Test { @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 testSearchForUnknownType() throws Exception { + IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); + try { + client.search(new UriDt("?aaaa")); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("Unable to determine the resource type from the given URI: ?aaaa", e.getMessage()); + } + } + + /** + * See #371 + */ + @Test + public void testSortDstu3Test() throws Exception { + IParser p = ourCtx.newXmlParser(); + + Bundle b = new Bundle(); + b.setType(BundleType.SEARCHSET); + + final String respString = p.encodeResourceToString(b); + 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")); + } + }); + + IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); + int idx = 0; + + //@formatter:off + client + .search() + .forResource(Patient.class) + .sort().ascending("address") + .returnBundle(Bundle.class) + .execute(); + assertEquals("http://example.com/fhir/Patient?_sort=address", capt.getAllValues().get(idx++).getURI().toASCIIString()); + + client + .search() + .forResource(Patient.class) + .sort().descending("address") + .returnBundle(Bundle.class) + .execute(); + assertEquals("http://example.com/fhir/Patient?_sort=-address", capt.getAllValues().get(idx++).getURI().toASCIIString()); - Date date = new DateTimeDt("2011-01-02T22:33:01Z").getValue(); - - //@formatter:off - client.search() - .forResource("Patient") - .where(Patient.BIRTHDATE.after().day("2011-01-02")) + client + .search() + .forResource(Patient.class) + .sort().descending("address") + .sort().ascending("name") + .sort().descending(Patient.BIRTHDATE) .returnBundle(Bundle.class) .execute(); - //@formatter:on - assertEquals("http://example.com/fhir/Patient?birthdate=gt2011-01-02", capt.getAllValues().get(idx).getURI().toString()); - idx++; + assertEquals("http://example.com/fhir/Patient?_sort=-address%2Cname%2C-birthdate", capt.getAllValues().get(idx++).getURI().toASCIIString()); + //@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(); + + 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")); + } + }); + + IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); + + Patient pt = new Patient(); + pt.setId("222"); + pt.getText().setDivAsString("A PATIENT"); + + client.update().resource(pt).withId("111").execute(); + + ourLog.info(Arrays.asList(capt.getAllValues().get(0).getAllHeaders()).toString()); + + assertEquals("http://example.com/fhir/Patient/111", capt.getAllValues().get(0).getURI().toASCIIString()); + validateUserAgent(capt); + + assertEquals("application/xml+fhir;charset=utf-8", capt.getAllValues().get(0).getHeaders("Content-Type")[0].getValue().toLowerCase().replace(" ", "")); + assertEquals(Constants.CT_FHIR_XML, capt.getAllValues().get(0).getHeaders("Accept")[0].getValue()); + String body = extractBodyAsString(capt); + assertThat(body, containsString("")); + } + + @Test + public void testUpdateWithPreferRepresentationServerReturnsOO() throws Exception { + final IParser p = ourCtx.newXmlParser(); + + final OperationOutcome resp0 = new OperationOutcome(); + resp0.getText().setDivAsString("OK!"); + + final Patient resp1 = new Patient(); + resp1.getText().setDivAsString("FINAL VALUE"); + + 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.getAllHeaders()).thenAnswer(new Answer() { + @Override + public Header[] answer(InvocationOnMock theInvocation) throws Throwable { + return new Header[] { new BasicHeader(Constants.HEADER_LOCATION, "http://foo.com/base/Patient/222/_history/3") }; + } + }); + 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 { + if (myAnswerCount++ == 0) { + return new ReaderInputStream(new StringReader(p.encodeResourceToString(resp0)), Charset.forName("UTF-8")); + } else { + return new ReaderInputStream(new StringReader(p.encodeResourceToString(resp1)), Charset.forName("UTF-8")); + } + } + }); + + IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); + + Patient pt = new Patient(); + pt.setId("Patient/222"); + pt.getText().setDivAsString("A PATIENT"); + + MethodOutcome outcome = client.update().resource(pt).prefer(PreferReturnEnum.REPRESENTATION).execute(); + + assertEquals(2, myAnswerCount); + assertNotNull(outcome.getOperationOutcome()); + assertNotNull(outcome.getResource()); + + assertEquals("
OK!
", ((OperationOutcome) outcome.getOperationOutcome()).getText().getDivAsString()); + assertEquals("
FINAL VALUE
", ((Patient) outcome.getResource()).getText().getDivAsString()); + + assertEquals(myAnswerCount, capt.getAllValues().size()); + assertEquals("http://example.com/fhir/Patient/222", capt.getAllValues().get(0).getURI().toASCIIString()); + assertEquals("http://foo.com/base/Patient/222/_history/3", capt.getAllValues().get(1).getURI().toASCIIString()); + } + + @Test + public void testUpdateWithPreferRepresentationServerReturnsResource() throws Exception { + final IParser p = ourCtx.newXmlParser(); + + final Patient resp1 = new Patient(); + resp1.getText().setDivAsString("FINAL VALUE"); + + 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.getAllHeaders()).thenAnswer(new Answer() { + @Override + public Header[] answer(InvocationOnMock theInvocation) throws Throwable { + return new Header[] { new BasicHeader(Constants.HEADER_LOCATION, "http://foo.com/base/Patient/222/_history/3") }; + } + }); + 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 { + myAnswerCount++; + return new ReaderInputStream(new StringReader(p.encodeResourceToString(resp1)), Charset.forName("UTF-8")); + } + }); + + IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); + + Patient pt = new Patient(); + pt.setId("Patient/222"); + pt.getText().setDivAsString("A PATIENT"); + + MethodOutcome outcome = client.update().resource(pt).prefer(PreferReturnEnum.REPRESENTATION).execute(); + + assertEquals(1, myAnswerCount); + assertNull(outcome.getOperationOutcome()); + assertNotNull(outcome.getResource()); + + assertEquals("
FINAL VALUE
", ((Patient) outcome.getResource()).getText().getDivAsString()); + + assertEquals(myAnswerCount, capt.getAllValues().size()); + assertEquals("http://example.com/fhir/Patient/222", capt.getAllValues().get(0).getURI().toASCIIString()); + } + + @Test + public void testUserAgentForBinary() throws Exception { + IParser p = ourCtx.newXmlParser(); + + Conformance conf = new Conformance(); + conf.setCopyright("COPY"); + + 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")); + } + }); + + IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); + + Binary bin = new Binary(); + bin.setContentType("application/foo"); + bin.setContent(new byte[] { 0, 1, 2, 3, 4 }); + client.create().resource(bin).execute(); + + ourLog.info(Arrays.asList(capt.getAllValues().get(0).getAllHeaders()).toString()); + + assertEquals("http://example.com/fhir/Binary", capt.getAllValues().get(0).getURI().toASCIIString()); + validateUserAgent(capt); + + assertEquals("application/foo", capt.getAllValues().get(0).getHeaders("Content-Type")[0].getValue()); + assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON, capt.getAllValues().get(0).getHeaders("Accept")[0].getValue()); + assertArrayEquals(new byte[] { 0, 1, 2, 3, 4 }, extractBodyAsByteArray(capt)); + + } + + @Test + public void testUserAgentForConformance() throws Exception { + IParser p = ourCtx.newXmlParser(); + + Conformance conf = new Conformance(); + conf.setCopyright("COPY"); + + 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")); + } + }); + + IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); + + client.fetchConformance().ofType(Conformance.class).execute(); + assertEquals("http://example.com/fhir/metadata", capt.getAllValues().get(0).getURI().toASCIIString()); + validateUserAgent(capt); + } + + @Test + public void testUserInfoInterceptor() throws Exception { + final String respString = CustomTypeDstu3Test.createBundle(CustomTypeDstu3Test.createResource(false)); + 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")); + } + }); + + IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); + client.registerInterceptor(new UserInfoInterceptor("user_id", "user_name", "app-name")); //@formatter:off - client.search() - .forResource("Patient") - .where(Patient.BIRTHDATE.after().day(date)) - .returnBundle(Bundle.class) + Bundle resp = client + .history() + .onType(Patient.class) + .andReturnBundle(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++; + @Test + public void testValidate() throws Exception { + final IParser p = ourCtx.newXmlParser(); - //@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++; + final OperationOutcome resp0 = new OperationOutcome(); + resp0.getText().setDivAsString("OK!"); - //@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++; + 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.getAllHeaders()).thenAnswer(new Answer() { + @Override + public Header[] answer(InvocationOnMock theInvocation) throws Throwable { + return new Header[] {}; + } + }); + 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(p.encodeResourceToString(resp0)), Charset.forName("UTF-8")); + } + }); - //@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++; + IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); - 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++; + Patient pt = new Patient(); + pt.setId("Patient/222"); + pt.getText().setDivAsString("A PATIENT"); - //@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++; + MethodOutcome outcome = client.validate().resource(pt).execute(); + + assertNotNull(outcome.getOperationOutcome()); + assertEquals("
OK!
", ((OperationOutcome) outcome.getOperationOutcome()).getText().getDivAsString()); + + } + + private List> toTypeList(Class theClass) { + ArrayList> retVal = new ArrayList>(); + retVal.add(theClass); + return retVal; + } + + private void validateUserAgent(ArgumentCaptor capt) { + assertEquals(1, capt.getAllValues().get(0).getHeaders("User-Agent").length); + assertEquals(expectedUserAgent(), capt.getAllValues().get(0).getHeaders("User-Agent")[0].getValue()); + } + + @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/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()); + } + +} diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/InterceptorDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/InterceptorDstu3Test.java index 857ecf5ee8b..ba706f6dcc1 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/InterceptorDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/InterceptorDstu3Test.java @@ -1,6 +1,7 @@ package ca.uhn.fhir.rest.server; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.mockito.Matchers.any; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; @@ -8,6 +9,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import java.util.ArrayList; import java.util.concurrent.TimeUnit; import javax.servlet.http.HttpServletRequest; @@ -25,6 +27,7 @@ import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.Patient; +import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; @@ -50,7 +53,7 @@ public class InterceptorDstu3Test { private static final FhirContext ourCtx = FhirContext.forDstu3(); private static int ourPort; private static Server ourServer; - private static RestfulServer servlet; + private static RestfulServer ourServlet; private IServerInterceptor myInterceptor1; private IServerInterceptor myInterceptor2; @@ -58,7 +61,7 @@ public class InterceptorDstu3Test { public void before() { myInterceptor1 = mock(IServerInterceptor.class); myInterceptor2 = mock(IServerInterceptor.class); - servlet.setInterceptors(myInterceptor1, myInterceptor2); + ourServlet.setInterceptors(myInterceptor1, myInterceptor2); } @@ -121,6 +124,13 @@ public class InterceptorDstu3Test { ourServer.stop(); TestUtil.clearAllStaticFieldsForUnitTest(); } + + @After + public void after() { + for (IServerInterceptor next : new ArrayList(ourServlet.getInterceptors())) { + ourServlet.unregisterInterceptor(next); + } + } @BeforeClass public static void beforeClass() throws Exception { @@ -130,9 +140,9 @@ public class InterceptorDstu3Test { DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); ServletHandler proxyHandler = new ServletHandler(); - servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); + ourServlet = new RestfulServer(ourCtx); + ourServlet.setResourceProviders(patientProvider); + ServletHolder servletHolder = new ServletHolder(ourServlet); proxyHandler.addServletWithMapping(servletHolder, "/*"); ourServer.setHandler(proxyHandler); ourServer.start(); diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchSortDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchSortDstu3Test.java index 9cc7e983b64..1fe1fefb4ac 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchSortDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchSortDstu3Test.java @@ -1,8 +1,6 @@ package ca.uhn.fhir.rest.server; -import static org.hamcrest.Matchers.stringContainsInOrder; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; import java.util.ArrayList; import java.util.List; @@ -26,13 +24,10 @@ import org.junit.BeforeClass; import org.junit.Test; import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.annotation.Count; -import ca.uhn.fhir.rest.annotation.OptionalParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.annotation.Sort; import ca.uhn.fhir.rest.api.SortOrderEnum; import ca.uhn.fhir.rest.api.SortSpec; -import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.util.PortUtil; import ca.uhn.fhir.util.TestUtil; diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchWithServerAddressStrategyDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchWithServerAddressStrategyDstu3Test.java index 613c80134e9..37171de83ee 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchWithServerAddressStrategyDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchWithServerAddressStrategyDstu3Test.java @@ -21,14 +21,11 @@ import org.hl7.fhir.dstu3.model.HumanName; 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 ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.annotation.RequiredParam; import ca.uhn.fhir.rest.annotation.Search; -import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.util.PortUtil; import ca.uhn.fhir.util.TestUtil; @@ -55,6 +52,55 @@ public class SearchWithServerAddressStrategyDstu3Test { assertThat(responseContent, containsString("")); } + @Test + public void testApacheProxyAddressStrategy() throws Exception { + + ourServlet.setServerAddressStrategy(ApacheProxyAddressStrategy.forHttp()); + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); + HttpResponse status = ourClient.execute(httpGet); + String responseContent = IOUtils.toString(status.getEntity().getContent()); + IOUtils.closeQuietly(status.getEntity().getContent()); + ourLog.info(responseContent); + assertEquals(200, status.getStatusLine().getStatusCode()); + assertThat(responseContent, containsString("")); + + ourServlet.setServerAddressStrategy(new ApacheProxyAddressStrategy(false)); + httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); + httpGet.addHeader("x-forwarded-host", "foo.com"); + status = ourClient.execute(httpGet); + responseContent = IOUtils.toString(status.getEntity().getContent()); + IOUtils.closeQuietly(status.getEntity().getContent()); + ourLog.info(responseContent); + assertEquals(200, status.getStatusLine().getStatusCode()); + assertThat(responseContent, containsString("")); + + ourServlet.setServerAddressStrategy(ApacheProxyAddressStrategy.forHttps()); + httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); + httpGet.addHeader("x-forwarded-host", "foo.com"); + status = ourClient.execute(httpGet); + responseContent = IOUtils.toString(status.getEntity().getContent()); + IOUtils.closeQuietly(status.getEntity().getContent()); + ourLog.info(responseContent); + assertEquals(200, status.getStatusLine().getStatusCode()); + assertThat(responseContent, containsString("")); + + ourServlet.setServerAddressStrategy(new ApacheProxyAddressStrategy(false)); + httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); + httpGet.addHeader("x-forwarded-host", "foo.com"); + httpGet.addHeader("x-forwarded-proto", "https"); + status = ourClient.execute(httpGet); + responseContent = IOUtils.toString(status.getEntity().getContent()); + IOUtils.closeQuietly(status.getEntity().getContent()); + ourLog.info(responseContent); + assertEquals(200, status.getStatusLine().getStatusCode()); + assertThat(responseContent, containsString("")); + + } + @Test public void testHardcodedAddressStrategy() throws Exception { ourServlet.setServerAddressStrategy(new HardcodedServerAddressStrategy("http://example.com/fhir/base")); diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/UnclassifiedServerExceptionDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/UnclassifiedServerExceptionDstu3Test.java new file mode 100644 index 00000000000..be4aa920776 --- /dev/null +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/UnclassifiedServerExceptionDstu3Test.java @@ -0,0 +1,110 @@ +package ca.uhn.fhir.rest.server; + +import static org.hamcrest.Matchers.stringContainsInOrder; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.io.IOUtils; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.hl7.fhir.dstu3.model.OperationOutcome; +import org.hl7.fhir.dstu3.model.OperationOutcome.IssueType; +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 ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.api.SortSpec; +import ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException; +import ca.uhn.fhir.util.PortUtil; +import ca.uhn.fhir.util.TestUtil; + +public class UnclassifiedServerExceptionDstu3Test { + + private static CloseableHttpClient ourClient; + private static FhirContext ourCtx = FhirContext.forDstu3(); + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(UnclassifiedServerExceptionDstu3Test.class); + private static int ourPort; + private static Server ourServer; + public static UnclassifiedServerFailureException ourException; + + @Test + public void testSearch() throws Exception { + + OperationOutcome operationOutcome = new OperationOutcome(); + operationOutcome.addIssue().setCode(IssueType.BUSINESSRULE); + ourException = new UnclassifiedServerFailureException(477, "SOME MESSAGE", operationOutcome); + + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); + CloseableHttpResponse status = ourClient.execute(httpGet); + try { + String responseContent = IOUtils.toString(status.getEntity().getContent()); + ourLog.info(status.getStatusLine().toString()); + ourLog.info(responseContent); + assertEquals(477, status.getStatusLine().getStatusCode()); + //assertEquals("SOME MESSAGE", status.getStatusLine().getReasonPhrase()); + assertThat(responseContent, stringContainsInOrder("business-rule")); + } finally { + IOUtils.closeQuietly(status.getEntity().getContent()); + } + + } + + @AfterClass + public static void afterClassClearContext() throws Exception { + ourServer.stop(); + TestUtil.clearAllStaticFieldsForUnitTest(); + } + + @BeforeClass + public static void beforeClass() throws Exception { + ourPort = PortUtil.findFreePort(); + ourServer = new Server(ourPort); + + DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); + + ServletHandler proxyHandler = new ServletHandler(); + RestfulServer servlet = new RestfulServer(ourCtx); + servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); + + servlet.setResourceProviders(patientProvider); + ServletHolder servletHolder = new ServletHolder(servlet); + proxyHandler.addServletWithMapping(servletHolder, "/*"); + ourServer.setHandler(proxyHandler); + ourServer.start(); + + PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); + HttpClientBuilder builder = HttpClientBuilder.create(); + builder.setConnectionManager(connectionManager); + ourClient = builder.build(); + + } + + public static class DummyPatientResourceProvider implements IResourceProvider { + + @Override + public Class getResourceType() { + return Patient.class; + } + + @Search() + public List search() { + throw ourException; + } + + } + +} diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ValidateDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ValidateDstu3Test.java index 0df8e26b752..f9e57d5d00a 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ValidateDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ValidateDstu3Test.java @@ -30,8 +30,6 @@ import org.junit.BeforeClass; import org.junit.Test; import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.model.api.IResource; -import ca.uhn.fhir.model.base.resource.BaseOperationOutcome; import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.Validate; import ca.uhn.fhir.rest.api.MethodOutcome; diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/validation/SchemaValidationTestDstu3.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/validation/SchemaValidationDstu3Test.java similarity index 86% rename from hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/validation/SchemaValidationTestDstu3.java rename to hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/validation/SchemaValidationDstu3Test.java index dbde0cb424f..f6ad6e7041d 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/validation/SchemaValidationTestDstu3.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/validation/SchemaValidationDstu3Test.java @@ -1,23 +1,20 @@ package ca.uhn.fhir.validation; import static org.hamcrest.Matchers.containsString; -import static org.junit.Assert.*; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; -import java.util.Locale; - -import org.hl7.fhir.dstu3.model.Patient; import org.junit.AfterClass; import org.junit.Test; import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.util.TestUtil; -public class SchemaValidationTestDstu3 { +public class SchemaValidationDstu3Test { private static FhirContext ourCtx = FhirContext.forDstu3(); - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SchemaValidationTestDstu3.class); + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SchemaValidationDstu3Test.class); /** * See #339 diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 374ad48dc35..fb95ab2f05e 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -41,6 +41,16 @@ ]]> + + Remove some clases that were deprecated over a year ago and have + suitable replacements: + +
  • QualifiedDateParam has been removed, but DateParam may be used instead
  • +
  • PathSpecification has been removedm but Include may be used instead
  • + + ]]> +
    ResponseValidatingInterceptor threw an InternalErrorException (HTTP 500) for operations that do not return any content (e.g. delete). Thanks to Mohammad Jafari for reporting! @@ -359,6 +369,18 @@ Server now supports the _at parameter (including multiple repetitions) for history operation + + + AuthorizationInterceptor can now allow or deny requests to extended + operations (e.g. $everything) + DecimalType used BigDecimal constructor instead of valueOf method to create a BigDecimal from a double, resulting in weird floating point