Merge branch 'master' of github.com:jamesagnew/hapi-fhir

This commit is contained in:
James Agnew 2016-06-20 10:53:43 -04:00
commit 89f3df80a4
98 changed files with 2756 additions and 2107 deletions

View File

@ -354,11 +354,6 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
RuntimeChildChoiceDefinition def = new RuntimeChildChoiceDefinition(next, elementName, childAnnotation, descriptionAnnotation, choiceTypes); RuntimeChildChoiceDefinition def = new RuntimeChildChoiceDefinition(next, elementName, childAnnotation, descriptionAnnotation, choiceTypes);
orderMap.put(order, def); 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) { } else if (extensionAttr != null) {
/* /*
* Child is an extension * Child is an extension
@ -427,8 +422,6 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
} else { } else {
def = new RuntimeChildPrimitiveDatatypeDefinition(next, elementName, descriptionAnnotation, childAnnotation, nextDatatype); def = new RuntimeChildPrimitiveDatatypeDefinition(next, elementName, descriptionAnnotation, childAnnotation, nextDatatype);
} }
} else if (IBaseXhtml.class.isAssignableFrom(nextElementType)) {
def = new RuntimeChildXhtmlDatatypeDefinition(next, elementName, descriptionAnnotation, childAnnotation, nextDatatype);
} else { } else {
if (IBoundCodeableConcept.class.isAssignableFrom(nextElementType)) { if (IBoundCodeableConcept.class.isAssignableFrom(nextElementType)) {
IValueSetEnumBinder<Enum<?>> binder = ModelScanner.getBoundCodeBinder(next); IValueSetEnumBinder<Enum<?>> binder = ModelScanner.getBoundCodeBinder(next);

View File

@ -44,6 +44,7 @@ import java.util.Set;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.TreeSet; 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.IAnyResource;
import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseBackboneElement; import org.hl7.fhir.instance.model.api.IBaseBackboneElement;
@ -574,6 +575,8 @@ class ModelScanner {
} }
} catch (IOException e) { } catch (IOException e) {
throw new ConfigurationException("Failed to load model property file from classpath: " + "/ca/uhn/fhir/model/dstu/model.properties"); throw new ConfigurationException("Failed to load model property file from classpath: " + "/ca/uhn/fhir/model/dstu/model.properties");
} finally {
IOUtils.closeQuietly(str);
} }
return retVal; return retVal;

View File

@ -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();
}
}

View File

@ -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<? extends IBase> theDatatype) {
super(theField, theElementName, theDescriptionAnnotation, theChildAnnotation, theDatatype);
}
}

View File

@ -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.
* <code>Patient.gender.coding</code>
* <p>
* 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)}.
* </p>
*
* @deprecated {@link Include} should be used instead
*/
@CoverageIgnore
@Deprecated
public class PathSpecification extends Include {
public PathSpecification(String theInclude) {
super(theInclude);
}
}

View File

@ -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<String, SecurityEventObjectSensitivityEnum> CODE_TO_ENUM = new HashMap<String, SecurityEventObjectSensitivityEnum>();
private static Map<String, Map<String, SecurityEventObjectSensitivityEnum>> SYSTEM_TO_CODE_TO_ENUM = new HashMap<String, Map<String, SecurityEventObjectSensitivityEnum>>();
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<String, SecurityEventObjectSensitivityEnum>());
}
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<SecurityEventObjectSensitivityEnum> VALUESET_BINDER = new IValueSetEnumBinder<SecurityEventObjectSensitivityEnum>() {
@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<String, SecurityEventObjectSensitivityEnum> 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;
}
}

View File

@ -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: <b>1</b>
*
* Person.
*/
PERSON("1", "http://hl7.org/fhir/object-type"),
/**
* Code Value: <b>2</b>
*
* System Object.
*/
SYSTEM_OBJECT("2", "http://hl7.org/fhir/object-type"),
/**
* Code Value: <b>3</b>
*
* Organization.
*/
ORGANIZATION("3", "http://hl7.org/fhir/object-type"),
/**
* Code Value: <b>4</b>
*
* 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<String, SecurityEventObjectTypeEnum> CODE_TO_ENUM = new HashMap<String, SecurityEventObjectTypeEnum>();
private static Map<String, Map<String, SecurityEventObjectTypeEnum>> SYSTEM_TO_CODE_TO_ENUM = new HashMap<String, Map<String, SecurityEventObjectTypeEnum>>();
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<String, SecurityEventObjectTypeEnum>());
}
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<SecurityEventObjectTypeEnum> VALUESET_BINDER = new IValueSetEnumBinder<SecurityEventObjectTypeEnum>() {
@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<String, SecurityEventObjectTypeEnum> 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;
}
}

View File

@ -52,6 +52,7 @@ public class TimeDt extends StringDt implements IQueryParameterType {
*/ */
@SimpleSetter @SimpleSetter
public TimeDt(@SimpleSetter.Parameter(name = "theString") String theValue) { public TimeDt(@SimpleSetter.Parameter(name = "theString") String theValue) {
this();
setValue(theValue); setValue(theValue);
} }

View File

@ -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;
}
}

View File

@ -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 doEncodeBundleToWriter(Bundle theBundle, Writer theWriter) throws IOException, DataFormatException;
protected abstract void doEncodeResourceToWriter(IBaseResource theResource, Writer theWriter) throws IOException, DataFormatException; protected abstract void doEncodeResourceToWriter(IBaseResource theResource, Writer theWriter) throws IOException, DataFormatException;

View File

@ -70,6 +70,13 @@ import ca.uhn.fhir.util.OperationOutcomeUtil;
public abstract class BaseClient implements IRestfulClient { 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 static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseClient.class);
private final IHttpClient myClient; private final IHttpClient myClient;
@ -89,6 +96,15 @@ public abstract class BaseClient implements IRestfulClient {
myClient = theClient; myClient = theClient;
myUrlBase = theUrlBase; myUrlBase = theUrlBase;
myFactory = theFactory; 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<String, List<String>> createExtraParams() { protected Map<String, List<String>> createExtraParams() {
@ -193,7 +209,7 @@ public abstract class BaseClient implements IRestfulClient {
// TODO: handle non 2xx status codes by throwing the correct exception, // TODO: handle non 2xx status codes by throwing the correct exception,
// and ensure it's passed upwards // and ensure it's passed upwards
IHttpRequest httpRequest; IHttpRequest httpRequest = null;
IHttpResponse response = null; IHttpResponse response = null;
try { try {
Map<String, List<String>> params = createExtraParams(); Map<String, List<String>> params = createExtraParams();
@ -339,11 +355,13 @@ public abstract class BaseClient implements IRestfulClient {
} }
} catch (DataFormatException e) { } 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) { } catch (IllegalStateException e) {
throw new FhirClientConnectionException(e); throw new FhirClientConnectionException(e);
} catch (IOException 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) { } catch (RuntimeException e) {
throw e; throw e;
} catch (Exception e) { } catch (Exception e) {

View File

@ -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.IHttpClient;
import ca.uhn.fhir.rest.client.api.IHttpRequest; import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException; 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.IClientExecutable;
import ca.uhn.fhir.rest.gclient.ICreate; import ca.uhn.fhir.rest.gclient.ICreate;
import ca.uhn.fhir.rest.gclient.ICreateTyped; 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.SortParameter;
import ca.uhn.fhir.rest.method.TransactionMethodBinding; import ca.uhn.fhir.rest.method.TransactionMethodBinding;
import ca.uhn.fhir.rest.method.ValidateMethodBindingDstu1; 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.DateParam;
import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.param.TokenParam;
@ -174,8 +175,8 @@ public class GenericClient extends BaseClient implements IGenericClient {
@Override @Override
public IBaseConformance conformance() { public IBaseConformance conformance() {
if (myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU2_HL7ORG)) { if (myContext.getVersion().getVersion().isRi()) {
throw new IllegalArgumentException("Must call fetchConformance() instead of conformance() for RI/DSTU3+ structures"); throw new IllegalArgumentException("Must call fetchConformance() instead of conformance() for RI/STU3+ structures");
} }
HttpGetClientInvocation invocation = MethodUtil.createConformanceInvocation(getFhirContext()); 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); // 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() { public boolean isLogRequestAndResponse() {
return myLogRequestAndResponse; return myLogRequestAndResponse;
} }
@ -549,7 +556,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
if (myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU1)) { if (myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU1)) {
invocation = ValidateMethodBindingDstu1.createValidateInvocation(theResource, null, myContext); invocation = ValidateMethodBindingDstu1.createValidateInvocation(theResource, null, myContext);
} else { } else {
invocation = ValidateMethodBindingDstu2.createValidateInvocation(myContext, theResource); invocation = ValidateMethodBindingDstu2Plus.createValidateInvocation(myContext, theResource);
} }
if (isKeepResponses()) { if (isKeepResponses()) {
@ -714,7 +721,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
protected IBaseResource parseResourceBody(String theResourceBody) { protected IBaseResource parseResourceBody(String theResourceBody) {
EncodingEnum encoding = MethodUtil.detectEncodingNoDefault(theResourceBody); EncodingEnum encoding = MethodUtil.detectEncodingNoDefault(theResourceBody);
if (encoding == null) { 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); return encoding.newParser(myContext).parseResource(theResourceBody);
} }
@ -2213,7 +2220,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
myRawBundle = theBundle; myRawBundle = theBundle;
myRawBundleEncoding = MethodUtil.detectEncodingNoDefault(myRawBundle); myRawBundleEncoding = MethodUtil.detectEncodingNoDefault(myRawBundle);
if (myRawBundleEncoding == null) { 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 @Override
public MethodOutcome execute() { public MethodOutcome execute() {
BaseHttpClientInvocation invocation = ValidateMethodBindingDstu2.createValidateInvocation(myContext, myResource); BaseHttpClientInvocation invocation = ValidateMethodBindingDstu2Plus.createValidateInvocation(myContext, myResource);
ResourceResponseHandler<BaseOperationOutcome> handler = new ResourceResponseHandler<BaseOperationOutcome>(null, null); ResourceResponseHandler<BaseOperationOutcome> handler = new ResourceResponseHandler<BaseOperationOutcome>(null, null);
IBaseOperationOutcome outcome = invoke(null, handler, invocation); IBaseOperationOutcome outcome = invoke(null, handler, invocation);
MethodOutcome retVal = new MethodOutcome(); MethodOutcome retVal = new MethodOutcome();
@ -2424,7 +2431,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
EncodingEnum enc = MethodUtil.detectEncodingNoDefault(theResourceRaw); EncodingEnum enc = MethodUtil.detectEncodingNoDefault(theResourceRaw);
if (enc == null) { 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) { switch (enc) {
case XML: case XML:

View File

@ -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.api.IRestfulClient;
import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException; import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException;
import ca.uhn.fhir.rest.client.exceptions.FhirClientInappropriateForServerException; 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.ICreate;
import ca.uhn.fhir.rest.gclient.IDelete; import ca.uhn.fhir.rest.gclient.IDelete;
import ca.uhn.fhir.rest.gclient.IFetchConformanceUntyped; import ca.uhn.fhir.rest.gclient.IFetchConformanceUntyped;
@ -288,7 +289,11 @@ public interface IGenericClient extends IRestfulClient {
* *
* @param theLogRequestAndResponse * @param theLogRequestAndResponse
* Should requests and responses be logged * 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); void setLogRequestAndResponse(boolean theLogRequestAndResponse);
/** /**

View File

@ -194,7 +194,7 @@ public abstract class RestfulClientFactory implements IRestfulClientFactory {
public void validateServerBaseIfConfiguredToDoSo(String theServerBase, IHttpClient theHttpClient, BaseClient theClient) { public void validateServerBaseIfConfiguredToDoSo(String theServerBase, IHttpClient theHttpClient, BaseClient theClient) {
String serverBase = normalizeBaseUrlForMap(theServerBase); String serverBase = normalizeBaseUrlForMap(theServerBase);
switch (myServerValidationMode) { switch (getServerValidationMode()) {
case NEVER: case NEVER:
break; break;
case ONCE: case ONCE:
@ -267,22 +267,6 @@ public abstract class RestfulClientFactory implements IRestfulClientFactory {
myPoolMaxPerRoute = thePoolMaxPerRoute; myPoolMaxPerRoute = thePoolMaxPerRoute;
resetHttpClient(); 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<Method, Object> theMethodToReturnValue, Map<Method, BaseMethodBinding<?>> theBindings, Map<Method, ClientInvocationHandlerFactory.ILambda> theMethodToLambda) {
return new ClientInvocationHandler(theClient, getFhirContext(), theUrlBase.toString(), theMethodToReturnValue,
theBindings, theMethodToLambda, this);
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
@ -300,7 +284,8 @@ public abstract class RestfulClientFactory implements IRestfulClientFactory {
Class implementingClass = myContext.getResourceDefinition("Conformance").getImplementingClass(); Class implementingClass = myContext.getResourceDefinition("Conformance").getImplementingClass();
conformance = (IBaseResource) client.fetchConformance().ofType(implementingClass).execute(); conformance = (IBaseResource) client.fetchConformance().ofType(implementingClass).execute();
} catch (FhirClientConnectionException e) { } 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(); FhirTerser t = myContext.newTerser();

View File

@ -44,8 +44,8 @@ import ca.uhn.fhir.rest.client.api.IHttpResponse;
*/ */
public class ApacheHttpRequest implements IHttpRequest { public class ApacheHttpRequest implements IHttpRequest {
private HttpRequestBase myRequest;
private HttpClient myClient; private HttpClient myClient;
private HttpRequestBase myRequest;
public ApacheHttpRequest(HttpClient theClient, HttpRequestBase theApacheRequest) { public ApacheHttpRequest(HttpClient theClient, HttpRequestBase theApacheRequest) {
this.myClient = theClient; this.myClient = theClient;
@ -57,14 +57,6 @@ public class ApacheHttpRequest implements IHttpRequest {
myRequest.addHeader(theName, theValue); myRequest.addHeader(theName, theValue);
} }
/**
* Get the ApacheRequest
* @return the ApacheRequest
*/
public HttpRequestBase getApacheRequest() {
return myRequest;
}
@Override @Override
public IHttpResponse execute() throws IOException { public IHttpResponse execute() throws IOException {
return new ApacheHttpResponse(myClient.execute(myRequest)); return new ApacheHttpResponse(myClient.execute(myRequest));
@ -82,9 +74,17 @@ public class ApacheHttpRequest implements IHttpRequest {
return result; return result;
} }
/**
* Get the ApacheRequest
* @return the ApacheRequest
*/
public HttpRequestBase getApacheRequest() {
return myRequest;
}
@Override @Override
public String toString() { public String getHttpVerbName() {
return myRequest.toString(); return myRequest.getMethod();
} }
@Override @Override
@ -98,4 +98,14 @@ public class ApacheHttpRequest implements IHttpRequest {
return null; return null;
} }
@Override
public String getUri() {
return myRequest.getURI().toString();
}
@Override
public String toString() {
return myRequest.toString();
}
} }

View File

@ -57,6 +57,7 @@ public class ApacheRestfulClientFactory extends RestfulClientFactory {
* Constructor * Constructor
*/ */
public ApacheRestfulClientFactory() { public ApacheRestfulClientFactory() {
super();
} }
/** /**

View File

@ -56,4 +56,14 @@ public interface IHttpRequest {
*/ */
public String getRequestBodyFromStream() throws IOException; public String getRequestBodyFromStream() throws IOException;
/**
* Return the request URI, or null
*/
public String getUri();
/**
* Return the HTTP verb (e.g. "GET")
*/
public String getHttpVerbName();
} }

View File

@ -25,6 +25,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IBaseBinary; import org.hl7.fhir.instance.model.api.IBaseBinary;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
@ -200,9 +201,7 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
if (myParams != null) { if (myParams != null) {
return httpClient.createParamRequest(getContext(), myParams, encoding); return httpClient.createParamRequest(getContext(), myParams, encoding);
} else { } else {
if (encoding == null) { encoding = ObjectUtils.defaultIfNull(encoding, EncodingEnum.XML);
encoding = EncodingEnum.XML;
}
String contents = encodeContents(thePrettyPrint, encoding); String contents = encodeContents(thePrettyPrint, encoding);
String contentType = getContentType(encoding); String contentType = getContentType(encoding);
return httpClient.createByteRequest(getContext(), contents, contentType, encoding); return httpClient.createByteRequest(getContext(), contents, contentType, encoding);

View File

@ -516,7 +516,7 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
if (theContext.getVersion().getVersion() == FhirVersionEnum.DSTU1) { if (theContext.getVersion().getVersion() == FhirVersionEnum.DSTU1) {
return new ValidateMethodBindingDstu1(theMethod, theContext, theProvider); return new ValidateMethodBindingDstu1(theMethod, theContext, theProvider);
} else { } else {
return new ValidateMethodBindingDstu2(returnType, returnTypeFromRp, theMethod, theContext, theProvider, validate); return new ValidateMethodBindingDstu2Plus(returnType, returnTypeFromRp, theMethod, theContext, theProvider, validate);
} }
} else if (getTags != null) { } else if (getTags != null) {
return new GetTagsMethodBinding(theMethod, theContext, theProvider, getTags); return new GetTagsMethodBinding(theMethod, theContext, theProvider, getTags);

View File

@ -253,7 +253,7 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
boolean prettyPrint = RestfulServerUtils.prettyPrintResponse(theServer, theRequest); boolean prettyPrint = RestfulServerUtils.prettyPrintResponse(theServer, theRequest);
Set<SummaryEnum> summaryMode = Collections.emptySet(); Set<SummaryEnum> 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()); // return theRequest.getResponse().returnResponse(ParseAction.create(outcome), operationStatus, allowPrefer, response, getResourceName());
} }

View File

@ -36,6 +36,7 @@ import java.util.Set;
import java.util.TreeSet; import java.util.TreeSet;
import org.hl7.fhir.instance.model.api.IBaseBundle; 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.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType; 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.Bundle;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.Include; 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.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.MethodOutcome;
@ -232,7 +232,7 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
return resource; return resource;
case METHOD_OUTCOME: case METHOD_OUTCOME:
MethodOutcome retVal = new MethodOutcome(); MethodOutcome retVal = new MethodOutcome();
retVal.setOperationOutcome((BaseOperationOutcome) resource); retVal.setOperationOutcome((IBaseOperationOutcome) resource);
return retVal; return retVal;
} }
break; break;
@ -260,8 +260,8 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
boolean prettyPrint = RestfulServerUtils.prettyPrintResponse(theServer, theRequest); boolean prettyPrint = RestfulServerUtils.prettyPrintResponse(theServer, theRequest);
return theRequest.getResponse().streamResponseAsResource(responseObject.getResource(), prettyPrint, summaryMode, Constants.STATUS_HTTP_200_OK, theRequest.isRespondGzip(), return theRequest.getResponse().streamResponseAsResource(responseObject.getResource(), prettyPrint, summaryMode, Constants.STATUS_HTTP_200_OK, null,
isAddContentLocationHeader()); theRequest.isRespondGzip(), isAddContentLocationHeader());
} else { } else {
// Is this request coming from a browser // Is this request coming from a browser

View File

@ -31,7 +31,6 @@ import java.util.TreeSet;
import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Include; 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.annotation.IncludeParam;
import ca.uhn.fhir.rest.param.BaseQueryParameter; import ca.uhn.fhir.rest.param.BaseQueryParameter;
import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.Constants;
@ -60,7 +59,7 @@ class IncludeParameter extends BaseQueryParameter {
} }
mySpecType = theSpecType; 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); throw new ConfigurationException("Invalid @" + IncludeParam.class.getSimpleName() + " parameter type: " + mySpecType);
} }
@ -72,7 +71,7 @@ class IncludeParameter extends BaseQueryParameter {
ArrayList<QualifiedParamList> retVal = new ArrayList<QualifiedParamList>(); ArrayList<QualifiedParamList> retVal = new ArrayList<QualifiedParamList>();
if (myInstantiableCollectionType == null) { if (myInstantiableCollectionType == null) {
if (mySpecType == Include.class || mySpecType == PathSpecification.class) { if (mySpecType == Include.class) {
convertAndAddIncludeToList(retVal, (Include) theObject); convertAndAddIncludeToList(retVal, (Include) theObject);
} else { } else {
retVal.add(QualifiedParamList.singleton(((String) theObject))); retVal.add(QualifiedParamList.singleton(((String) theObject)));

View File

@ -6,10 +6,8 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.IOException; import java.io.IOException;
import java.io.PushbackReader; import java.io.PushbackReader;
import java.io.Reader; import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@ -23,6 +21,7 @@ import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseMetaType; 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.IQueryParameterType;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.Include; 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.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.Tag; import ca.uhn.fhir.model.api.Tag;
import ca.uhn.fhir.model.api.TagList; import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.api.annotation.Description; import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.annotation.At; import ca.uhn.fhir.rest.annotation.At;
import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; 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.IDynamicSearchResourceProvider;
import ca.uhn.fhir.rest.server.SearchParameterMap; import ca.uhn.fhir.rest.server.SearchParameterMap;
import ca.uhn.fhir.util.DateUtils; import ca.uhn.fhir.util.DateUtils;
import ca.uhn.fhir.util.ParametersUtil;
import ca.uhn.fhir.util.ReflectionUtil; import ca.uhn.fhir.util.ReflectionUtil;
import ca.uhn.fhir.util.UrlUtil;
/* /*
* #%L * #%L
@ -114,6 +113,12 @@ import ca.uhn.fhir.util.ReflectionUtil;
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public class MethodUtil { public class MethodUtil {
/** Non instantiable */
private MethodUtil() {
// nothing
}
private static final String LABEL = "label=\""; private static final String LABEL = "label=\"";
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(MethodUtil.class); 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<? extends IIdType> idParamType) { @SuppressWarnings("unchecked")
if (value != null && !idParamType.isAssignableFrom(value.getClass())) { public static <T extends IIdType> T convertIdToType(IIdType value, Class<T> theIdParamType) {
try { if (value != null && !theIdParamType.isAssignableFrom(value.getClass())) {
IIdType newValue = idParamType.newInstance(); IIdType newValue = ReflectionUtil.newInstance(theIdParamType);
newValue.setValue(value.getValue()); newValue.setValue(value.getValue());
value = newValue; value = newValue;
} catch (InstantiationException e) {
throw new ConfigurationException("Failed to instantiate " + idParamType, e);
} catch (IllegalAccessException e) {
throw new ConfigurationException("Failed to instantiate " + idParamType, e);
}
} }
return value; return (T) value;
} }
public static HttpGetClientInvocation createConformanceInvocation(FhirContext theContext) { public static HttpGetClientInvocation createConformanceInvocation(FhirContext theContext) {
@ -213,13 +213,9 @@ public class MethodUtil {
for (String nextValue : nextEntry.getValue()) { for (String nextValue : nextEntry.getValue()) {
b.append(haveQuestionMark ? '&' : '?'); b.append(haveQuestionMark ? '&' : '?');
haveQuestionMark = true; haveQuestionMark = true;
try { b.append(UrlUtil.escape(nextEntry.getKey()));
b.append(URLEncoder.encode(nextEntry.getKey(), "UTF-8")); b.append('=');
b.append('='); b.append(UrlUtil.escape(nextValue));
b.append(URLEncoder.encode(nextValue, "UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new ConfigurationException("UTF-8 not supported on this platform");
}
} }
} }
@ -288,9 +284,7 @@ public class MethodUtil {
public static EncodingEnum detectEncoding(String theBody) { public static EncodingEnum detectEncoding(String theBody) {
EncodingEnum retVal = detectEncodingNoDefault(theBody); EncodingEnum retVal = detectEncodingNoDefault(theBody);
if (retVal == null) { retVal = ObjectUtils.defaultIfNull(retVal, EncodingEnum.XML);
retVal = EncodingEnum.XML;
}
return retVal; 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) { public static Integer findIdParameterIndex(Method theMethod, FhirContext theContext) {
Integer index = MethodUtil.findParamAnnotationIndex(theMethod, IdParam.class); Integer index = MethodUtil.findParamAnnotationIndex(theMethod, IdParam.class);
if (index != null) { if (index != null) {
@ -366,7 +356,7 @@ public class MethodUtil {
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static List<IParameter> getResourceParameters(FhirContext theContext, Method theMethod, Object theProvider, RestOperationTypeEnum theRestfulOperationTypeEnum) { public static List<IParameter> getResourceParameters(final FhirContext theContext, Method theMethod, Object theProvider, RestOperationTypeEnum theRestfulOperationTypeEnum) {
List<IParameter> parameters = new ArrayList<IParameter>(); List<IParameter> parameters = new ArrayList<IParameter>();
Class<?>[] parameterTypes = theMethod.getParameterTypes(); Class<?>[] parameterTypes = theMethod.getParameterTypes();
@ -442,7 +432,7 @@ public class MethodUtil {
if (parameterType == String.class) { if (parameterType == String.class) {
instantiableCollectionType = null; instantiableCollectionType = null;
specType = String.class; 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() + ">"); throw new ConfigurationException("Method '" + theMethod.getName() + "' is annotated with @" + IncludeParam.class.getSimpleName() + " but has a type other than Collection<" + Include.class.getSimpleName() + ">");
} else { } else {
instantiableCollectionType = (Class<? extends Collection<Include>>) CollectionBinder.getInstantiableCollectionType(innerCollectionType, "Method '" + theMethod.getName() + "'"); instantiableCollectionType = (Class<? extends Collection<Include>>) CollectionBinder.getInstantiableCollectionType(innerCollectionType, "Method '" + theMethod.getName() + "'");
@ -515,7 +505,7 @@ public class MethodUtil {
@Override @Override
public Object outgoingClient(Object theObject) { public Object outgoingClient(Object theObject) {
return new StringDt(((ValidationModeEnum)theObject).getCode()); return ParametersUtil.createString(theContext, ((ValidationModeEnum)theObject).getCode());
} }
}); });
} else if (nextAnnotation instanceof Validate.Profile) { } else if (nextAnnotation instanceof Validate.Profile) {
@ -530,7 +520,7 @@ public class MethodUtil {
@Override @Override
public Object outgoingClient(Object theObject) { public Object outgoingClient(Object theObject) {
return new StringDt(theObject.toString()); return ParametersUtil.createString(theContext, theObject.toString());
} }
}); });
} else { } else {

View File

@ -59,7 +59,6 @@ import ca.uhn.fhir.rest.param.HasParam;
import ca.uhn.fhir.rest.param.NumberAndListParam; import ca.uhn.fhir.rest.param.NumberAndListParam;
import ca.uhn.fhir.rest.param.NumberOrListParam; import ca.uhn.fhir.rest.param.NumberOrListParam;
import ca.uhn.fhir.rest.param.NumberParam; 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.QuantityAndListParam;
import ca.uhn.fhir.rest.param.QuantityOrListParam; import ca.uhn.fhir.rest.param.QuantityOrListParam;
import ca.uhn.fhir.rest.param.QuantityParam; 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.CollectionUtil;
import ca.uhn.fhir.util.ReflectionUtil; import ca.uhn.fhir.util.ReflectionUtil;
@SuppressWarnings("deprecation")
public class SearchParameter extends BaseQueryParameter { public class SearchParameter extends BaseQueryParameter {
private static final String EMPTY_STRING = ""; private static final String EMPTY_STRING = "";
@ -341,8 +339,6 @@ public class SearchParameter extends BaseQueryParameter {
// ok // ok
} else if (StringDt.class.isAssignableFrom(type)) { } else if (StringDt.class.isAssignableFrom(type)) {
myParamType = RestSearchParameterTypeEnum.STRING; myParamType = RestSearchParameterTypeEnum.STRING;
} else if (QualifiedDateParam.class.isAssignableFrom(type)) {
myParamType = RestSearchParameterTypeEnum.DATE;
} else if (BaseIdentifierDt.class.isAssignableFrom(type)) { } else if (BaseIdentifierDt.class.isAssignableFrom(type)) {
myParamType = RestSearchParameterTypeEnum.TOKEN; myParamType = RestSearchParameterTypeEnum.TOKEN;
} else if (BaseQuantityDt.class.isAssignableFrom(type)) { } else if (BaseQuantityDt.class.isAssignableFrom(type)) {

View File

@ -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<String, String> getQueryParams(String query) {
try {
Map<String, String> params = new HashMap<String, String>();
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);
}
}
}

View File

@ -37,9 +37,9 @@ import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.util.ParametersUtil; import ca.uhn.fhir.util.ParametersUtil;
public class ValidateMethodBindingDstu2 extends OperationMethodBinding { public class ValidateMethodBindingDstu2Plus extends OperationMethodBinding {
public ValidateMethodBindingDstu2(Class<?> theReturnResourceType, Class<? extends IBaseResource> theReturnTypeFromRp, Method theMethod, FhirContext theContext, Object theProvider, public ValidateMethodBindingDstu2Plus(Class<?> theReturnResourceType, Class<? extends IBaseResource> theReturnTypeFromRp, Method theMethod, FhirContext theContext, Object theProvider,
Validate theAnnotation) { Validate theAnnotation) {
super(theReturnResourceType, theReturnTypeFromRp, theMethod, theContext, theProvider, true, Constants.EXTOP_VALIDATE, theAnnotation.type(), new OperationParam[0], BundleTypeEnum.COLLECTION); super(theReturnResourceType, theReturnTypeFromRp, theMethod, theContext, theProvider, true, Constants.EXTOP_VALIDATE, theAnnotation.type(), new OperationParam[0], BundleTypeEnum.COLLECTION);

View File

@ -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<? extends IBaseResource> 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<IParameter> newParams = new ArrayList<IParameter>();
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;
}
}

View File

@ -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);
}
}

View File

@ -38,7 +38,7 @@ import ca.uhn.fhir.rest.method.ParseAction;
public interface IRestfulResponse { public interface IRestfulResponse {
Object streamResponseAsResource(IBaseResource theActualResourceToReturn, boolean prettyPrint, Set<SummaryEnum> summaryMode, int operationStatus, boolean respondGzip, boolean addContentLocationHeader) throws IOException; Object streamResponseAsResource(IBaseResource theActualResourceToReturn, boolean thePrettyPrint, Set<SummaryEnum> theSummaryMode, int theStatusCode, String theStatusMessage, boolean theRespondGzip, boolean theAddContentLocation) throws IOException;
Object streamResponseAsBundle(Bundle bundle, Set<SummaryEnum> summaryMode, boolean respondGzip, boolean requestIsBrowser) throws IOException; Object streamResponseAsBundle(Bundle bundle, Set<SummaryEnum> 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; 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; Object sendWriterResponse(int status, String contentType, String charset, Writer writer) throws IOException;

View File

@ -90,10 +90,10 @@ public abstract class RestfulResponse<T extends RequestDetails> implements IRest
} }
@Override @Override
public final Object streamResponseAsResource(IBaseResource resource, boolean prettyPrint, Set<SummaryEnum> summaryMode, public final Object streamResponseAsResource(IBaseResource theResource, boolean thePrettyPrint, Set<SummaryEnum> theSummaryMode,
int statusCode, boolean respondGzip, boolean addContentLocationHeader) int theStatusCode, String theStatusMessage, boolean theRespondGzip, boolean theAddContentLocation)
throws IOException { 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);
} }

View File

@ -537,7 +537,7 @@ public class RestfulServerUtils {
String contentType = responseEncoding.getBundleContentType(); String contentType = responseEncoding.getBundleContentType();
String charset = Constants.CHARSET_NAME_UTF8; 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 { try {
IParser parser = RestfulServerUtils.getNewParser(theServer.getFhirContext(), theRequestDetails); IParser parser = RestfulServerUtils.getNewParser(theServer.getFhirContext(), theRequestDetails);
@ -552,11 +552,11 @@ public class RestfulServerUtils {
} }
public static Object streamResponseAsResource(IRestfulServerDefaults theServer, IBaseResource theResource, Set<SummaryEnum> theSummaryMode, int stausCode, boolean theAddContentLocationHeader, boolean respondGzip, RequestDetails theRequestDetails) throws IOException { public static Object streamResponseAsResource(IRestfulServerDefaults theServer, IBaseResource theResource, Set<SummaryEnum> 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<SummaryEnum> theSummaryMode, int stausCode, boolean theAddContentLocationHeader, boolean respondGzip, RequestDetails theRequestDetails, IIdType theOperationResourceId, IPrimitiveType<Date> theOperationResourceLastUpdated) throws IOException { public static Object streamResponseAsResource(IRestfulServerDefaults theServer, IBaseResource theResource, Set<SummaryEnum> theSummaryMode, int theStausCode, String theStatusMessage, boolean theAddContentLocationHeader, boolean respondGzip, RequestDetails theRequestDetails, IIdType theOperationResourceId, IPrimitiveType<Date> theOperationResourceLastUpdated) throws IOException {
IRestfulResponse restUtil = theRequestDetails.getResponse(); IRestfulResponse restUtil = theRequestDetails.getResponse();
// Determine response encoding // Determine response encoding
@ -596,7 +596,7 @@ public class RestfulServerUtils {
// malicious images or HTML blocks being served up as content. // malicious images or HTML blocks being served up as content.
restUtil.addHeader(Constants.HEADER_CONTENT_DISPOSITION, "Attachment;"); 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 // 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; 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) { if (theResource == null) {
// No response is being returned // No response is being returned
} else if (encodingDomainResourceAsText && theResource instanceof IResource) { } else if (encodingDomainResourceAsText && theResource instanceof IResource) {
@ -666,7 +666,7 @@ public class RestfulServerUtils {
parser.encodeResourceToWriter(theResource, writer); 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) { public static IIdType fullyQualifyResourceIdOrReturnNull(IRestfulServerDefaults theServer, IBaseResource theResource, String theServerBase, IIdType theResourceId) {

View File

@ -39,6 +39,7 @@ import ca.uhn.fhir.rest.method.RequestDetails;
import ca.uhn.fhir.rest.server.IRestfulResponse; import ca.uhn.fhir.rest.server.IRestfulResponse;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException;
import ca.uhn.fhir.util.OperationOutcomeUtil; import ca.uhn.fhir.util.OperationOutcomeUtil;
public class ExceptionHandlingInterceptor extends InterceptorAdapter { 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); // theResponse.setStatus(statusCode);
// theRequestDetails.getServer().addHeadersToResponse(theResponse); // theRequestDetails.getServer().addHeadersToResponse(theResponse);
// theResponse.setContentType("text/plain"); // theResponse.setContentType("text/plain");

View File

@ -340,19 +340,21 @@ public class ResponseHighlighterInterceptor extends InterceptorAdapter {
b.append("<p>"); b.append("<p>");
b.append("This result is being rendered in HTML for easy viewing. "); 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("<a href=\""); b.append("<a href=\"");
b.append(createLinkHref(parameters, Constants.FORMAT_JSON)); b.append(createLinkHref(parameters, Constants.FORMAT_JSON));
b.append("\">Raw JSON</a>, "); b.append("\">Raw JSON</a> or ");
b.append("<a href=\""); b.append("<a href=\"");
b.append(createLinkHref(parameters, Constants.FORMAT_XML)); b.append(createLinkHref(parameters, Constants.FORMAT_XML));
b.append("\">Raw XML</a>, "); b.append("\">Raw XML</a>, ");
b.append(" or view this content in ");
b.append("<a href=\""); b.append("<a href=\"");
b.append(createLinkHref(parameters, Constants.FORMATS_HTML_JSON)); b.append(createLinkHref(parameters, Constants.FORMATS_HTML_JSON));
b.append("\">HTML JSON</a>, "); b.append("\">HTML JSON</a> ");
b.append("or "); b.append("or ");
b.append("<a href=\""); b.append("<a href=\"");

View File

@ -0,0 +1,30 @@
package ca.uhn.fhir.rest.server.interceptor.auth;
import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor.Verdict;
abstract class BaseRule implements IAuthRule {
private String myName;
private PolicyEnum myMode;
BaseRule(String theRuleName) {
myName = theRuleName;
}
@Override
public String getName() {
return myName;
}
public void setMode(PolicyEnum theRuleMode) {
myMode = theRuleMode;
}
Verdict newVerdict() {
return new Verdict(myMode, this);
}
public PolicyEnum getMode() {
return myMode;
}
}

View File

@ -0,0 +1,17 @@
package ca.uhn.fhir.rest.server.interceptor.auth;
public interface IAuthRuleBuilderOperation {
/**
* This rule applies to the operation with the given name
*
* @param The operation name, e.g. "validate" or "$validate" (either form may be used here)
*/
IAuthRuleBuilderOperationNamed named(String theOperationName);
/**
* This rule applies to any operation
*/
IAuthRuleBuilderOperationNamed withAnyName();
}

View File

@ -0,0 +1,23 @@
package ca.uhn.fhir.rest.server.interceptor.auth;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
public interface IAuthRuleBuilderOperationNamed {
/**
* Rule applies to invocations of this operation at the <code>server</code> level
*/
IAuthRuleBuilderRuleOpClassifierFinished onServer();
/**
* Rule applies to invocations of this operation at the <code>type</code> level
*/
IAuthRuleBuilderRuleOpClassifierFinished onType(Class<? extends IBaseResource> theType);
/**
* Rule applies to invocations of this operation at the <code>instance</code> level
*/
IAuthRuleBuilderRuleOpClassifierFinished onInstance(IIdType theInstanceId);
}

View File

@ -55,4 +55,9 @@ public interface IAuthRuleBuilderRule {
*/ */
IAuthRuleBuilderRuleOp write(); IAuthRuleBuilderRuleOp write();
/**
* This rule applies to a FHIR operation (e.g. <code>$validate</code>)
*/
IAuthRuleBuilderOperation operation();
} }

View File

@ -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<Class<? extends IBaseResource>> myAppliesToTypes;
private List<IIdType> 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<? extends IBaseResource> 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<Class<? extends IBaseResource>> theAppliesToTypes) {
myAppliesToTypes = theAppliesToTypes;
}
public void appliesToInstances(List<IIdType> theAppliesToIds) {
myAppliesToIds = theAppliesToIds;
}
}

View File

@ -24,7 +24,6 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Set; 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.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; 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.BundleUtil.BundleEntryParts;
import ca.uhn.fhir.util.FhirTerser; import ca.uhn.fhir.util.FhirTerser;
class Rule implements IAuthRule { class Rule extends BaseRule implements IAuthRule {
private AppliesTypeEnum myAppliesTo; private AppliesTypeEnum myAppliesTo;
private Set<?> myAppliesToTypes; private Set<?> myAppliesToTypes;
private String myClassifierCompartmentName; private String myClassifierCompartmentName;
private Collection<? extends IIdType> myClassifierCompartmentOwners; private Collection<? extends IIdType> myClassifierCompartmentOwners;
private ClassifierTypeEnum myClassifierType; private ClassifierTypeEnum myClassifierType;
private PolicyEnum myMode;
private String myName;
private RuleOpEnum myOp; private RuleOpEnum myOp;
private TransactionAppliesToEnum myTransactionAppliesToOp; private TransactionAppliesToEnum myTransactionAppliesToOp;
public Rule(String theRuleName) { public Rule(String theRuleName) {
myName = theRuleName; super(theRuleName);
} }
@Override @Override
@ -77,7 +74,7 @@ class Rule implements IAuthRule {
case DELETE: case DELETE:
if (theOperation == RestOperationTypeEnum.DELETE) { if (theOperation == RestOperationTypeEnum.DELETE) {
if (theInputResource == null) { if (theInputResource == null) {
return new Verdict(myMode, this); return newVerdict();
} else { } else {
appliesTo = theInputResource; appliesTo = theInputResource;
} }
@ -88,13 +85,13 @@ class Rule implements IAuthRule {
case BATCH: case BATCH:
case TRANSACTION: case TRANSACTION:
if (theInputResource != null && requestAppliesToTransaction(ctx, myOp, theInputResource)) { if (theInputResource != null && requestAppliesToTransaction(ctx, myOp, theInputResource)) {
if (myMode == PolicyEnum.DENY) { if (getMode() == PolicyEnum.DENY) {
return new Verdict(PolicyEnum.DENY, this); return new Verdict(PolicyEnum.DENY, this);
} else { } else {
List<BundleEntryParts> inputResources = BundleUtil.toListOfEntries(ctx, (IBaseBundle) theInputResource); List<BundleEntryParts> inputResources = BundleUtil.toListOfEntries(ctx, (IBaseBundle) theInputResource);
Verdict verdict = null; Verdict verdict = null;
for (BundleEntryParts nextPart : inputResources) { for (BundleEntryParts nextPart : inputResources) {
IBaseResource inputResource = nextPart.getResource(); IBaseResource inputResource = nextPart.getResource();
RestOperationTypeEnum operation = null; RestOperationTypeEnum operation = null;
if (nextPart.getRequestType() == RequestTypeEnum.GET) { if (nextPart.getRequestType() == RequestTypeEnum.GET) {
@ -111,13 +108,13 @@ class Rule implements IAuthRule {
/* /*
* This is basically just being conservative - Be careful of transactions containing * This is basically just being conservative - Be careful of transactions containing
* nested operations and nested transactions. We block the by default. At some point * 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()); RuntimeResourceDefinition resourceDef = ctx.getResourceDefinition(nextPart.getResource());
if ("Parameters".equals(resourceDef.getName()) || "Bundle".equals(resourceDef.getName())) { 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); Verdict newVerdict = theRuleApplier.applyRulesAndReturnDecision(operation, theRequestDetails, inputResource, null);
if (newVerdict == null) { if (newVerdict == null) {
continue; continue;
@ -155,7 +152,7 @@ class Rule implements IAuthRule {
return new Verdict(PolicyEnum.DENY, this); return new Verdict(PolicyEnum.DENY, this);
case METADATA: case METADATA:
if (theOperation == RestOperationTypeEnum.METADATA) { if (theOperation == RestOperationTypeEnum.METADATA) {
return new Verdict(myMode, this); return newVerdict();
} else { } else {
return null; return null;
} }
@ -196,14 +193,15 @@ class Rule implements IAuthRule {
throw new IllegalStateException("Unable to apply security to event of applies to type " + myAppliesTo); 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) { private boolean requestAppliesToTransaction(FhirContext theContext, RuleOpEnum theOp, IBaseResource theInputResource) {
if (!"Bundle".equals(theContext.getResourceDefinition(theInputResource).getName())) { if (!"Bundle".equals(theContext.getResourceDefinition(theInputResource).getName())) {
return false; return false;
} }
IBaseBundle request = (IBaseBundle) theInputResource; IBaseBundle request = (IBaseBundle) theInputResource;
String bundleType = BundleUtil.getBundleType(theContext, request); String bundleType = BundleUtil.getBundleType(theContext, request);
switch (theOp) { switch (theOp) {
@ -216,11 +214,6 @@ class Rule implements IAuthRule {
} }
} }
@Override
public String getName() {
return myName;
}
public TransactionAppliesToEnum getTransactionAppliesToOp() { public TransactionAppliesToEnum getTransactionAppliesToOp() {
return myTransactionAppliesToOp; return myTransactionAppliesToOp;
} }
@ -245,9 +238,6 @@ class Rule implements IAuthRule {
myClassifierType = theClassifierType; myClassifierType = theClassifierType;
} }
public void setMode(PolicyEnum theRuleMode) {
myMode = theRuleMode;
}
public Rule setOp(RuleOpEnum theRuleOp) { public Rule setOp(RuleOpEnum theRuleOp) {
myOp = theRuleOp; myOp = theRuleOp;
@ -257,4 +247,5 @@ class Rule implements IAuthRule {
public void setTransactionAppliesToOp(TransactionAppliesToEnum theOp) { public void setTransactionAppliesToOp(TransactionAppliesToEnum theOp) {
myTransactionAppliesToOp = theOp; myTransactionAppliesToOp = theOp;
} }
} }

View File

@ -24,6 +24,7 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; 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<? extends IBaseResource> theType) {
Validate.notNull(theType, "theType must not be null");
OperationRule rule = createRule();
HashSet<Class<? extends IBaseResource>> appliesToTypes = new HashSet<Class<? extends IBaseResource>>();
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<IIdType> ids = new ArrayList<IIdType>();
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();
}
} }
} }

View File

@ -28,5 +28,6 @@ enum RuleOpEnum {
TRANSACTION, TRANSACTION,
METADATA, METADATA,
BATCH, BATCH,
DELETE DELETE,
OPERATION
} }

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.rest.server.servlet; package ca.uhn.fhir.rest.server.servlet;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
/* /*
* #%L * #%L
* HAPI FHIR - Core Library * HAPI FHIR - Core Library
@ -61,12 +63,12 @@ public class ServletRestfulResponse extends RestfulResponse<ServletRequestDetail
} }
@Override @Override
public Writer getResponseWriter(int statusCode, String contentType, String charset, boolean theRespondGzip) throws UnsupportedEncodingException, IOException { public Writer getResponseWriter(int theStatusCode, String theStatusMessage, String theContentType, String theCharset, boolean theRespondGzip) throws UnsupportedEncodingException, IOException {
addHeaders(); addHeaders();
HttpServletResponse theHttpResponse = getRequestDetails().getServletResponse(); HttpServletResponse theHttpResponse = getRequestDetails().getServletResponse();
theHttpResponse.setCharacterEncoding(charset); theHttpResponse.setCharacterEncoding(theCharset);
theHttpResponse.setStatus(statusCode); theHttpResponse.setStatus(theStatusCode);
theHttpResponse.setContentType(contentType); theHttpResponse.setContentType(theContentType);
if (theRespondGzip) { if (theRespondGzip) {
theHttpResponse.addHeader(Constants.HEADER_CONTENT_ENCODING, Constants.ENCODING_GZIP); theHttpResponse.addHeader(Constants.HEADER_CONTENT_ENCODING, Constants.ENCODING_GZIP);
return new OutputStreamWriter(new GZIPOutputStream(theHttpResponse.getOutputStream()), Constants.CHARSET_NAME_UTF8); return new OutputStreamWriter(new GZIPOutputStream(theHttpResponse.getOutputStream()), Constants.CHARSET_NAME_UTF8);

View File

@ -1656,10 +1656,6 @@ public class XmlUtil {
ourHaveLoggedStaxImplementation = true; ourHaveLoggedStaxImplementation = true;
} }
public static void main(String[] args) throws FactoryConfigurationError, XMLStreamException {
createXmlWriter(new StringWriter());
}
private static final class ExtendedEntityReplacingXmlResolver implements XMLResolver { private static final class ExtendedEntityReplacingXmlResolver implements XMLResolver {
@Override @Override
public Object resolveEntity(String thePublicID, String theSystemID, String theBaseURI, String theNamespace) throws XMLStreamException { public Object resolveEntity(String thePublicID, String theSystemID, String theBaseURI, String theNamespace) throws XMLStreamException {

View File

@ -7,11 +7,15 @@ ca.uhn.fhir.context.FhirContext.noStructuresForSpecifiedVersion=Could not find t
ca.uhn.fhir.context.RuntimeResourceDefinition.nonInstantiableType=Resource type "{0}" can not be instantiated. Check that this class has a no-argument constructor, and that it is static if it is a nested type. Error is: {1} ca.uhn.fhir.context.RuntimeResourceDefinition.nonInstantiableType=Resource type "{0}" can not be instantiated. Check that this class has a no-argument constructor, and that it is static if it is a nested type. Error is: {1}
ca.uhn.fhir.rest.client.BaseClient.ioExceptionDuringOperation=Encountered IOException when performing {0} to URL {1} - {2}
ca.uhn.fhir.rest.client.BaseClient.failedToParseResponse=Failed to parse response from server when performing {0} to URL {1} - {2}
ca.uhn.fhir.rest.client.GenericClient.cantDetermineRequestType=Unable to determing encoding of request (body does not appear to be valid XML or JSON)
ca.uhn.fhir.rest.client.GenericClient.noPagingLinkFoundInBundle=Can not perform paging operation because no link was found in Bundle with relation "{0}" ca.uhn.fhir.rest.client.GenericClient.noPagingLinkFoundInBundle=Can not perform paging operation because no link was found in Bundle with relation "{0}"
ca.uhn.fhir.rest.client.GenericClient.noVersionIdForVread=No version specified in URL for 'vread' operation: {0} ca.uhn.fhir.rest.client.GenericClient.noVersionIdForVread=No version specified in URL for 'vread' operation: {0}
ca.uhn.fhir.rest.client.GenericClient.incompleteUriForRead=The given URI is not an absolute URL and is not usable for this operation: {0} ca.uhn.fhir.rest.client.GenericClient.incompleteUriForRead=The given URI is not an absolute URL and is not usable for this operation: {0}
ca.uhn.fhir.rest.client.GenericClient.cannotDetermineResourceTypeFromUri=Unable to determine the resource type from the given URI: {0} ca.uhn.fhir.rest.client.GenericClient.cannotDetermineResourceTypeFromUri=Unable to determine the resource type from the given URI: {0}
ca.uhn.fhir.rest.client.RestfulClientFactory.failedToRetrieveConformance=Failed to retrieve the server's metadata statement during client initialization. URL used was: {0} ca.uhn.fhir.rest.client.RestfulClientFactory.failedToRetrieveConformance=Failed to retrieve the server metadata statement during client initialization. URL used was {0}
ca.uhn.fhir.rest.client.RestfulClientFactory.wrongVersionInConformance=The server at base URL "{0}" returned a conformance statement indicating that it supports FHIR version "{1}" which corresponds to {2}, but this client is configured to use {3} (via the FhirContext). ca.uhn.fhir.rest.client.RestfulClientFactory.wrongVersionInConformance=The server at base URL "{0}" returned a conformance statement indicating that it supports FHIR version "{1}" which corresponds to {2}, but this client is configured to use {3} (via the FhirContext).
ca.uhn.fhir.rest.method.BaseOutcomeReturningMethodBindingWithResourceParam.incorrectIdForUpdate=Can not update resource, resource body must contain an ID element which matches the request URL for update (PUT) operation - Resource body ID of "{0}" does not match URL ID of "{1}" ca.uhn.fhir.rest.method.BaseOutcomeReturningMethodBindingWithResourceParam.incorrectIdForUpdate=Can not update resource, resource body must contain an ID element which matches the request URL for update (PUT) operation - Resource body ID of "{0}" does not match URL ID of "{1}"

View File

@ -26,7 +26,7 @@ public class FhirDbConfig {
extraProperties.put("hibernate.search.default.directory_provider", "filesystem"); extraProperties.put("hibernate.search.default.directory_provider", "filesystem");
extraProperties.put("hibernate.search.default.indexBase", "target/lucenefiles"); extraProperties.put("hibernate.search.default.indexBase", "target/lucenefiles");
extraProperties.put("hibernate.search.lucene_version", "LUCENE_CURRENT"); extraProperties.put("hibernate.search.lucene_version", "LUCENE_CURRENT");
// extraProperties.put("hibernate.search.default.worker.execution", "async"); extraProperties.put("hibernate.search.default.worker.execution", "async");
if (System.getProperty("lowmem") != null) { if (System.getProperty("lowmem") != null) {
extraProperties.put("hibernate.search.autoregister_listeners", "false"); extraProperties.put("hibernate.search.autoregister_listeners", "false");

View File

@ -103,4 +103,14 @@ public class JaxRsHttpRequest implements IHttpRequest {
return null; return null;
} }
@Override
public String getUri() {
return ""; // TODO: can we get this from somewhere?
}
@Override
public String getHttpVerbName() {
return myRequestType.name();
}
} }

View File

@ -194,7 +194,7 @@ public abstract class AbstractJaxRsConformanceProvider extends AbstractJaxRsProv
if (conformance != null) { if (conformance != null) {
Set<SummaryEnum> summaryMode = Collections.emptySet(); Set<SummaryEnum> 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()); return (Response) response.returnResponse(null, Constants.STATUS_HTTP_500_INTERNAL_ERROR, true, null, getResourceType().getSimpleName());
} }

View File

@ -65,7 +65,7 @@ public class JaxRsResponse extends RestfulResponse<JaxRsRequest> {
* by the server. * by the server.
*/ */
@Override @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 { throws UnsupportedEncodingException, IOException {
return new StringWriter(); return new StringWriter();
} }

View File

@ -65,6 +65,7 @@ import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.PreferReturnEnum; import ca.uhn.fhir.rest.api.PreferReturnEnum;
import ca.uhn.fhir.rest.api.SummaryEnum; 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.IGenericClient;
import ca.uhn.fhir.rest.client.ServerValidationModeEnum; import ca.uhn.fhir.rest.client.ServerValidationModeEnum;
import ca.uhn.fhir.rest.client.api.Header; import ca.uhn.fhir.rest.client.api.Header;
@ -94,6 +95,7 @@ public class GenericJaxRsClientDstu3Test {
ourCtx.setRestfulClientFactory(clientFactory); ourCtx.setRestfulClientFactory(clientFactory);
ourResponseCount = 0; ourResponseCount = 0;
System.setProperty("hapi.client.keepresponses", "true");
} }
private String getPatientFeedWithOneResult() { private String getPatientFeedWithOneResult() {
@ -211,7 +213,6 @@ public class GenericJaxRsClientDstu3Test {
} }
@Test @Test
@SuppressWarnings("deprecation")
public void testConformance() throws Exception { public void testConformance() throws Exception {
IParser p = ourCtx.newXmlParser(); IParser p = ourCtx.newXmlParser();
@ -228,7 +229,7 @@ public class GenericJaxRsClientDstu3Test {
//@formatter:off //@formatter:off
Conformance resp = (Conformance)client.conformance(); Conformance resp = (Conformance)client.fetchConformance().ofType(Conformance.class).execute();
//@formatter:on //@formatter:on
assertEquals("http://localhost:" + ourPort + "/fhir/metadata", ourRequestUri); assertEquals("http://localhost:" + ourPort + "/fhir/metadata", ourRequestUri);

View File

@ -51,8 +51,13 @@ public class DaoConfig {
// *** // ***
// update setter javadoc if default changes // update setter javadoc if default changes
// *** // ***
private int myDeferIndexingForCodesystemsOfSize = 2000;
// ***
// update setter javadoc if default changes
// ***
private long myExpireSearchResultsAfterMillis = DateUtils.MILLIS_PER_HOUR; private long myExpireSearchResultsAfterMillis = DateUtils.MILLIS_PER_HOUR;
private int myHardSearchLimit = 1000; private int myHardSearchLimit = 1000;
private int myHardTagListLimit = 1000; private int myHardTagListLimit = 1000;
private int myIncludeLimit = 2000; private int myIncludeLimit = 2000;
@ -61,24 +66,36 @@ public class DaoConfig {
// update setter javadoc if default changes // update setter javadoc if default changes
// *** // ***
private boolean myIndexContainedResources = true; private boolean myIndexContainedResources = true;
private List<IServerInterceptor> myInterceptors; private List<IServerInterceptor> myInterceptors;
// *** // ***
// update setter javadoc if default changes // update setter javadoc if default changes
// *** // ***
private int myMaximumExpansionSize = 5000; private int myMaximumExpansionSize = 5000;
private ResourceEncodingEnum myResourceEncoding = ResourceEncodingEnum.JSONC; private ResourceEncodingEnum myResourceEncoding = ResourceEncodingEnum.JSONC;
private boolean mySchedulingDisabled; private boolean mySchedulingDisabled;
private boolean mySubscriptionEnabled;
private long mySubscriptionPollDelay = 1000;
private Long mySubscriptionPurgeInactiveAfterMillis; private boolean mySubscriptionEnabled;
private Set<String> myTreatBaseUrlsAsLocal = new HashSet<String>();
private long mySubscriptionPollDelay = 1000;
private Long mySubscriptionPurgeInactiveAfterMillis;
private Set<String> myTreatBaseUrlsAsLocal = new HashSet<String>();
/**
* 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.
* <p>
* Defaults to 2000
* </p>
*/
public int getDeferIndexingForCodesystemsOfSize() {
return myDeferIndexingForCodesystemsOfSize;
}
/** /**
* Sets the number of milliseconds that search results for a given client search * Sets the number of milliseconds that search results for a given client search
* should be preserved before being purged from the database. * should be preserved before being purged from the database.
@ -117,6 +134,7 @@ public class DaoConfig {
} }
return myInterceptors; return myInterceptors;
} }
/** /**
* See {@link #setMaximumExpansionSize(int)} * See {@link #setMaximumExpansionSize(int)}
*/ */
@ -180,7 +198,6 @@ public class DaoConfig {
public boolean isAllowInlineMatchUrlReferences() { public boolean isAllowInlineMatchUrlReferences() {
return myAllowInlineMatchUrlReferences; return myAllowInlineMatchUrlReferences;
} }
public boolean isAllowMultipleDelete() { public boolean isAllowMultipleDelete() {
return myAllowMultipleDelete; return myAllowMultipleDelete;
} }
@ -251,6 +268,18 @@ public class DaoConfig {
myAllowMultipleDelete = theAllowMultipleDelete; 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.
* <p>
* Defaults to 2000
* </p>
*/
public void setDeferIndexingForCodesystemsOfSize(int theDeferIndexingForCodesystemsOfSize) {
myDeferIndexingForCodesystemsOfSize = theDeferIndexingForCodesystemsOfSize;
}
/** /**
* Sets the number of milliseconds that search results for a given client search * Sets the number of milliseconds that search results for a given client search
* should be preserved before being purged from the database. * should be preserved before being purged from the database.

View File

@ -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<HistoryTuple> {
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();
}
}

View File

@ -55,14 +55,12 @@ import org.hibernate.search.annotations.Field;
import org.hibernate.search.annotations.Fields; import org.hibernate.search.annotations.Fields;
import org.hibernate.search.annotations.Indexed; import org.hibernate.search.annotations.Indexed;
import org.hibernate.search.annotations.Store; 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.entity.TermConceptParentChildLink.RelationshipTypeEnum;
import ca.uhn.fhir.jpa.search.DeferConceptIndexingInterceptor;
//@formatter:off //@formatter:off
@Entity @Entity
@Indexed(interceptor=DeferConceptIndexingInterceptor.class) @Indexed()
@Table(name="TRM_CONCEPT", uniqueConstraints= { @Table(name="TRM_CONCEPT", uniqueConstraints= {
@UniqueConstraint(name="IDX_CONCEPT_CS_CODE", columnNames= {"CODESYSTEM_PID", "CODE"}) @UniqueConstraint(name="IDX_CONCEPT_CS_CODE", columnNames= {"CODESYSTEM_PID", "CODE"})
}, indexes= { }, indexes= {
@ -71,12 +69,11 @@ import ca.uhn.fhir.jpa.search.DeferConceptIndexingInterceptor;
//@formatter:on //@formatter:on
public class TermConcept implements Serializable { public class TermConcept implements Serializable {
private static final int MAX_DESC_LENGTH = 400; private static final int MAX_DESC_LENGTH = 400;
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@OneToMany(fetch=FetchType.LAZY, mappedBy="myParent") @OneToMany(fetch=FetchType.LAZY, mappedBy="myParent")
private Collection<TermConceptParentChildLink> myChildren; private Collection<TermConceptParentChildLink> myChildren;
@Column(name="CODE", length=100, nullable=false) @Column(name="CODE", length=100, nullable=false)
@Fields({ @Fields({
@Field(name = "myCode", index = org.hibernate.search.annotations.Index.YES, store = Store.YES, analyze = Analyze.YES, analyzer = @Analyzer(definition = "exactAnalyzer")), @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() @ManyToOne()
@JoinColumn(name="CODESYSTEM_PID", referencedColumnName="PID", foreignKey=@ForeignKey(name="FK_CONCEPT_PID_CS_PID")) @JoinColumn(name="CODESYSTEM_PID", referencedColumnName="PID", foreignKey=@ForeignKey(name="FK_CONCEPT_PID_CS_PID"))
private TermCodeSystemVersion myCodeSystem; private TermCodeSystemVersion myCodeSystem;
@Column(name="CODESYSTEM_PID", insertable=false, updatable=false) @Column(name="CODESYSTEM_PID", insertable=false, updatable=false)
@Fields({ @Fields({
@Field(name="myCodeSystemVersionPid") @Field(name="myCodeSystemVersionPid")
@ -103,7 +100,7 @@ public class TermConcept implements Serializable {
}) })
private String myDisplay; private String myDisplay;
//@formatter:on //@formatter:on
@Id() @Id()
@SequenceGenerator(name="SEQ_CONCEPT_PID", sequenceName="SEQ_CONCEPT_PID") @SequenceGenerator(name="SEQ_CONCEPT_PID", sequenceName="SEQ_CONCEPT_PID")
@GeneratedValue(strategy=GenerationType.AUTO, generator="SEQ_CONCEPT_PID") @GeneratedValue(strategy=GenerationType.AUTO, generator="SEQ_CONCEPT_PID")
@ -189,6 +186,10 @@ public class TermConcept implements Serializable {
return myId; return myId;
} }
public Long getIndexStatus() {
return myIndexStatus;
}
public Collection<TermConceptParentChildLink> getParents() { public Collection<TermConceptParentChildLink> getParents() {
if (myParents == null) { if (myParents == null) {
myParents = new ArrayList<TermConceptParentChildLink>(); myParents = new ArrayList<TermConceptParentChildLink>();

View File

@ -66,6 +66,10 @@ public class TermConceptParentChildLink implements Serializable {
return myChild; return myChild;
} }
public RelationshipTypeEnum getRelationshipType() {
return myRelationshipType;
}
public TermCodeSystemVersion getCodeSystem() { public TermCodeSystemVersion getCodeSystem() {
return myCodeSystem; return myCodeSystem;
} }

View File

@ -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;
}
}

View File

@ -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<QuestionnaireResponse> {
// nothing yet
}

View File

@ -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<QuestionnaireResponse> {
// nothing yet
}

View File

@ -26,7 +26,6 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import javax.servlet.http.HttpServletRequest; 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) { } else if (thePackage == null || thePackage.getData() == null || thePackage.getData().length == 0) {
throw new InvalidRequestException("No 'localfile' or 'package' parameter, or package had no data"); throw new InvalidRequestException("No 'localfile' or 'package' parameter, or package had no data");
} else { } else {
data = Arrays.asList(thePackage.getData()); data = new ArrayList<byte[]>();
data.add(thePackage.getData());
thePackage.setData(null);
} }
String url = theUrl != null ? theUrl.getValueAsString() : null; String url = theUrl != null ? theUrl.getValueAsString() : null;

View File

@ -1,9 +0,0 @@
package ca.uhn.fhir.jpa.search;
import org.hibernate.search.indexes.interceptor.DontInterceptEntityInterceptor;
public class DeferConceptIndexingInterceptor extends DontInterceptEntityInterceptor
//implements EntityIndexingInterceptor<TermConcept>
{
// nothing for now
}

View File

@ -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<ISubscriptionWebsocketHandler> {
static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SubscriptionWebsocketHandlerDstu2.class);
@Override
public ISubscriptionWebsocketHandler getObject() throws Exception {
return new SubscriptionWebsocketHandlerDstu2();
}
@Override
public Class<ISubscriptionWebsocketHandler> getObjectType() {
return ISubscriptionWebsocketHandler.class;
}
@Override
public boolean isSingleton() {
return false;
}
}

View File

@ -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<ISubscriptionWebsocketHandler> {
static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SubscriptionWebsocketHandlerDstu3.class);
@Override
public ISubscriptionWebsocketHandler getObject() throws Exception {
return new SubscriptionWebsocketHandlerDstu3();
}
@Override
public Class<ISubscriptionWebsocketHandler> getObjectType() {
return ISubscriptionWebsocketHandler.class;
}
@Override
public boolean isSingleton() {
return false;
}
}

View File

@ -33,6 +33,7 @@ import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType; import javax.persistence.PersistenceContextType;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -67,9 +68,13 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
@Autowired @Autowired
protected ITermConceptDao myConceptDao; protected ITermConceptDao myConceptDao;
private List<TermConceptParentChildLink> myConceptLinksToSaveLater = new ArrayList<TermConceptParentChildLink>();
@Autowired @Autowired
private ITermConceptParentChildLinkDao myConceptParentChildLinkDao; private ITermConceptParentChildLinkDao myConceptParentChildLinkDao;
private List<TermConcept> myConceptsToSaveLater = new ArrayList<TermConcept>();
@Autowired @Autowired
protected FhirContext myContext; protected FhirContext myContext;
@ -78,6 +83,8 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
@PersistenceContext(type = PersistenceContextType.TRANSACTION) @PersistenceContext(type = PersistenceContextType.TRANSACTION)
protected EntityManager myEntityManager; protected EntityManager myEntityManager;
private boolean myProcessDeferred = true;
private boolean addToSet(Set<TermConcept> theSetToPopulate, TermConcept theConcept) { private boolean addToSet(Set<TermConcept> theSetToPopulate, TermConcept theConcept) {
boolean retVal = theSetToPopulate.add(theConcept); boolean retVal = theSetToPopulate.add(theConcept);
@ -197,35 +204,101 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
TermCodeSystemVersion csv = cs.getCurrentVersion(); TermCodeSystemVersion csv = cs.getCurrentVersion();
return csv; return csv;
} }
private TermCodeSystem getCodeSystem(String theSystem) { private TermCodeSystem getCodeSystem(String theSystem) {
TermCodeSystem cs = myCodeSystemDao.findByCodeSystemUri(theSystem); TermCodeSystem cs = myCodeSystemDao.findByCodeSystemUri(theSystem);
return cs; return cs;
} }
private void parentPids(TermConcept theNextConcept, Set<Long> 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<TermConcept, Object> theConceptsStack, int theTotalConcepts) { private void persistChildren(TermConcept theConcept, TermCodeSystemVersion theCodeSystem, IdentityHashMap<TermConcept, Object> theConceptsStack, int theTotalConcepts) {
if (theConceptsStack.put(theConcept, PLACEHOLDER_OBJECT) != null) { if (theConceptsStack.put(theConcept, PLACEHOLDER_OBJECT) != null) {
return; return;
} }
if (theConceptsStack.size() % 1000 == 0) { if (theConceptsStack.size() == 1 || theConceptsStack.size() % 10000 == 0) {
float pct = (float) theConceptsStack.size() / (float) theTotalConcepts; float pct = (float) theConceptsStack.size() / (float) theTotalConcepts;
ourLog.info("Have saved {}/{} concepts ({}%), flushing", theConceptsStack.size(), theTotalConcepts, (int)( pct*100.0f)); ourLog.info("Have processed {}/{} concepts ({}%)", theConceptsStack.size(), theTotalConcepts, (int)( pct*100.0f));
myConceptDao.flush();
myConceptParentChildLinkDao.flush();
} }
theConcept.setCodeSystem(theCodeSystem); theConcept.setCodeSystem(theCodeSystem);
theConcept.setIndexStatus(BaseHapiFhirDao.INDEX_STATUS_INDEXED); theConcept.setIndexStatus(BaseHapiFhirDao.INDEX_STATUS_INDEXED);
myConceptDao.save(theConcept); Set<Long> parentPids = new HashSet<Long>();
parentPids(theConcept, parentPids);
theConcept.setParentPids(parentPids);
if (theConceptsStack.size() <= myDaoConfig.getDeferIndexingForCodesystemsOfSize()) {
myConceptDao.save(theConcept);
} else {
myConceptsToSaveLater.add(theConcept);
}
for (TermConceptParentChildLink next : theConcept.getChildren()) { for (TermConceptParentChildLink next : theConcept.getChildren()) {
persistChildren(next.getChild(), theCodeSystem, theConceptsStack, theTotalConcepts); persistChildren(next.getChild(), theCodeSystem, theConceptsStack, theTotalConcepts);
} }
for (TermConceptParentChildLink next : theConcept.getChildren()) { 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 @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)"); ourLog.info("Validating all codes in CodeSystem for storage (this can take some time for large sets)");
// Validate the code system // Validate the code system
IdentityHashMap<TermConcept, Object> conceptsStack = new IdentityHashMap<TermConcept, Object>(); ArrayList<String> conceptsStack = new ArrayList<String>();
IdentityHashMap<TermConcept, Object> allConcepts = new IdentityHashMap<TermConcept, Object>(); IdentityHashMap<TermConcept, Object> allConcepts = new IdentityHashMap<TermConcept, Object>();
int totalCodeCount = 0; int totalCodeCount = 0;
for (TermConcept next : theCodeSystemVersion.getConcepts()) { for (TermConcept next : theCodeSystemVersion.getConcepts()) {
@ -279,11 +352,17 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
codeSystem.setCurrentVersion(theCodeSystemVersion); codeSystem.setCurrentVersion(theCodeSystemVersion);
codeSystem = myCodeSystemDao.saveAndFlush(codeSystem); codeSystem = myCodeSystemDao.saveAndFlush(codeSystem);
ourLog.info("Saving {} concepts...", totalCodeCount); ourLog.info("Setting codesystemversion on {} concepts...", totalCodeCount);
conceptsStack = new IdentityHashMap<TermConcept, Object>();
for (TermConcept next : theCodeSystemVersion.getConcepts()) { for (TermConcept next : theCodeSystemVersion.getConcepts()) {
persistChildren(next, codeSystemVersion, conceptsStack, totalCodeCount); populateVersion(next, codeSystemVersion);
}
ourLog.info("Saving {} concepts...", totalCodeCount);
IdentityHashMap<TermConcept, Object> conceptsStack2 = new IdentityHashMap<TermConcept, Object>();
for (TermConcept next : theCodeSystemVersion.getConcepts()) {
persistChildren(next, codeSystemVersion, conceptsStack2, totalCodeCount);
} }
ourLog.info("Done saving concepts, flushing to database"); ourLog.info("Done saving concepts, flushing to database");
@ -291,27 +370,6 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
myConceptDao.flush(); myConceptDao.flush();
myConceptParentChildLinkDao.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<Long> parentPids = new HashSet<Long>();
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 * 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"); ourLog.info("Done deleting old code system versions");
}
if (myConceptsToSaveLater.size() > 0 || myConceptLinksToSaveLater.size() > 0) {
private void parentPids(TermConcept theNextConcept, Set<Long> theParentPids) { ourLog.info("Note that some concept saving was deferred - still have {} concepts and {} relationships", myConceptsToSaveLater.size(), myConceptLinksToSaveLater.size());
for (TermConceptParentChildLink nextParentLink : theNextConcept.getParents()){
TermConcept parent = nextParentLink.getParent();
if (parent != null && theParentPids.add(parent.getId())) {
parentPids(parent, theParentPids);
}
} }
} }
@Override @Override
public boolean supportsSystem(String theSystem) { public boolean supportsSystem(String theSystem) {
TermCodeSystem cs = getCodeSystem(theSystem); TermCodeSystem cs = getCodeSystem(theSystem);
@ -349,16 +402,17 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
return retVal; return retVal;
} }
private int validateConceptForStorage(TermConcept theConcept, TermCodeSystemVersion theCodeSystem, IdentityHashMap<TermConcept, Object> theConceptsStack, private int validateConceptForStorage(TermConcept theConcept, TermCodeSystemVersion theCodeSystem, ArrayList<String> theConceptsStack,
IdentityHashMap<TermConcept, Object> theAllConcepts) { IdentityHashMap<TermConcept, Object> theAllConcepts) {
ValidateUtil.isTrueOrThrowInvalidRequest(theConcept.getCodeSystem() != null, "CodesystemValue is null"); ValidateUtil.isTrueOrThrowInvalidRequest(theConcept.getCodeSystem() != null, "CodesystemValue is null");
ValidateUtil.isTrueOrThrowInvalidRequest(theConcept.getCodeSystem() == theCodeSystem, "CodeSystems are not equal"); ValidateUtil.isTrueOrThrowInvalidRequest(theConcept.getCodeSystem() == theCodeSystem, "CodeSystems are not equal");
ValidateUtil.isNotBlankOrThrowInvalidRequest(theConcept.getCode(), "Codesystem contains a code with no code value"); 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()); throw new InvalidRequestException("CodeSystem contains circular reference around code " + theConcept.getCode());
} }
theConceptsStack.add(theConcept.getCode());
int retVal = 0; int retVal = 0;
if (theAllConcepts.put(theConcept, theAllConcepts) == null) { if (theAllConcepts.put(theConcept, theAllConcepts) == null) {
if (theAllConcepts.size() % 1000 == 0) { if (theAllConcepts.size() % 1000 == 0) {
@ -372,7 +426,7 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
retVal += validateConceptForStorage(next.getChild(), theCodeSystem, theConceptsStack, theAllConcepts); retVal += validateConceptForStorage(next.getChild(), theCodeSystem, theConceptsStack, theAllConcepts);
} }
theConceptsStack.remove(theConcept); theConceptsStack.remove(theConceptsStack.size() - 1);
return retVal; return retVal;
} }

View File

@ -174,7 +174,7 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvc implements I
} else if (nextFilter.getOp() == FilterOperator.ISA) { } else if (nextFilter.getOp() == FilterOperator.ISA) {
if (isNotBlank(nextFilter.getValue())) { if (isNotBlank(nextFilter.getValue())) {
TermConcept code = super.findCode(system, 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 { } else {
throw new InvalidRequestException("Unknown filter property[" + nextFilter + "] + op[" + nextFilter.getOpElement().getValueAsString() + "]"); throw new InvalidRequestException("Unknown filter property[" + nextFilter + "] + op[" + nextFilter.getOpElement().getValueAsString() + "]");

View File

@ -48,4 +48,12 @@ public interface IHapiTerminologySvc {
List<TermConcept> findCodes(String theSystem); List<TermConcept> 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);
} }

View File

@ -1,6 +1,5 @@
package ca.uhn.fhir.jpa.term; package ca.uhn.fhir.jpa.term;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank; 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.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.Reader; import java.io.Reader;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
@ -56,18 +54,20 @@ import org.apache.commons.lang3.Validate;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import com.google.common.annotations.VisibleForTesting; 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.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink; import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum; 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.method.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.util.CoverageIgnore;
public class TerminologyLoaderSvc implements IHapiTerminologyLoaderSvc { 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_FILE = "loinc.csv";
public static final String LOINC_HIERARCHY_FILE = "MULTI-AXIAL_HIERARCHY.CSV"; public static final String LOINC_HIERARCHY_FILE = "MULTI-AXIAL_HIERARCHY.CSV";
@ -79,15 +79,8 @@ public class TerminologyLoaderSvc implements IHapiTerminologyLoaderSvc {
@Autowired @Autowired
private IHapiTerminologySvc myTermSvc; private IHapiTerminologySvc myTermSvc;
private void cleanUpTemporaryFiles(Map<String, File> filenameToFile) { private void dropCircularRefs(TermConcept theConcept, ArrayList<String> theChain, Map<String, TermConcept> theCode2concept, Counter theCircularCounter) {
ourLog.info("Finished terminology file import, cleaning up temporary files");
for (File nextFile : filenameToFile.values()) {
nextFile.delete();
}
}
private void dropCircularRefs(TermConcept theConcept, LinkedHashSet<String> theChain, Map<String, TermConcept> theCode2concept) {
theChain.add(theConcept.getCode()); theChain.add(theConcept.getCode());
for (Iterator<TermConceptParentChildLink> childIter = theConcept.getChildren().iterator(); childIter.hasNext();) { for (Iterator<TermConceptParentChildLink> childIter = theConcept.getChildren().iterator(); childIter.hasNext();) {
TermConceptParentChildLink next = childIter.next(); TermConceptParentChildLink next = childIter.next();
@ -110,46 +103,25 @@ public class TerminologyLoaderSvc implements IHapiTerminologyLoaderSvc {
ourLog.info(b.toString(), theConcept.getCode()); ourLog.info(b.toString(), theConcept.getCode());
childIter.remove(); childIter.remove();
} else { } else {
dropCircularRefs(nextChild, theChain, theCode2concept); dropCircularRefs(nextChild, theChain, theCode2concept, theCircularCounter);
} }
} }
theChain.remove(theConcept.getCode()); theChain.remove(theChain.size() - 1);
} }
private Map<String, File> extractFiles(List<byte[]> theZipBytes, List<String> theExpectedFilenameFragments) { private void extractFiles(List<byte[]> theZipBytes, List<String> theExpectedFilenameFragments) {
Map<String, File> filenameToFile = new HashMap<String, File>(); Set<String> foundFragments = new HashSet<String>();
for (byte[] nextZipBytes : theZipBytes) { for (byte[] nextZipBytes : theZipBytes) {
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new ByteArrayInputStream(nextZipBytes))); ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new ByteArrayInputStream(nextZipBytes)));
try { try {
for (ZipEntry nextEntry; (nextEntry = zis.getNextEntry()) != null;) { for (ZipEntry nextEntry; (nextEntry = zis.getNextEntry()) != null;) {
ZippedFileInputStream inputStream = new ZippedFileInputStream(zis);
boolean want = false;
for (String next : theExpectedFilenameFragments) { for (String next : theExpectedFilenameFragments) {
if (nextEntry.getName().contains(next)) { 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) { } catch (IOException e) {
throw new InternalErrorException(e); throw new InternalErrorException(e);
@ -158,10 +130,12 @@ public class TerminologyLoaderSvc implements IHapiTerminologyLoaderSvc {
} }
} }
if (filenameToFile.size() != theExpectedFilenameFragments.size()) { for (String next : theExpectedFilenameFragments) {
throw new InvalidRequestException("Invalid input zip file, expected zip to contain the following name fragments: " + theExpectedFilenameFragments + " but found: " + filenameToFile.keySet()); 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) { public String firstNonBlank(String... theStrings) {
@ -185,49 +159,54 @@ public class TerminologyLoaderSvc implements IHapiTerminologyLoaderSvc {
return concept; return concept;
} }
private void iterateOverZipFile(Map<String, File> theFilenameToFile, String fileNamePart, IRecordHandler handler, char theDelimiter, QuoteMode theQuoteMode) { private void iterateOverZipFile(List<byte[]> theZipBytes, String fileNamePart, IRecordHandler handler, char theDelimiter, QuoteMode theQuoteMode) {
boolean found = false; boolean found = false;
for (Entry<String, File> nextEntry : new HashMap<String, File>(theFilenameToFile).entrySet()) {
if (nextEntry.getKey().contains(fileNamePart)) { for (byte[] nextZipBytes : theZipBytes) {
ourLog.info("Processing file {}", nextEntry.getKey()); ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new ByteArrayInputStream(nextZipBytes)));
found = true; try {
for (ZipEntry nextEntry; (nextEntry = zis.getNextEntry()) != null;) {
ZippedFileInputStream inputStream = new ZippedFileInputStream(zis);
Reader reader = null; String nextFilename = nextEntry.getName();
CSVParser parsed = null; if (nextFilename.contains(fileNamePart)) {
try { ourLog.info("Processing file {}", nextFilename);
reader = new BufferedReader(new FileReader(nextEntry.getValue())); found = true;
CSVFormat format = CSVFormat.newFormat(theDelimiter).withFirstRecordAsHeader();
if (theQuoteMode != null) {
format = format.withQuote('"').withQuoteMode(theQuoteMode);
}
parsed = new CSVParser(reader, format);
Iterator<CSVRecord> iter = parsed.iterator();
ourLog.debug("Header map: {}", parsed.getHeaderMap());
int count = 0; Reader reader = null;
int logIncrement = 100000; CSVParser parsed = null;
int nextLoggedCount = logIncrement; try {
while (iter.hasNext()) { reader = new InputStreamReader(zis, Charsets.UTF_8);
CSVRecord nextRecord = iter.next(); CSVFormat format = CSVFormat.newFormat(theDelimiter).withFirstRecordAsHeader();
handler.accept(nextRecord); if (theQuoteMode != null) {
count++; format = format.withQuote('"').withQuoteMode(theQuoteMode);
if (count >= nextLoggedCount) { }
ourLog.info(" * Processed {} records in {}", count, fileNamePart); parsed = new CSVParser(reader, format);
nextLoggedCount += logIncrement; Iterator<CSVRecord> 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<byte[]> theZipBytes, RequestDetails theRequestDetails) { public UploadStatistics loadLoinc(List<byte[]> theZipBytes, RequestDetails theRequestDetails) {
List<String> expectedFilenameFragments = Arrays.asList(LOINC_FILE, LOINC_HIERARCHY_FILE); List<String> expectedFilenameFragments = Arrays.asList(LOINC_FILE, LOINC_HIERARCHY_FILE);
Map<String, File> filenameToFile = extractFiles(theZipBytes, expectedFilenameFragments); extractFiles(theZipBytes, expectedFilenameFragments);
ourLog.info("Beginning LOINC processing"); ourLog.info("Beginning LOINC processing");
try { return processLoincFiles(theZipBytes, theRequestDetails);
return processLoincFiles(filenameToFile, theRequestDetails);
} finally {
cleanUpTemporaryFiles(filenameToFile);
}
} }
@Override @Override
public UploadStatistics loadSnomedCt(List<byte[]> theZipBytes, RequestDetails theRequestDetails) { public UploadStatistics loadSnomedCt(List<byte[]> theZipBytes, RequestDetails theRequestDetails) {
List<String> expectedFilenameFragments = Arrays.asList(SCT_FILE_DESCRIPTION, SCT_FILE_RELATIONSHIP, SCT_FILE_CONCEPT); List<String> expectedFilenameFragments = Arrays.asList(SCT_FILE_DESCRIPTION, SCT_FILE_RELATIONSHIP, SCT_FILE_CONCEPT);
Map<String, File> filenameToFile = extractFiles(theZipBytes, expectedFilenameFragments); extractFiles(theZipBytes, expectedFilenameFragments);
ourLog.info("Beginning SNOMED CT processing"); ourLog.info("Beginning SNOMED CT processing");
try { return processSnomedCtFiles(theZipBytes, theRequestDetails);
return processSnomedCtFiles(filenameToFile, theRequestDetails);
} finally {
cleanUpTemporaryFiles(filenameToFile);
}
} }
UploadStatistics processLoincFiles(Map<String, File> filenameToFile, RequestDetails theRequestDetails) { UploadStatistics processLoincFiles(List<byte[]> theZipBytes, RequestDetails theRequestDetails) {
final TermCodeSystemVersion codeSystemVersion = new TermCodeSystemVersion(); final TermCodeSystemVersion codeSystemVersion = new TermCodeSystemVersion();
final Map<String, TermConcept> code2concept = new HashMap<String, TermConcept>(); final Map<String, TermConcept> code2concept = new HashMap<String, TermConcept>();
IRecordHandler handler = new LoincHandler(codeSystemVersion, code2concept); 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); 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<Entry<String, TermConcept>> iter = code2concept.entrySet().iterator(); iter.hasNext();) { for (Iterator<Entry<String, TermConcept>> iter = code2concept.entrySet().iterator(); iter.hasNext();) {
Entry<String, TermConcept> next = iter.next(); Entry<String, TermConcept> next = iter.next();
// if (isBlank(next.getKey())) { // 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()); 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()); return new UploadStatistics(code2concept.size());
} }
UploadStatistics processSnomedCtFiles(Map<String, File> 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<byte[]> theZipBytes, RequestDetails theRequestDetails) {
final TermCodeSystemVersion codeSystemVersion = new TermCodeSystemVersion(); final TermCodeSystemVersion codeSystemVersion = new TermCodeSystemVersion();
final Map<String, TermConcept> id2concept = new HashMap<String, TermConcept>(); final Map<String, TermConcept> id2concept = new HashMap<String, TermConcept>();
final Map<String, TermConcept> code2concept = new HashMap<String, TermConcept>(); final Map<String, TermConcept> code2concept = new HashMap<String, TermConcept>();
final Set<String> validConceptIds = new HashSet<String>(); final Set<String> validConceptIds = new HashSet<String>();
IRecordHandler handler = new SctHandlerConcept(validConceptIds); 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()); ourLog.info("Have {} valid concept IDs", validConceptIds.size());
handler = new SctHandlerDescription(validConceptIds, code2concept, id2concept, codeSystemVersion); 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()); ourLog.info("Got {} concepts, cloning map", code2concept.size());
final HashMap<String, TermConcept> rootConcepts = new HashMap<String, TermConcept>(code2concept); final HashMap<String, TermConcept> rootConcepts = new HashMap<String, TermConcept>(code2concept);
handler = new SctHandlerRelationship(codeSystemVersion, rootConcepts, 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<Entry<String, TermConcept>> 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()); ourLog.info("Done loading SNOMED CT files - {} root codes, {} total codes", rootConcepts.size(), code2concept.size());
Counter circularCounter = new Counter();
for (TermConcept next : rootConcepts.values()) { for (TermConcept next : rootConcepts.values()) {
dropCircularRefs(next, new LinkedHashSet<String>(), 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<String>(), code2concept, circularCounter);
} }
codeSystemVersion.getConcepts().addAll(rootConcepts.values()); codeSystemVersion.getConcepts().addAll(rootConcepts.values());
myTermSvc.storeNewCodeSystemVersion(SCT_URL, codeSystemVersion, theRequestDetails); String url = SCT_URL;
storeCodeSystem(theRequestDetails, codeSystemVersion, url);
return new UploadStatistics(code2concept.size()); return new UploadStatistics(code2concept.size());
} }
@ -332,20 +326,6 @@ public class TerminologyLoaderSvc implements IHapiTerminologyLoaderSvc {
myTermSvc = theTermSvc; 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<String, File> files = new HashMap<String, File>();
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 { private interface IRecordHandler {
void accept(CSVRecord theRecord); void accept(CSVRecord theRecord);
} }
@ -506,23 +486,33 @@ public class TerminologyLoaderSvc implements IHapiTerminologyLoaderSvc {
String destinationId = theRecord.get("destinationId"); String destinationId = theRecord.get("destinationId");
String typeId = theRecord.get("typeId"); String typeId = theRecord.get("typeId");
boolean active = "1".equals(theRecord.get("active")); boolean active = "1".equals(theRecord.get("active"));
if (!active) {
return;
}
TermConcept typeConcept = myCode2concept.get(typeId); TermConcept typeConcept = myCode2concept.get(typeId);
TermConcept sourceConcept = myCode2concept.get(sourceId); TermConcept sourceConcept = myCode2concept.get(sourceId);
TermConcept targetConcept = myCode2concept.get(destinationId); TermConcept targetConcept = myCode2concept.get(destinationId);
if (sourceConcept != null && targetConcept != null && typeConcept != null) { if (sourceConcept != null && targetConcept != null && typeConcept != null) {
if (typeConcept.getDisplay().equals("Is a (attribute)")) { if (typeConcept.getDisplay().equals("Is a (attribute)")) {
RelationshipTypeEnum relationshipType = RelationshipTypeEnum.ISA;
if (!sourceId.equals(destinationId)) { if (!sourceId.equals(destinationId)) {
TermConceptParentChildLink link = new TermConceptParentChildLink(); if (active) {
link.setChild(sourceConcept); TermConceptParentChildLink link = new TermConceptParentChildLink();
link.setParent(targetConcept); link.setChild(sourceConcept);
link.setRelationshipType(TermConceptParentChildLink.RelationshipTypeEnum.ISA); link.setParent(targetConcept);
link.setCodeSystem(myCodeSystemVersion); link.setRelationshipType(relationshipType);
myRootConcepts.remove(link.getChild().getCode()); link.setCodeSystem(myCodeSystemVersion);
targetConcept.addChild(sourceConcept, RelationshipTypeEnum.ISA); targetConcept.addChild(sourceConcept, relationshipType);
} else {
// not active, so we're removing any existing links
for (TermConceptParentChildLink next : new ArrayList<TermConceptParentChildLink>(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())) { } else if (ignoredTypes.contains(typeConcept.getDisplay())) {
// ignore // 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 static class ZippedFileInputStream extends InputStream {
private ZipInputStream is; private ZipInputStream is;

View File

@ -0,0 +1,11 @@
package ca.uhn.fhir.jpa.util;
public class Counter {
private long myCount;
public long getThenAdd() {
return myCount++;
}
}

View File

@ -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("_", "");
//
// }
}

View File

@ -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";
}
}

View File

@ -9,7 +9,6 @@ import static org.junit.Assert.fail;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.TreeSet;
import org.hl7.fhir.dstu3.model.AuditEvent; import org.hl7.fhir.dstu3.model.AuditEvent;
import org.hl7.fhir.dstu3.model.CodeSystem; 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.FilterOperator;
import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionContainsComponent; import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.After;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.Before; import org.junit.Before;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.SearchParameterMap; import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.jpa.entity.ResourceTable; import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; 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()); 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 @Test
public void testExpandWithExcludeInExternalValueSet() { public void testExpandWithExcludeInExternalValueSet() {
createExternalCsAndLocalVs(); createExternalCsAndLocalVs();
@ -486,6 +523,11 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
public void before() { public void before() {
myDaoConfig.setMaximumExpansionSize(5000); myDaoConfig.setMaximumExpansionSize(5000);
} }
@After
public void after() {
myDaoConfig.setDeferIndexingForCodesystemsOfSize(new DaoConfig().getDeferIndexingForCodesystemsOfSize());
}
@Test @Test
public void testSearchCodeBelowLocalCodesystem() { public void testSearchCodeBelowLocalCodesystem() {

View File

@ -185,22 +185,22 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
List<String> idValues; List<String> idValues;
idValues = searchAndReturnUnqualifiedIdValues(ourServerBase + "/Patient/" + id.getIdPart() + "/_history?_at=gt" + toStr(preDates.get(0)) + "&_at=lt" + toStr(preDates.get(3))); 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))); 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))); 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"); idValues = searchAndReturnUnqualifiedIdValues(ourServerBase + "/_history?_at=gt2060");
assertThat(idValues, empty()); assertThat(idValues.toString(), idValues, empty());
idValues = searchAndReturnUnqualifiedIdValues(ourServerBase + "/_history?_at=" + InstantDt.withCurrentTime().getYear()); 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()); 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()); idValues = searchAndReturnUnqualifiedIdValues(ourServerBase + "/_history?_at=gt" + InstantDt.withCurrentTime().getYear());
assertThat(idValues, hasSize(0)); assertThat(idValues, hasSize(0));

View File

@ -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_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_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")); 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);
} }
} }

View File

@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.term;
import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInRelativeOrder; import static org.hamcrest.Matchers.containsInRelativeOrder;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
@ -12,8 +13,10 @@ import static org.mockito.Mockito.verify;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
@ -40,19 +43,19 @@ import ca.uhn.fhir.util.TestUtil;
public class TerminologyLoaderSvcTest { public class TerminologyLoaderSvcTest {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TerminologyLoaderSvcTest.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TerminologyLoaderSvcTest.class);
private TerminologyLoaderSvc mySvc; private TerminologyLoaderSvc mySvc;
@Mock @Mock
private IHapiTerminologySvc myTermSvc; private IHapiTerminologySvc myTermSvc;
@Captor @Captor
private ArgumentCaptor<TermCodeSystemVersion> myCsvCaptor; private ArgumentCaptor<TermCodeSystemVersion> myCsvCaptor;
@Before @Before
public void before() { public void before() {
mySvc = new TerminologyLoaderSvc(); mySvc = new TerminologyLoaderSvc();
mySvc.setTermSvcForUnitTests(myTermSvc); mySvc.setTermSvcForUnitTests(myTermSvc);
} }
@AfterClass @AfterClass
public static void afterClassClearContext() { public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest(); TestUtil.clearAllStaticFieldsForUnitTest();
@ -62,18 +65,18 @@ public class TerminologyLoaderSvcTest {
public void testLoadLoinc() throws Exception { public void testLoadLoinc() throws Exception {
ByteArrayOutputStream bos1 = new ByteArrayOutputStream(); ByteArrayOutputStream bos1 = new ByteArrayOutputStream();
ZipOutputStream zos1 = new ZipOutputStream(bos1); ZipOutputStream zos1 = new ZipOutputStream(bos1);
addEntry(zos1,"/loinc/", "loinc.csv"); addEntry(zos1, "/loinc/", "loinc.csv");
zos1.close(); zos1.close();
ourLog.info("ZIP file has {} bytes", bos1.toByteArray().length); ourLog.info("ZIP file has {} bytes", bos1.toByteArray().length);
ByteArrayOutputStream bos2 = new ByteArrayOutputStream(); ByteArrayOutputStream bos2 = new ByteArrayOutputStream();
ZipOutputStream zos2 = new ZipOutputStream(bos2); 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(); zos2.close();
ourLog.info("ZIP file has {} bytes", bos2.toByteArray().length); ourLog.info("ZIP file has {} bytes", bos2.toByteArray().length);
RequestDetails details = mock(RequestDetails.class); RequestDetails details = mock(RequestDetails.class);
mySvc.loadLoinc(Arrays.asList(bos1.toByteArray(), bos2.toByteArray()), details); mySvc.loadLoinc(list(bos1.toByteArray(), bos2.toByteArray()), details);
} }
@Test @Test
@ -84,38 +87,48 @@ public class TerminologyLoaderSvcTest {
addEntry(zos, "/sct/", "sct2_Concept_Full-en_INT_20160131.txt"); addEntry(zos, "/sct/", "sct2_Concept_Full-en_INT_20160131.txt");
addEntry(zos, "/sct/", "sct2_Description_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_Identifier_Full_INT_20160131.txt");
addEntry(zos,"/sct/", "sct2_Relationship_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_StatedRelationship_Full_INT_20160131.txt");
addEntry(zos, "/sct/", "sct2_TextDefinition_Full-en_INT_20160131.txt"); addEntry(zos, "/sct/", "sct2_TextDefinition_Full-en_INT_20160131.txt");
zos.close(); zos.close();
ourLog.info("ZIP file has {} bytes", bos.toByteArray().length); ourLog.info("ZIP file has {} bytes", bos.toByteArray().length);
RequestDetails details = mock(RequestDetails.class); 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)); verify(myTermSvc).storeNewCodeSystemVersion(any(String.class), myCsvCaptor.capture(), any(RequestDetails.class));
TermCodeSystemVersion csv = myCsvCaptor.getValue(); TermCodeSystemVersion csv = myCsvCaptor.getValue();
TreeSet<String> allCodes = toCodes(csv); TreeSet<String> allCodes = toCodes(csv, true);
ourLog.info(allCodes.toString()); ourLog.info(allCodes.toString());
assertThat(allCodes, containsInRelativeOrder("116680003")); assertThat(allCodes, containsInRelativeOrder("116680003"));
assertThat(allCodes, not(containsInRelativeOrder("207527008"))); assertThat(allCodes, not(containsInRelativeOrder("207527008")));
allCodes = toCodes(csv, false);
ourLog.info(allCodes.toString());
assertThat(allCodes, hasItem("126816002"));
} }
private TreeSet<String> toCodes(TermCodeSystemVersion theCsv) { private List<byte[]> list(byte[]... theByteArray) {
return new ArrayList<byte[]>(Arrays.asList(theByteArray));
}
private TreeSet<String> toCodes(TermCodeSystemVersion theCsv, boolean theAddChildren) {
TreeSet<String> retVal = new TreeSet<String>(); TreeSet<String> retVal = new TreeSet<String>();
for (TermConcept next : theCsv.getConcepts()) { for (TermConcept next : theCsv.getConcepts()) {
toCodes(retVal, next); toCodes(retVal, next, theAddChildren);
} }
return retVal; return retVal;
} }
private void toCodes(TreeSet<String> theCodes, TermConcept theConcept) { private void toCodes(TreeSet<String> theCodes, TermConcept theConcept, boolean theAddChildren) {
theCodes.add(theConcept.getCode()); theCodes.add(theConcept.getCode());
for (TermConceptParentChildLink next : theConcept.getChildren()) { if (theAddChildren) {
toCodes(theCodes, next.getChild()); for (TermConceptParentChildLink next : theConcept.getChildren()) {
toCodes(theCodes, next.getChild(), theAddChildren);
}
} }
} }
@ -125,9 +138,9 @@ public class TerminologyLoaderSvcTest {
ZipOutputStream zos = new ZipOutputStream(bos); ZipOutputStream zos = new ZipOutputStream(bos);
addEntry(zos, "/sct/", "sct2_StatedRelationship_Full_INT_20160131.txt"); addEntry(zos, "/sct/", "sct2_StatedRelationship_Full_INT_20160131.txt");
zos.close(); zos.close();
ourLog.info("ZIP file has {} bytes", bos.toByteArray().length); ourLog.info("ZIP file has {} bytes", bos.toByteArray().length);
RequestDetails details = mock(RequestDetails.class); RequestDetails details = mock(RequestDetails.class);
try { try {
mySvc.loadSnomedCt(Collections.singletonList(bos.toByteArray()), details); mySvc.loadSnomedCt(Collections.singletonList(bos.toByteArray()), details);
@ -145,6 +158,5 @@ public class TerminologyLoaderSvcTest {
zos.write(byteArray); zos.write(byteArray);
zos.closeEntry(); zos.closeEntry();
} }
} }

View File

@ -13,6 +13,7 @@ id effectiveTime active moduleId definitionStatusId
126813005 20020131 1 900000000000207008 900000000000074008 126813005 20020131 1 900000000000207008 900000000000074008
126813006 20020131 1 900000000000207008 900000000000074008 126813006 20020131 1 900000000000207008 900000000000074008
126817006 20020131 1 900000000000207008 900000000000074008 126817006 20020131 1 900000000000207008 900000000000074008
126816002 20020131 1 900000000000207008 900000000000074008
207527008 20020131 1 900000000000207008 900000000000074008 207527008 20020131 1 900000000000207008 900000000000074008
207527008 20040731 1 900000000000207008 900000000000073002 207527008 20040731 1 900000000000207008 900000000000073002
207527008 20090731 0 900000000000207008 900000000000074008 207527008 20090731 0 900000000000207008 900000000000074008

View File

@ -1,5 +1,6 @@
id effectiveTime active moduleId sourceId destinationId relationshipGroup typeId characteristicTypeId modifierId id effectiveTime active moduleId sourceId destinationId relationshipGroup typeId characteristicTypeId modifierId
100022 20020131 1 900000000000207008 126815003 126813005 0 116680003 900000000000011006 900000000000451002 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 126817006 126815003 0 116680003 900000000000011006 900000000000451002
101021 20020131 1 900000000000207008 126815003 126817006 0 116680003 900000000000011006 900000000000451002 101021 20020131 1 900000000000207008 126815003 126817006 0 116680003 900000000000011006 900000000000451002

View File

@ -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.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu.resource.AllergyIntolerance; import ca.uhn.fhir.model.dstu.resource.AllergyIntolerance;
import ca.uhn.fhir.model.dstu.resource.Binary; 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.Composition;
import ca.uhn.fhir.model.dstu.resource.Condition; import ca.uhn.fhir.model.dstu.resource.Condition;
import ca.uhn.fhir.model.dstu.resource.Conformance; 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.DateTimeDt;
import ca.uhn.fhir.model.primitive.DecimalDt; import ca.uhn.fhir.model.primitive.DecimalDt;
import ca.uhn.fhir.model.primitive.IdDt; 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.InstantDt;
import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator; 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 * Test for #82 - Not yet enabled because the test won't pass
*/ */

View File

@ -32,7 +32,7 @@ import ca.uhn.fhir.rest.client.exceptions.FhirClientInappropriateForServerExcept
import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.TestUtil;
public class ClientServerValidationTestDstu { public class ClientServerValidationDstu1Test {
private FhirContext myCtx; private FhirContext myCtx;
private HttpClient myHttpClient; private HttpClient myHttpClient;

View File

@ -15,7 +15,9 @@ import java.io.InputStream;
import java.io.StringReader; import java.io.StringReader;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.ReaderInputStream; 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.apache.http.message.BasicStatusLine;
import org.hamcrest.Matchers; import org.hamcrest.Matchers;
import org.hamcrest.core.StringContains; import org.hamcrest.core.StringContains;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
@ -82,6 +85,8 @@ public class GenericClientTest {
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
myHttpResponse = mock(HttpResponse.class, new ReturnsDeepStubs()); myHttpResponse = mock(HttpResponse.class, new ReturnsDeepStubs());
System.setProperty(BaseClient.HAPI_CLIENT_KEEPRESPONSES, "true");
} }
private String extractBody(ArgumentCaptor<HttpUriRequest> capt, int count) throws IOException { private String extractBody(ArgumentCaptor<HttpUriRequest> capt, int count) throws IOException {
@ -89,6 +94,24 @@ public class GenericClientTest {
return body; 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() { private String getPatientFeedWithOneResult() {
//@formatter:off //@formatter:off
String msg = "<feed xmlns=\"http://www.w3.org/2005/Atom\">\n" + String msg = "<feed xmlns=\"http://www.w3.org/2005/Atom\">\n" +
@ -155,6 +178,9 @@ public class GenericClientTest {
resp = client.create().resource(ourCtx.newXmlParser().encodeResourceToString(p1)).execute(); resp = client.create().resource(ourCtx.newXmlParser().encodeResourceToString(p1)).execute();
assertNull(resp.getCreated()); assertNull(resp.getCreated());
ourLog.info("lastRequest: {}", ((GenericClient)client).getLastRequest());
ourLog.info("lastResponse: {}", ((GenericClient)client).getLastResponse());
ourLog.info("lastResponseBody: {}", ((GenericClient)client).getLastResponseBody());
} }
@Test @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<HttpUriRequest> 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<IBaseResource> input = new ArrayList<IBaseResource>();
input.addAll(bundle.toListOfResources());
client.transaction(input);
}
@Test @Test
public void testTransactionJson() throws Exception { public void testTransactionJson() throws Exception {
String bundleStr = IOUtils.toString(getClass().getResourceAsStream("/bundle.json")); String bundleStr = IOUtils.toString(getClass().getResourceAsStream("/bundle.json"));

View File

@ -10,11 +10,25 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.reflect.ClassPath; import com.google.common.reflect.ClassPath;
import com.google.common.reflect.ClassPath.ClassInfo; 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; import ca.uhn.fhir.util.TestUtil;
public class ExceptionPropertiesTest { public class ExceptionPropertiesTest {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExceptionPropertiesTest.class); 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") @SuppressWarnings("deprecation")
@Test @Test
public void testExceptionsAreGood() throws Exception { public void testExceptionsAreGood() throws Exception {

View File

@ -11,6 +11,7 @@ import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.dstu2.valueset.AdministrativeGenderEnum; import ca.uhn.fhir.model.dstu2.valueset.AdministrativeGenderEnum;
import ca.uhn.fhir.model.dstu2.valueset.MaritalStatusCodesEnum; import ca.uhn.fhir.model.dstu2.valueset.MaritalStatusCodesEnum;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.TestUtil;
public class FhirContextDstu2Test { public class FhirContextDstu2Test {
@ -23,6 +24,12 @@ public class FhirContextDstu2Test {
TestUtil.clearAllStaticFieldsForUnitTest(); TestUtil.clearAllStaticFieldsForUnitTest();
} }
@Test(expected=DataFormatException.class)
public void testScanInvalid() {
FhirContext ctx = FhirContext.forDstu2();
ctx.getResourceDefinition("InvalidResource");
}
@Test @Test
public void testQueryBoundCode() { public void testQueryBoundCode() {

View File

@ -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());
}
}

View File

@ -37,9 +37,9 @@ import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.TestUtil;
public class DefaultThymeleafNarrativeGeneratorTestDstu2 { public class DefaultThymeleafNarrativeGeneratorDstu2Test {
private static FhirContext ourCtx = FhirContext.forDstu2(); 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; private DefaultThymeleafNarrativeGenerator myGen;
@AfterClass @AfterClass

View File

@ -1,7 +1,12 @@
package ca.uhn.fhir.rest.client; package ca.uhn.fhir.rest.client;
import static org.hamcrest.Matchers.containsString; 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.mock;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; 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.Conformance;
import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.primitive.UriDt; 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.exceptions.FhirClientInappropriateForServerException;
import ca.uhn.fhir.rest.client.interceptor.BasicAuthInterceptor; import ca.uhn.fhir.rest.client.interceptor.BasicAuthInterceptor;
import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
import ca.uhn.fhir.util.TestUtil; 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 FhirContext myCtx;
private boolean myFirstResponse; private boolean myFirstResponse;
private HttpClient myHttpClient; private HttpClient myHttpClient;

View File

@ -19,6 +19,7 @@ import java.nio.charset.Charset;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.HashMap;
import java.util.List; import java.util.List;
import org.apache.commons.io.IOUtils; 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.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer; import org.mockito.stubbing.Answer;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle; import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.ExtensionDt; 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.Include;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.dstu2.composite.IdentifierDt; import ca.uhn.fhir.model.dstu2.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu2.composite.MetaDt; 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.Entry;
import ca.uhn.fhir.model.dstu2.resource.Bundle.Link; 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.Rest;
import ca.uhn.fhir.model.dstu2.resource.Conformance.RestSecurity; 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.Encounter;
import ca.uhn.fhir.model.dstu2.resource.Observation; import ca.uhn.fhir.model.dstu2.resource.Observation;
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome; 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.InstantDt;
import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.UriDt; import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.parser.XmlParserDstu2Test; import ca.uhn.fhir.parser.XmlParserDstu2Test;
import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.PreferReturnEnum; import ca.uhn.fhir.rest.api.PreferReturnEnum;
import ca.uhn.fhir.rest.api.SummaryEnum; import ca.uhn.fhir.rest.api.SummaryEnum;
import ca.uhn.fhir.rest.client.apache.ApacheRestfulClientFactory; 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.exceptions.InvalidResponseException;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.method.SearchStyleEnum; 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.param.DateRangeParam;
import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.EncodingEnum;
@ -84,28 +91,29 @@ import ca.uhn.fhir.util.TestUtil;
public class GenericClientDstu2Test { public class GenericClientDstu2Test {
private static FhirContext ourCtx; 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 HttpClient myHttpClient;
private HttpResponse myHttpResponse; private HttpResponse myHttpResponse;
private int myResponseCount = 0; private int myResponseCount = 0;
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@Before @Before
public void before() { public void before() {
myHttpClient = mock(HttpClient.class, new ReturnsDeepStubs()); myHttpClient = mock(HttpClient.class, new ReturnsDeepStubs());
ourCtx.setRestfulClientFactory(new ApacheRestfulClientFactory(ourCtx)); 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().setHttpClient(myHttpClient);
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
myHttpResponse = mock(HttpResponse.class, new ReturnsDeepStubs()); myHttpResponse = mock(HttpResponse.class, new ReturnsDeepStubs());
myResponseCount = 0; myResponseCount = 0;
System.setProperty(BaseClient.HAPI_CLIENT_KEEPRESPONSES, "true");
} }
private String extractBody(ArgumentCaptor<HttpUriRequest> capt, int count) throws IOException { private String extractBody(ArgumentCaptor<HttpUriRequest> capt, int count) throws IOException {
@ -113,7 +121,6 @@ public class GenericClientDstu2Test {
return body; return body;
} }
private String getPatientFeedWithOneResult() { private String getPatientFeedWithOneResult() {
//@formatter:off //@formatter:off
String msg = "<Bundle xmlns=\"http://hl7.org/fhir\">\n" + String msg = "<Bundle xmlns=\"http://hl7.org/fhir\">\n" +
@ -134,7 +141,7 @@ public class GenericClientDstu2Test {
//@formatter:on //@formatter:on
return msg; return msg;
} }
@Test @Test
public void testAcceptHeaderFetchConformance() throws Exception { public void testAcceptHeaderFetchConformance() throws Exception {
IParser p = ourCtx.newXmlParser(); IParser p = ourCtx.newXmlParser();
@ -177,70 +184,6 @@ public class GenericClientDstu2Test {
idx++; idx++;
} }
/**
* See #322
*/
@Test
public void testFetchConformanceWithSmartExtensions() throws Exception {
final String respString = IOUtils.toString(GenericClientDstu2Test.class.getResourceAsStream("/conformance_322.json"));
ArgumentCaptor<HttpUriRequest> 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<ReaderInputStream>() {
@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<ExtensionDt> ext = security.getUndeclaredExtensionsByUrl("http://fhir-registry.smarthealthit.org/StructureDefinition/oauth-uris");
List<ExtensionDt> 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<HttpUriRequest> 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<ReaderInputStream>() {
@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<ExtensionDt> ext = security.getUndeclaredExtensionsByUrl("http://fhir-registry.smarthealthit.org/StructureDefinition/oauth-uris");
List<ExtensionDt> 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 @Test
public void testAcceptHeaderPreflightConformance() throws Exception { public void testAcceptHeaderPreflightConformance() throws Exception {
String methodName = "testAcceptHeaderPreflightConformance"; String methodName = "testAcceptHeaderPreflightConformance";
@ -540,6 +483,30 @@ public class GenericClientDstu2Test {
assertEquals("Patient/123", output.getResource().getIdElement().toUnqualifiedVersionless().getValue()); assertEquals("Patient/123", output.getResource().getIdElement().toUnqualifiedVersionless().getValue());
} }
@Test
public void testDeleteByResource() throws Exception {
ArgumentCaptor<HttpUriRequest> 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<ReaderInputStream>() {
@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 @Test
public void testDeleteConditional() throws Exception { public void testDeleteConditional() throws Exception {
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class); ArgumentCaptor<HttpUriRequest> 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") @SuppressWarnings("deprecation")
@Test @Test
public void testDeleteNonFluent() throws Exception { 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<HttpUriRequest> 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<ReaderInputStream>() {
@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<ExtensionDt> ext = security.getUndeclaredExtensionsByUrl("http://fhir-registry.smarthealthit.org/StructureDefinition/oauth-uris");
List<ExtensionDt> 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<HttpUriRequest> 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<ReaderInputStream>() {
@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<ExtensionDt> ext = security.getUndeclaredExtensionsByUrl("http://fhir-registry.smarthealthit.org/StructureDefinition/oauth-uris");
List<ExtensionDt> 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 @Test
public void testHistory() throws Exception { public void testHistory() throws Exception {
@ -709,6 +781,16 @@ public class GenericClientDstu2Test {
idx++; 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 @Test
public void testMetaAdd() throws Exception { public void testMetaAdd() throws Exception {
IParser p = ourCtx.newXmlParser(); IParser p = ourCtx.newXmlParser();
@ -1504,10 +1586,10 @@ public class GenericClientDstu2Test {
} }
@Test @Test
public void testProviderWhereWeForgotToSetTheContext() throws Exception { public void testProviderWhereWeForgotToSetTheContext() throws Exception {
ApacheRestfulClientFactory clientFactory = new ApacheRestfulClientFactory(); // no ctx ApacheRestfulClientFactory clientFactory = new ApacheRestfulClientFactory(); // no ctx
clientFactory.setServerValidationMode(ServerValidationModeEnum.NEVER); clientFactory.setServerValidationMode(ServerValidationModeEnum.NEVER);
ourCtx.setRestfulClientFactory(clientFactory); ourCtx.setRestfulClientFactory(clientFactory);
try { try {
@ -1576,6 +1658,24 @@ public class GenericClientDstu2Test {
assertEquals("FAM", response.getName().get(0).getFamily().get(0).getValue()); 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 @Test
public void testReadUpdatedHeaderDoesntOverwriteResourceValue() throws Exception { 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<HttpUriRequest> 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<HttpUriRequest> 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<HttpUriRequest> 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 @Test
public void testSearchByNumber() throws Exception { 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\"}]}}]}"; 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 @Override
public InputStream answer(InvocationOnMock theInvocation) throws Throwable { public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")); return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
}}); }
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
int idx = 0; int idx = 0;
//@formatter:off //@formatter:off
client.search() client.search()
.forResource("Encounter") .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<HttpUriRequest> 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<HttpUriRequest> 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<HttpUriRequest> 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 @Test
public void testSearchByUrl() throws Exception { 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<HttpUriRequest> 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<String, List<IQueryParameterType>> params = new HashMap<String, List<IQueryParameterType>>();
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 @Test
public void testSearchWithProfileAndSecurity() throws Exception { 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\"}]}}]}"; 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; return (OperationOutcome) theOperationOutcome;
} }
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@BeforeClass @BeforeClass
public static void beforeClass() { public static void beforeClass() {
ourCtx = FhirContext.forDstu2(); ourCtx = FhirContext.forDstu2();
} }
public final static class RestfulClientInstance implements IRestfulClient {
@Override
public <T extends IBaseResource> T fetchResourceFromUrl(Class<T> 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
}
}
} }

View File

@ -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());
}
}
}

View File

@ -18,6 +18,7 @@ import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Update; 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.api.MethodOutcome;
import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.util.TestUtil; 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 { public static class OperationReturningOldBundleProvider implements IResourceProvider {
@Override @Override
@ -90,4 +136,46 @@ public class ServerInvalidDefinitionDstu2Test {
} }
public static class UpdateWithWrongResourceType implements IResourceProvider {
@Override
public Class<? extends IBaseResource> getResourceType() {
return Patient.class;
}
@Update
public MethodOutcome update(@ResourceParam Integer theParam2) {
return null;
}
}
public static class ValidateWithWrongModeType implements IResourceProvider {
@Override
public Class<? extends IBaseResource> 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<? extends IBaseResource> getResourceType() {
return Patient.class;
}
@Validate
public MethodOutcome update(@ResourceParam Patient thePatient, @Validate.Profile Integer theParam2) {
return null;
}
}
} }

View File

@ -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.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu2.resource.Bundle; import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.Observation; 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.resource.Patient;
import ca.uhn.fhir.model.dstu2.valueset.BundleTypeEnum; import ca.uhn.fhir.model.dstu2.valueset.BundleTypeEnum;
import ca.uhn.fhir.model.dstu2.valueset.HTTPVerbEnum; 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.Create;
import ca.uhn.fhir.rest.annotation.Delete; import ca.uhn.fhir.rest.annotation.Delete;
import ca.uhn.fhir.rest.annotation.IdParam; 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.Read;
import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.annotation.Search;
@ -158,6 +160,215 @@ public class AuthorizationInterceptorDstu2Test {
assertTrue(ourHitMethod); assertTrue(ourHitMethod);
} }
@Test
public void testOperationServerLevel() throws Exception {
ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> 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<IAuthRule> 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<IAuthRule> 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<IAuthRule> 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 @Test
public void testDenyAll() throws Exception { public void testDenyAll() throws Exception {
ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@ -762,6 +973,17 @@ public class AuthorizationInterceptorDstu2Test {
retVal.setResource(theResource); retVal.setResource(theResource);
return retVal; 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); retVal.setResource(theResource);
return retVal; 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 public static class PlainProvider
{ {
@Operation(name="opName", idempotent=true)
public Parameters operation() {
ourHitMethod = true;
return (Parameters) new Parameters().setId("1");
}
@Transaction() @Transaction()
public Bundle search(@TransactionParam Bundle theInput) { public Bundle search(@TransactionParam Bundle theInput) {
ourHitMethod = true; ourHitMethod = true;

View File

@ -24,7 +24,7 @@ public class FhirContextDstu3Test {
public static void afterClassClearContext() { public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest(); TestUtil.clearAllStaticFieldsForUnitTest();
} }
/** /**
* See #344 * See #344
*/ */

View File

@ -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 * #%L
@ -20,16 +25,15 @@ package ca.uhn.fhir.validation;
* #L% * #L%
*/ */
public class Problem { /**
* Adapter implementation with NOP implementations of all {@link IParserErrorHandler} methods.
private String myDescription; */
public class ErrorHandlerAdapterTest {
public Problem(String theDescription) {
myDescription=theDescription; @Test
public void testMethods() {
new ErrorHandlerAdapter().unexpectedRepeatingElement(null, null);
new ErrorHandlerAdapter().unknownAttribute(null, null);
new ErrorHandlerAdapter().unknownElement(null, null);
} }
public String getDescription() {
return myDescription;
}
} }

View File

@ -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<HttpUriRequest> 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<HttpUriRequest> 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<ReaderInputStream>() {
@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("<OperationOutcome xmlns=\"http://hl7.org/fhir\"><text><div xmlns=\"http://www.w3.org/1999/xhtml\">OK!</div></text></OperationOutcome>", resp);
assertEquals("http://example.com/fhir/$validate", capt.getAllValues().get(idx).getURI().toString());
String request = extractBodyAsString(capt,idx);
assertEquals("<Parameters xmlns=\"http://hl7.org/fhir\"><parameter><name value=\"resource\"/><resource><Patient xmlns=\"http://hl7.org/fhir\"><name><family value=\"FAM\"/></name></Patient></resource></parameter></Parameters>", request);
idx = 1;
outcome = client.validate(patient, ValidationModeEnum.CREATE, "http://foo");
resp = ourCtx.newXmlParser().encodeResourceToString(outcome.getOperationOutcome());
assertEquals("<OperationOutcome xmlns=\"http://hl7.org/fhir\"><text><div xmlns=\"http://www.w3.org/1999/xhtml\">OK!</div></text></OperationOutcome>", resp);
assertEquals("http://example.com/fhir/$validate", capt.getAllValues().get(idx).getURI().toString());
request = extractBodyAsString(capt,idx);
assertEquals("<Parameters xmlns=\"http://hl7.org/fhir\"><parameter><name value=\"resource\"/><resource><Patient xmlns=\"http://hl7.org/fhir\"><name><family value=\"FAM\"/></name></Patient></resource></parameter><parameter><name value=\"mode\"/><valueString value=\"create\"/></parameter><parameter><name value=\"profile\"/><valueString value=\"http://foo\"/></parameter></Parameters>", 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();
}
}

View File

@ -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());
}
}

View File

@ -1,6 +1,7 @@
package ca.uhn.fhir.rest.server; 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.Matchers.any;
import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock; 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.verifyNoMoreInteractions;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletRequest; 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.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.dstu3.model.Patient;
import org.junit.After;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
@ -50,7 +53,7 @@ public class InterceptorDstu3Test {
private static final FhirContext ourCtx = FhirContext.forDstu3(); private static final FhirContext ourCtx = FhirContext.forDstu3();
private static int ourPort; private static int ourPort;
private static Server ourServer; private static Server ourServer;
private static RestfulServer servlet; private static RestfulServer ourServlet;
private IServerInterceptor myInterceptor1; private IServerInterceptor myInterceptor1;
private IServerInterceptor myInterceptor2; private IServerInterceptor myInterceptor2;
@ -58,7 +61,7 @@ public class InterceptorDstu3Test {
public void before() { public void before() {
myInterceptor1 = mock(IServerInterceptor.class); myInterceptor1 = mock(IServerInterceptor.class);
myInterceptor2 = mock(IServerInterceptor.class); myInterceptor2 = mock(IServerInterceptor.class);
servlet.setInterceptors(myInterceptor1, myInterceptor2); ourServlet.setInterceptors(myInterceptor1, myInterceptor2);
} }
@ -121,6 +124,13 @@ public class InterceptorDstu3Test {
ourServer.stop(); ourServer.stop();
TestUtil.clearAllStaticFieldsForUnitTest(); TestUtil.clearAllStaticFieldsForUnitTest();
} }
@After
public void after() {
for (IServerInterceptor next : new ArrayList<IServerInterceptor>(ourServlet.getInterceptors())) {
ourServlet.unregisterInterceptor(next);
}
}
@BeforeClass @BeforeClass
public static void beforeClass() throws Exception { public static void beforeClass() throws Exception {
@ -130,9 +140,9 @@ public class InterceptorDstu3Test {
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();
ServletHandler proxyHandler = new ServletHandler(); ServletHandler proxyHandler = new ServletHandler();
servlet = new RestfulServer(ourCtx); ourServlet = new RestfulServer(ourCtx);
servlet.setResourceProviders(patientProvider); ourServlet.setResourceProviders(patientProvider);
ServletHolder servletHolder = new ServletHolder(servlet); ServletHolder servletHolder = new ServletHolder(ourServlet);
proxyHandler.addServletWithMapping(servletHolder, "/*"); proxyHandler.addServletWithMapping(servletHolder, "/*");
ourServer.setHandler(proxyHandler); ourServer.setHandler(proxyHandler);
ourServer.start(); ourServer.start();

View File

@ -1,8 +1,6 @@
package ca.uhn.fhir.rest.server; package ca.uhn.fhir.rest.server;
import static org.hamcrest.Matchers.stringContainsInOrder;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -26,13 +24,10 @@ import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import ca.uhn.fhir.context.FhirContext; 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.Search;
import ca.uhn.fhir.rest.annotation.Sort; import ca.uhn.fhir.rest.annotation.Sort;
import ca.uhn.fhir.rest.api.SortOrderEnum; import ca.uhn.fhir.rest.api.SortOrderEnum;
import ca.uhn.fhir.rest.api.SortSpec; 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.PortUtil;
import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.TestUtil;

View File

@ -21,14 +21,11 @@ import org.hl7.fhir.dstu3.model.HumanName;
import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import ca.uhn.fhir.context.FhirContext; 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.annotation.Search;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.util.PortUtil; import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.TestUtil;
@ -55,6 +52,55 @@ public class SearchWithServerAddressStrategyDstu3Test {
assertThat(responseContent, containsString("<fullUrl value=\"http://localhost:" + ourPort + "/Patient/1\"/>")); assertThat(responseContent, containsString("<fullUrl value=\"http://localhost:" + ourPort + "/Patient/1\"/>"));
} }
@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("<family value=\"FAMILY\""));
assertThat(responseContent, containsString("<fullUrl value=\"http://localhost:" + ourPort + "/Patient/1\"/>"));
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("<family value=\"FAMILY\""));
assertThat(responseContent, containsString("<fullUrl value=\"http://foo.com/Patient/1\"/>"));
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("<family value=\"FAMILY\""));
assertThat(responseContent, containsString("<fullUrl value=\"https://foo.com/Patient/1\"/>"));
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("<family value=\"FAMILY\""));
assertThat(responseContent, containsString("<fullUrl value=\"https://foo.com/Patient/1\"/>"));
}
@Test @Test
public void testHardcodedAddressStrategy() throws Exception { public void testHardcodedAddressStrategy() throws Exception {
ourServlet.setServerAddressStrategy(new HardcodedServerAddressStrategy("http://example.com/fhir/base")); ourServlet.setServerAddressStrategy(new HardcodedServerAddressStrategy("http://example.com/fhir/base"));

View File

@ -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<? extends IBaseResource> getResourceType() {
return Patient.class;
}
@Search()
public List<Patient> search() {
throw ourException;
}
}
}

View File

@ -30,8 +30,6 @@ import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import ca.uhn.fhir.context.FhirContext; 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.ResourceParam;
import ca.uhn.fhir.rest.annotation.Validate; import ca.uhn.fhir.rest.annotation.Validate;
import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.MethodOutcome;

View File

@ -1,23 +1,20 @@
package ca.uhn.fhir.validation; package ca.uhn.fhir.validation;
import static org.hamcrest.Matchers.containsString; 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.AfterClass;
import org.junit.Test; import org.junit.Test;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.TestUtil;
public class SchemaValidationTestDstu3 { public class SchemaValidationDstu3Test {
private static FhirContext ourCtx = FhirContext.forDstu3(); 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 * See #339

View File

@ -41,6 +41,16 @@
</ul> </ul>
]]> ]]>
</action> </action>
<action type="remove">
Remove some clases that were deprecated over a year ago and have
suitable replacements:
<![CDATA[
<ul>
<li>QualifiedDateParam has been removed, but DateParam may be used instead</li>
<li>PathSpecification has been removedm but Include may be used instead</li>
</ul>
]]>
</action>
<action type="fix" issue="345"> <action type="fix" issue="345">
ResponseValidatingInterceptor threw an InternalErrorException (HTTP 500) for operations ResponseValidatingInterceptor threw an InternalErrorException (HTTP 500) for operations
that do not return any content (e.g. delete). Thanks to Mohammad Jafari for reporting! 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) Server now supports the _at parameter (including multiple repetitions)
for history operation for history operation
</action> </action>
<!--
This one actually doesn't seem possible without using a deprecated servlet API
<action type="fix">
When throwing UnclassifiedServerException in server methods, the HTTP response
status line contained the response code specified in the exception, but not the
response message
</action>
-->
<action type="add">
AuthorizationInterceptor can now allow or deny requests to extended
operations (e.g. $everything)
</action>
<action type="fix"> <action type="fix">
DecimalType used BigDecimal constructor instead of valueOf method to DecimalType used BigDecimal constructor instead of valueOf method to
create a BigDecimal from a double, resulting in weird floating point create a BigDecimal from a double, resulting in weird floating point