Support $validate operatioh in DSTU2 client

This commit is contained in:
jamesagnew 2015-06-16 08:05:27 -04:00
parent 7086508ead
commit 81bfc28147
12 changed files with 454 additions and 217 deletions

View File

@ -12,10 +12,12 @@ import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.Conformance;
import ca.uhn.fhir.model.dstu2.resource.Observation;
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome;
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome.Issue;
import ca.uhn.fhir.model.dstu2.resource.Organization;
import ca.uhn.fhir.model.dstu2.resource.Parameters;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.dstu2.valueset.AdministrativeGenderEnum;
import ca.uhn.fhir.model.dstu2.valueset.IssueSeverityEnum;
import ca.uhn.fhir.model.primitive.DateDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
@ -99,6 +101,29 @@ public class GenericClientExample {
IdDt id = outcome.getId();
// END SNIPPET: createConditional
}
{
// START SNIPPET: validate
Patient patient = new Patient();
patient.addIdentifier().setSystem("http://hospital.com").setValue("123445");
patient.addName().addFamily("Smith").addGiven("John");
// Validate the resource
MethodOutcome outcome = client.validate()
.resource(patient)
.execute();
// The returned object will contain an operation outcome resource
OperationOutcome oo = (OperationOutcome) outcome.getOperationOutcome();
// If the OperationOutcome has any issues with a severity of ERROR or SEVERE,
// the validation failed.
for (Issue nextIssue : oo.getIssue()) {
if (nextIssue.getSeverityElement().getValueAsEnum().ordinal() >= IssueSeverityEnum.ERROR.ordinal()) {
System.out.println("We failed validation!");
}
}
// END SNIPPET: validate
}
{
// START SNIPPET: update
Patient patient = new Patient();

View File

@ -175,10 +175,12 @@ public abstract class BaseClient implements IRestfulClient {
} else if (theEncoding == EncodingEnum.JSON) {
params.put(Constants.PARAM_FORMAT, Collections.singletonList("json"));
}
if (thePrettyPrint == Boolean.TRUE) {
params.put(Constants.PARAM_PRETTY, Collections.singletonList(Constants.PARAM_PRETTY_VALUE_TRUE));
}
EncodingEnum encoding = getEncoding();
if (theEncoding != null) {
encoding=theEncoding;

View File

@ -104,6 +104,8 @@ import ca.uhn.fhir.rest.gclient.IUpdateExecutable;
import ca.uhn.fhir.rest.gclient.IUpdateTyped;
import ca.uhn.fhir.rest.gclient.IUpdateWithQuery;
import ca.uhn.fhir.rest.gclient.IUpdateWithQueryTyped;
import ca.uhn.fhir.rest.gclient.IValidate;
import ca.uhn.fhir.rest.gclient.IValidateUntyped;
import ca.uhn.fhir.rest.method.DeleteMethodBinding;
import ca.uhn.fhir.rest.method.HistoryMethodBinding;
import ca.uhn.fhir.rest.method.HttpDeleteClientInvocation;
@ -117,6 +119,7 @@ import ca.uhn.fhir.rest.method.SearchMethodBinding;
import ca.uhn.fhir.rest.method.SearchStyleEnum;
import ca.uhn.fhir.rest.method.TransactionMethodBinding;
import ca.uhn.fhir.rest.method.ValidateMethodBindingDstu1;
import ca.uhn.fhir.rest.method.ValidateMethodBindingDstu2;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
@ -146,13 +149,13 @@ public class GenericClient extends BaseClient implements IGenericClient {
super(theHttpClient, theServerBase, theFactory);
myContext = theContext;
}
@Override
public BaseConformance conformance() {
if (myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU2_HL7ORG)) {
throw new IllegalArgumentException("Must call conformance(" + IBaseConformance.class.getSimpleName() + ") instead of conformance() for HL7.org structures");
}
HttpGetClientInvocation invocation = MethodUtil.createConformanceInvocation();
if (isKeepResponses()) {
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
@ -166,21 +169,11 @@ public class GenericClient extends BaseClient implements IGenericClient {
return resp;
}
@Override
public void forceConformanceCheck() {
super.forceConformanceCheck();
}
@Override
public ICreate create() {
return new CreateInternal();
}
@Override
public FhirContext getFhirContext() {
return myContext;
}
@Override
public MethodOutcome create(IResource theResource) {
BaseHttpClientInvocation invocation = MethodUtil.createCreateInvocation(theResource, myContext);
@ -221,19 +214,6 @@ public class GenericClient extends BaseClient implements IGenericClient {
return delete(theType, new IdDt(theId));
}
// public IResource read(UriDt url) {
// return read(inferResourceClass(url), url);
// }
//
// @SuppressWarnings("unchecked")
// public <T extends IResource> T read(final Class<T> theType, UriDt url) {
// return (T) invoke(theType, url, new ResourceResponseHandler<T>(theType));
// }
//
// public Bundle search(UriDt url) {
// return search(inferResourceClass(url), url);
// }
private <T extends IBaseResource> T doReadOrVRead(final Class<T> theType, IdDt theId, boolean theVRead, ICallable<T> theNotModifiedHandler, String theIfVersionMatches) {
String resName = toResourceName(theType);
IdDt id = theId;
@ -277,6 +257,34 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
@Override
public IFetchConformanceUntyped fetchConformance() {
return new FetchConformanceInternal();
}
// public IResource read(UriDt url) {
// return read(inferResourceClass(url), url);
// }
//
// @SuppressWarnings("unchecked")
// public <T extends IResource> T read(final Class<T> theType, UriDt url) {
// return (T) invoke(theType, url, new ResourceResponseHandler<T>(theType));
// }
//
// public Bundle search(UriDt url) {
// return search(inferResourceClass(url), url);
// }
@Override
public void forceConformanceCheck() {
super.forceConformanceCheck();
}
@Override
public FhirContext getFhirContext() {
return myContext;
}
public HttpRequestBase getLastRequest() {
return myLastRequest;
}
@ -349,15 +357,15 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
public boolean isLogRequestAndResponse() {
return myLogRequestAndResponse;
}
// @Override
// public <T extends IBaseResource> T read(final Class<T> theType, IdDt theId) {
// return doReadOrVRead(theType, theId, false, null, null);
// }
public boolean isLogRequestAndResponse() {
return myLogRequestAndResponse;
}
@Override
public IGetPage loadPage() {
return new LoadPageInternal();
@ -366,8 +374,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
@Override
public IOperation operation() {
if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1) == false) {
throw new IllegalStateException("Operations are only supported in FHIR DSTU2 and later. This client was created using a context configured for "
+ myContext.getVersion().getVersion().name());
throw new IllegalStateException("Operations are only supported in FHIR DSTU2 and later. This client was created using a context configured for " + myContext.getVersion().getVersion().name());
}
return new OperationInternal();
}
@ -377,11 +384,6 @@ public class GenericClient extends BaseClient implements IGenericClient {
return new ReadInternal();
}
@Override
public IFetchConformanceUntyped fetchConformance() {
return new FetchConformanceInternal();
}
@Override
public <T extends IBaseResource> T read(Class<T> theType, String theId) {
return read(theType, new IdDt(theId));
@ -505,17 +507,20 @@ public class GenericClient extends BaseClient implements IGenericClient {
return update(new IdDt(theId), theResource);
}
@Override
public IValidate validate() {
return new ValidateInternal();
}
@Override
public MethodOutcome validate(IResource theResource) {
BaseHttpClientInvocation invocation;
// if (myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU1)) {
if (myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU1)) {
invocation = ValidateMethodBindingDstu1.createValidateInvocation(theResource, null, myContext);
// } else {
// invocation = ValidateMethodBindingDstu2.createOperationInvocation(theContext, theResourceName, theId, theOperationName, theInput, theUseHttpGet)
// //createValidateInvocation(theResource, null, myContext);
// }
} else {
invocation = ValidateMethodBindingDstu2.createValidateInvocation(myContext, theResource);
}
if (isKeepResponses()) {
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
}
@ -611,7 +616,6 @@ public class GenericClient extends BaseClient implements IGenericClient {
return encoding.newParser(myContext).parseResource(theResourceBody);
}
@SuppressWarnings("unchecked")
@Override
public T prettyPrint() {
@ -620,7 +624,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
}
private final class BundleResponseHandler implements IClientResponseHandler<Bundle> {
private Class<? extends IBaseResource> myType;
@ -630,8 +634,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
@Override
public Bundle invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException,
BaseServerResponseException {
public Bundle invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
if (respType == null) {
throw NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader);
@ -641,15 +644,6 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
}
private final class StringResponseHandler implements IClientResponseHandler<String> {
@Override
public String invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException,
BaseServerResponseException {
return IOUtils.toString(theResponseReader);
}
}
private class CreateInternal extends BaseClientExecutable<ICreateTyped, MethodOutcome> implements ICreate, ICreateTyped, ICreateWithQuery, ICreateWithQueryTyped {
private CriterionList myCriterionList;
@ -851,11 +845,34 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private class FetchConformanceInternal extends BaseClientExecutable implements IFetchConformanceUntyped, IFetchConformanceTyped {
private RuntimeResourceDefinition myType;
@Override
public Object execute() {
ResourceResponseHandler binding = new ResourceResponseHandler(myType.getImplementingClass(), null);
HttpGetClientInvocation invocation = MethodUtil.createConformanceInvocation();
return invokeClient(myContext, binding, invocation, myLogRequestAndResponse);
}
@Override
public <T extends IBaseConformance> IFetchConformanceTyped<T> ofType(Class<T> theResourceType) {
Validate.notNull(theResourceType, "theResourceType must not be null");
myType = myContext.getResourceDefinition(theResourceType);
if (myType == null) {
throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_CANNOT_DETEMINE_RESOURCE_TYPE, theResourceType));
}
return this;
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private class GetPageInternal extends BaseClientExecutable<IGetPageTyped<Object>, Object> implements IGetPageTyped<Object> {
private String myUrl;
private Class<? extends IBaseBundle> myBundleType;
private String myUrl;
public GetPageInternal(String theUrl) {
myUrl = theUrl;
@ -1042,28 +1059,38 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
@SuppressWarnings({"unchecked", "rawtypes"})
@SuppressWarnings({ "unchecked", "rawtypes" })
private final class LoadPageInternal implements IGetPage, IGetPageUntyped {
private static final String PREVIOUS = "previous";
private static final String PREV = "prev";
private static final String PREVIOUS = "previous";
private String myPageUrl;
@Override
public <T extends IBaseBundle> IGetPageTyped andReturnBundle(Class<T> theBundleType) {
Validate.notNull(theBundleType, "theBundleType must not be null");
return new GetPageInternal(myPageUrl, theBundleType);
}
@Override
public IGetPageTyped andReturnDstu1Bundle() {
return new GetPageInternal(myPageUrl);
}
@Override
public IGetPageUntyped byUrl(String thePageUrl) {
if (isBlank(thePageUrl)) {
throw new IllegalArgumentException("thePagingUrl must not be blank or null");
}
myPageUrl = thePageUrl;
return this;
}
@Override
public IGetPageTyped next(Bundle theBundle) {
return new GetPageInternal(theBundle.getLinkNext().getValue());
}
@Override
public IGetPageTyped previous(Bundle theBundle) {
return new GetPageInternal(theBundle.getLinkPrevious().getValue());
}
@Override
public IGetPageTyped url(String thePageUrl) {
return new GetPageInternal(thePageUrl);
}
@Override
public <T extends IBaseBundle> IGetPageTyped<T> next(T theBundle) {
return nextOrPrevious("next", theBundle);
@ -1081,13 +1108,13 @@ public class GenericClient extends BaseClient implements IGenericClient {
if (rel == null || rel.isEmpty()) {
continue;
}
String relation = ((IPrimitiveType<?>)rel.get(0)).getValueAsString();
String relation = ((IPrimitiveType<?>) rel.get(0)).getValueAsString();
if (theWantRel.equals(relation) || (theWantRel == PREVIOUS && PREV.equals(relation))) {
List<IBase> urls = linkDef.getChildByName("url").getAccessor().getValues(nextLink);
if (urls == null || urls.isEmpty()) {
continue;
}
String url = ((IPrimitiveType<?>)urls.get(0)).getValueAsString();
String url = ((IPrimitiveType<?>) urls.get(0)).getValueAsString();
if (isBlank(url)) {
continue;
}
@ -1096,30 +1123,20 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
throw new IllegalArgumentException(myContext.getLocalizer().getMessage(GenericClient.class, "noPagingLinkFoundInBundle", theWantRel));
}
@Override
public IGetPageTyped previous(Bundle theBundle) {
return new GetPageInternal(theBundle.getLinkPrevious().getValue());
}
@Override
public <T extends IBaseBundle> IGetPageTyped<T> previous(T theBundle) {
return nextOrPrevious(PREVIOUS, theBundle);
}
@Override
public IGetPageUntyped byUrl(String thePageUrl) {
if (isBlank(thePageUrl)) {
throw new IllegalArgumentException("thePagingUrl must not be blank or null");
}
myPageUrl = thePageUrl;
return this;
}
@Override
public IGetPageTyped andReturnDstu1Bundle() {
return new GetPageInternal(myPageUrl);
}
@Override
public <T extends IBaseBundle> IGetPageTyped andReturnBundle(Class<T> theBundleType) {
Validate.notNull(theBundleType, "theBundleType must not be null");
return new GetPageInternal(myPageUrl, theBundleType);
public IGetPageTyped url(String thePageUrl) {
return new GetPageInternal(thePageUrl);
}
}
@ -1197,11 +1214,9 @@ public class GenericClient extends BaseClient implements IGenericClient {
return this;
}
@SuppressWarnings({ "unchecked" })
@Override
public IOperationUntypedWithInput withParameters(IBaseParameters theParameters) {
Validate.notNull(theParameters, "theParameters can not be null");
myParameters = theParameters;
public IOperationUntypedWithInput useHttpGet() {
myUseHttpGet = true;
return this;
}
@ -1214,16 +1229,17 @@ public class GenericClient extends BaseClient implements IGenericClient {
throw new IllegalArgumentException("theOutputParameterType must refer to a HAPI FHIR Resource type: " + theOutputParameterType.getName());
}
if (!"Parameters".equals(def.getName())) {
throw new IllegalArgumentException("theOutputParameterType must refer to a HAPI FHIR Resource type for a resource named " + "Parameters" + " - " + theOutputParameterType.getName()
+ " is a resource named: " + def.getName());
throw new IllegalArgumentException("theOutputParameterType must refer to a HAPI FHIR Resource type for a resource named " + "Parameters" + " - " + theOutputParameterType.getName() + " is a resource named: " + def.getName());
}
myParameters = (IBaseParameters) def.newInstance();
return this;
}
@SuppressWarnings({ "unchecked" })
@Override
public IOperationUntypedWithInput useHttpGet() {
myUseHttpGet = true;
public IOperationUntypedWithInput withParameters(IBaseParameters theParameters) {
Validate.notNull(theParameters, "theParameters can not be null");
myParameters = theParameters;
return this;
}
@ -1232,8 +1248,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
private final class OperationOutcomeResponseHandler implements IClientResponseHandler<BaseOperationOutcome> {
@Override
public BaseOperationOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException,
BaseServerResponseException {
public BaseOperationOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
if (respType == null) {
return null;
@ -1261,8 +1276,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
@Override
public MethodOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException,
BaseServerResponseException {
public MethodOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
MethodOutcome response = MethodUtil.process2xxResponse(myContext, myResourceName, theResponseStatusCode, theResponseMimeType, theResponseReader, theHeaders);
if (theResponseStatusCode == Constants.STATUS_HTTP_201_CREATED) {
response.setCreated(true);
@ -1402,8 +1416,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
@SuppressWarnings("unchecked")
@Override
public List<IBaseResource> invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException,
BaseServerResponseException {
public List<IBaseResource> invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
Class<? extends IBaseResource> bundleType = myContext.getResourceDefinition("Bundle").getImplementingClass();
ResourceResponseHandler<IBaseResource> handler = new ResourceResponseHandler<IBaseResource>((Class<IBaseResource>) bundleType, null);
@ -1448,14 +1461,14 @@ public class GenericClient extends BaseClient implements IGenericClient {
private String myCompartmentName;
private CriterionList myCriterion = new CriterionList();
private List<Include> myInclude = new ArrayList<Include>();
private List<Include> myRevInclude = new ArrayList<Include>();
private Integer myParamLimit;
private String myResourceId;
private String myResourceName;
private Class<? extends IBaseResource> myResourceType;
private Class<? extends IBaseBundle> myReturnBundleType;
private List<Include> myRevInclude = new ArrayList<Include>();
private SearchStyleEnum mySearchStyle;
private List<SortInternal> mySort = new ArrayList<SortInternal>();
private Class<? extends IBaseBundle> myReturnBundleType;
public SearchInternal() {
myResourceType = null;
@ -1496,8 +1509,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
if (myReturnBundleType == null && myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU2_HL7ORG)) {
throw new IllegalArgumentException("When using the client with HL7.org structures, you must specify "
+ "the bundle return type for the client by adding \".returnBundle(org.hl7.fhir.instance.model.Bundle.class)\" to your search method call before the \".execute()\" method");
throw new IllegalArgumentException("When using the client with HL7.org structures, you must specify " + "the bundle return type for the client by adding \".returnBundle(org.hl7.fhir.instance.model.Bundle.class)\" to your search method call before the \".execute()\" method");
}
IClientResponseHandler<? extends IBase> binding;
@ -1548,6 +1560,21 @@ public class GenericClient extends BaseClient implements IGenericClient {
return this;
}
@Override
public IClientExecutable returnBundle(Class theClass) {
if (theClass == null) {
throw new NullPointerException("theClass must not be null");
}
myReturnBundleType = theClass;
return this;
}
@Override
public IQuery revInclude(Include theInclude) {
myRevInclude.add(theInclude);
return this;
}
private void setType(Class<? extends IBaseResource> theResourceType) {
myResourceType = theResourceType;
RuntimeResourceDefinition definition = myContext.getResourceDefinition(theResourceType);
@ -1585,21 +1612,6 @@ public class GenericClient extends BaseClient implements IGenericClient {
return this;
}
@Override
public IQuery revInclude(Include theInclude) {
myRevInclude.add(theInclude);
return this;
}
@Override
public IClientExecutable returnBundle(Class theClass) {
if (theClass == null) {
throw new NullPointerException("theClass must not be null");
}
myReturnBundleType = theClass;
return this;
}
}
@SuppressWarnings("rawtypes")
@ -1644,11 +1656,18 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
private final class StringResponseHandler implements IClientResponseHandler<String> {
@Override
public String invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
return IOUtils.toString(theResponseReader);
}
}
private final class TagListResponseHandler implements IClientResponseHandler<TagList> {
@Override
public TagList invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException,
BaseServerResponseException {
public TagList invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
if (respType == null) {
throw NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader);
@ -1660,24 +1679,24 @@ public class GenericClient extends BaseClient implements IGenericClient {
private final class TransactionExecutable<T> extends BaseClientExecutable<ITransactionTyped<T>, T> implements ITransactionTyped<T> {
private Bundle myBundle;
private List<? extends IBaseResource> myResources;
private IBaseBundle myBaseBundle;
private Bundle myBundle;
private String myRawBundle;
private EncodingEnum myRawBundleEncoding;
private List<? extends IBaseResource> myResources;
public TransactionExecutable(Bundle theResources) {
myBundle = theResources;
}
public TransactionExecutable(List<? extends IBaseResource> theResources) {
myResources = theResources;
}
public TransactionExecutable(IBaseBundle theBundle) {
myBaseBundle = theBundle;
}
public TransactionExecutable(List<? extends IBaseResource> theResources) {
myResources = theResources;
}
public TransactionExecutable(String theBundle) {
myRawBundle = theBundle;
myRawBundleEncoding = MethodUtil.detectEncodingNoDefault(myRawBundle);
@ -1729,9 +1748,9 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
@Override
public ITransactionTyped<List<IBaseResource>> withResources(List<? extends IBaseResource> theResources) {
Validate.notNull(theResources, "theResources must not be null");
return new TransactionExecutable<List<IBaseResource>>(theResources);
public ITransactionTyped<String> withBundle(String theBundle) {
Validate.notBlank(theBundle, "theBundle must not be null");
return new TransactionExecutable<String>(theBundle);
}
@Override
@ -1741,9 +1760,9 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
@Override
public ITransactionTyped<String> withBundle(String theBundle) {
Validate.notBlank(theBundle, "theBundle must not be null");
return new TransactionExecutable<String>(theBundle);
public ITransactionTyped<List<IBaseResource>> withResources(List<? extends IBaseResource> theResources) {
Validate.notNull(theResources, "theResources must not be null");
return new TransactionExecutable<List<IBaseResource>>(theResources);
}
}
@ -1856,32 +1875,42 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private class FetchConformanceInternal extends BaseClientExecutable implements IFetchConformanceUntyped, IFetchConformanceTyped {
private RuntimeResourceDefinition myType;
private class ValidateInternal extends BaseClientExecutable<IValidateUntyped, MethodOutcome> implements IValidate, IValidateUntyped {
private IBaseResource myResource;
@Override
public Object execute() {
ResourceResponseHandler binding = new ResourceResponseHandler(myType.getImplementingClass(), null);
HttpGetClientInvocation invocation = MethodUtil.createConformanceInvocation();
return invokeClient(myContext, binding, invocation, myLogRequestAndResponse);
public MethodOutcome execute() {
BaseHttpClientInvocation invocation = ValidateMethodBindingDstu2.createValidateInvocation(myContext, myResource);
ResourceResponseHandler<BaseOperationOutcome> handler = new ResourceResponseHandler<BaseOperationOutcome>(null, null);
BaseOperationOutcome outcome = invoke(null, handler, invocation);
MethodOutcome retVal = new MethodOutcome();
retVal.setOperationOutcome(outcome);
return retVal;
}
@Override
public IValidateUntyped resource(IBaseResource theResource) {
Validate.notNull(theResource, "theResource must not be null");
myResource = theResource;
return this;
}
@Override
public <T extends IBaseConformance> IFetchConformanceTyped<T> ofType(Class<T> theResourceType) {
Validate.notNull(theResourceType, "theResourceType must not be null");
myType = myContext.getResourceDefinition(theResourceType);
if (myType == null) {
throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_CANNOT_DETEMINE_RESOURCE_TYPE, theResourceType));
public IValidateUntyped resource(String theResourceRaw) {
Validate.notBlank(theResourceRaw, "theResourceRaw must not be null or blank");
myResource = parseResourceBody(theResourceRaw);
EncodingEnum enc = MethodUtil.detectEncodingNoDefault(theResourceRaw);
if (enc==null) {
throw new IllegalArgumentException("Could not detect encoding (XML/JSON) in string. Is this a valid FHIR resource?");
}
switch (enc) {
case XML:
encodedXml();
break;
case JSON:
encodedJson();
break;
}
return this;
}

View File

@ -47,6 +47,7 @@ import ca.uhn.fhir.rest.gclient.IRead;
import ca.uhn.fhir.rest.gclient.ITransaction;
import ca.uhn.fhir.rest.gclient.IUntypedQuery;
import ca.uhn.fhir.rest.gclient.IUpdate;
import ca.uhn.fhir.rest.gclient.IValidate;
public interface IGenericClient extends IRestfulClient {
@ -238,6 +239,7 @@ public interface IGenericClient extends IRestfulClient {
/**
* Register a new interceptor for this client. An interceptor can be used to add additional logging, or add security headers, or pre-process responses, etc.
*/
@Override
void registerInterceptor(IClientInterceptor theInterceptor);
IUntypedQuery search();
@ -278,6 +280,11 @@ public interface IGenericClient extends IRestfulClient {
*/
ITransaction transaction();
/**
* Validate a resource
*/
IValidate validate();
/**
* Implementation of the "transaction" method.
*
@ -293,6 +300,7 @@ public interface IGenericClient extends IRestfulClient {
/**
* Remove an intercaptor that was previously registered using {@link IRestfulClient#registerInterceptor(IClientInterceptor)}
*/
@Override
void unregisterInterceptor(IClientInterceptor theInterceptor);
/**

View File

@ -0,0 +1,37 @@
package ca.uhn.fhir.rest.gclient;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import org.hl7.fhir.instance.model.api.IBaseResource;
public interface IValidate {
/**
* Use a resource as validate input
*/
IValidateUntyped resource(IBaseResource theResource);
/**
* Use a raw resource as validate input
*/
IValidateUntyped resource(String theRawResource);
}

View File

@ -0,0 +1,30 @@
package ca.uhn.fhir.rest.gclient;
import ca.uhn.fhir.rest.api.MethodOutcome;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public interface IValidateUntyped extends IClientExecutable<IValidateUntyped, MethodOutcome> {
// nothing for now
}

View File

@ -66,14 +66,16 @@ public class ValidateMethodBindingDstu2 extends OperationMethodBinding {
}
public static BaseHttpClientInvocation createValidationMethodBinding(FhirContext theContext, IBaseResource theResource) {
public static BaseHttpClientInvocation createValidateInvocation(FhirContext theContext, IBaseResource theResource) {
IBaseParameters parameters = (IBaseParameters) theContext.getResourceDefinition("Parameters").newInstance();
OperationParameter.addParameterToParameters(theContext, parameters, theResource, "resource");
String resourceName = theContext.getResourceDefinition(theResource).getName();
String resourceId = theResource.getIdElement().getIdPart();
return createOperationInvocation(theContext, resourceName, resourceId, Constants.EXTOP_VALIDATE, parameters, false);
BaseHttpClientInvocation retVal = createOperationInvocation(theContext, resourceName, resourceId, Constants.EXTOP_VALIDATE, parameters, false);
return retVal;
}
}

View File

@ -47,7 +47,6 @@ import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.ValidationModeEnum;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.ValidationResult;
@ -182,14 +181,6 @@ public class JpaResourceProviderDstu2<T extends IResource> extends BaseJpaResour
oo.getIssue().add((Issue) next);
}
if (oo.getIssue().size() > 0) {
/*
* It is also possible to pass an OperationOutcome resource to the UnprocessableEntityException if you want to return a custom populated OperationOutcome. Otherwise, a simple one is
* created using the string supplied below.
*/
throw new UnprocessableEntityException("Validation failed", oo);
}
// This method returns a MethodOutcome object
MethodOutcome retVal = new MethodOutcome();
oo.addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDetails("Validation succeeded");

View File

@ -69,7 +69,7 @@ public class GenericClientTest {
myHttpClient = mock(HttpClient.class, new ReturnsDeepStubs());
ourCtx.getRestfulClientFactory().setHttpClient(myHttpClient);
ourCtx.getRestfulClientFactory().setServerValidationModeEnum(ServerValidationModeEnum.NEVER);
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
myHttpResponse = mock(HttpResponse.class, new ReturnsDeepStubs());
}
@ -291,7 +291,7 @@ public class GenericClientTest {
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
MethodOutcome outcome = client.create(p1);
MethodOutcome outcome = client.create().resource(p1).execute();
assertEquals("44", outcome.getId().getIdPart());
assertEquals("22", outcome.getId().getVersionIdPart());
@ -1177,7 +1177,7 @@ public class GenericClientTest {
//@formatter:on
assertEquals("http://example.com/fhir", capt.getValue().getURI().toString());
assertEquals(bundle.getEntries().get(0).getId(), response.getEntries().get(0).getId());
assertEquals(bundle.getEntries().get(0).getResource().getId(), response.getEntries().get(0).getResource().getId());
assertEquals(EncodingEnum.XML.getBundleContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, capt.getAllValues().get(0).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
}
@ -1210,7 +1210,7 @@ public class GenericClientTest {
assertEquals("http://example.com/fhir?_format=json", value.getURI().toString());
assertThat(IOUtils.toString(value.getEntity().getContent()), StringContains.containsString("\"resourceType\""));
assertEquals(bundle.getEntries().get(0).getId(), response.getEntries().get(0).getId());
assertEquals(bundle.getEntries().get(0).getResource().getId(), response.getEntries().get(0).getResource().getId());
}
@Test

View File

@ -16,7 +16,6 @@ import org.apache.commons.io.input.ReaderInputStream;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
@ -36,12 +35,14 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.dstu2.resource.Observation;
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome;
import ca.uhn.fhir.model.dstu2.resource.Parameters;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.primitive.DateDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum;
@ -339,7 +340,7 @@ public class GenericClientDstu2Test {
assertEquals("http://example.com/fhir/Patient/123/$SOMEOPERATION?param1=STRINGVALIN1&param1=STRINGVALIN1b&param2=STRINGVALIN2", capt.getAllValues().get(idx).getURI().toASCIIString());
idx++;
}
@Test
public void testOperationAsGetWithNoInParameters() throws Exception {
IParser p = ourCtx.newXmlParser();
@ -419,6 +420,48 @@ public class GenericClientDstu2Test {
idx++;
}
@Test
public void testOperationWithBundleResponseJson() throws Exception {
final String resp = "{\n" +
" \"resourceType\":\"Bundle\",\n" +
" \"id\":\"8cef5f2a-0ba9-43a5-be26-c8dde9ff0e19\",\n" +
" \"base\":\"http://fhirtest.uhn.ca/baseDstu2\"\n" +
"}";
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(resp), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://fhirtest.uhn.ca/baseDstu2");
client.registerInterceptor(new LoggingInterceptor(true));
// Create the input parameters to pass to the server
Parameters inParams = new Parameters();
inParams.addParameter().setName("start").setValue(new DateDt("2001-01-01"));
inParams.addParameter().setName("end").setValue(new DateDt("2015-03-01"));
// Invoke $everything on "Patient/1"
Parameters outParams = client.operation().onInstance(new IdDt("Patient", "18066")).named("$everything").withParameters(inParams).execute();
/*
* Note that the $everything operation returns a Bundle instead of a Parameters resource. The client operation methods return a Parameters instance however, so HAPI creates a Parameters object
* with a single parameter containing the value.
*/
ca.uhn.fhir.model.dstu2.resource.Bundle responseBundle = (ca.uhn.fhir.model.dstu2.resource.Bundle) outParams.getParameter().get(0).getResource();
// Print the response bundle
assertEquals("8cef5f2a-0ba9-43a5-be26-c8dde9ff0e19", responseBundle.getId().getIdPart());
}
@Test
public void testOperationWithBundleResponseXml() throws Exception {
IParser p = ourCtx.newXmlParser();
@ -464,48 +507,6 @@ public class GenericClientDstu2Test {
idx++;
}
@Test
public void testOperationWithBundleResponseJson() throws Exception {
final String resp = "{\n" +
" \"resourceType\":\"Bundle\",\n" +
" \"id\":\"8cef5f2a-0ba9-43a5-be26-c8dde9ff0e19\",\n" +
" \"base\":\"http://fhirtest.uhn.ca/baseDstu2\"\n" +
"}";
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(resp), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://fhirtest.uhn.ca/baseDstu2");
client.registerInterceptor(new LoggingInterceptor(true));
// Create the input parameters to pass to the server
Parameters inParams = new Parameters();
inParams.addParameter().setName("start").setValue(new DateDt("2001-01-01"));
inParams.addParameter().setName("end").setValue(new DateDt("2015-03-01"));
// Invoke $everything on "Patient/1"
Parameters outParams = client.operation().onInstance(new IdDt("Patient", "18066")).named("$everything").withParameters(inParams).execute();
/*
* Note that the $everything operation returns a Bundle instead of a Parameters resource. The client operation methods return a Parameters instance however, so HAPI creates a Parameters object
* with a single parameter containing the value.
*/
ca.uhn.fhir.model.dstu2.resource.Bundle responseBundle = (ca.uhn.fhir.model.dstu2.resource.Bundle) outParams.getParameter().get(0).getResource();
// Print the response bundle
assertEquals("8cef5f2a-0ba9-43a5-be26-c8dde9ff0e19", responseBundle.getId().getIdPart());
}
@Test
public void testOperationWithListOfParameterResponse() throws Exception {
IParser p = ourCtx.newXmlParser();
@ -1020,6 +1021,95 @@ public class GenericClientDstu2Test {
}
@Test
public void testValidateNonFluent() throws Exception {
OperationOutcome oo = new OperationOutcome();
oo.addIssue().setDetails("FOOBAR");
final String msg = ourCtx.newXmlParser().encodeResourceToString(oo);
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<InputStream>() {
@Override
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
Patient p = new Patient();
p.addName().addGiven("GIVEN");
int idx = 0;
MethodOutcome response;
//@formatter:off
response = client.validate(p);
//@formatter:on
assertEquals("http://example.com/fhir/Patient/$validate", capt.getAllValues().get(idx).getURI().toASCIIString());
assertEquals("POST", capt.getAllValues().get(idx).getRequestLine().getMethod());
assertEquals("<Parameters xmlns=\"http://hl7.org/fhir\"><parameter><name value=\"resource\"/><resource><Patient xmlns=\"http://hl7.org/fhir\"><name><given value=\"GIVEN\"/></name></Patient></resource></parameter></Parameters>", extractBody(capt, idx));
assertNotNull(response.getOperationOutcome());
assertEquals("FOOBAR", response.getOperationOutcome().getIssueFirstRep().getDetailsElement().getValue());
idx++;
}
@Test
public void testValidateFluent() throws Exception {
OperationOutcome oo = new OperationOutcome();
oo.addIssue().setDetails("FOOBAR");
final String msg = ourCtx.newXmlParser().encodeResourceToString(oo);
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<InputStream>() {
@Override
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
Patient p = new Patient();
p.addName().addGiven("GIVEN");
int idx = 0;
MethodOutcome response;
response = client.validate().resource(p).execute();
assertEquals("http://example.com/fhir/Patient/$validate", capt.getAllValues().get(idx).getURI().toASCIIString());
assertEquals("POST", capt.getAllValues().get(idx).getRequestLine().getMethod());
assertEquals("<Parameters xmlns=\"http://hl7.org/fhir\"><parameter><name value=\"resource\"/><resource><Patient xmlns=\"http://hl7.org/fhir\"><name><given value=\"GIVEN\"/></name></Patient></resource></parameter></Parameters>", extractBody(capt, idx));
assertNotNull(response.getOperationOutcome());
assertEquals("FOOBAR", response.getOperationOutcome().getIssueFirstRep().getDetailsElement().getValue());
idx++;
response = client.validate().resource(ourCtx.newXmlParser().encodeResourceToString(p)).execute();
assertEquals("http://example.com/fhir/Patient/$validate?_format=xml", capt.getAllValues().get(idx).getURI().toASCIIString());
assertEquals("POST", capt.getAllValues().get(idx).getRequestLine().getMethod());
assertEquals("<Parameters xmlns=\"http://hl7.org/fhir\"><parameter><name value=\"resource\"/><resource><Patient xmlns=\"http://hl7.org/fhir\"><name><given value=\"GIVEN\"/></name></Patient></resource></parameter></Parameters>", extractBody(capt, idx));
assertNotNull(response.getOperationOutcome());
assertEquals("FOOBAR", response.getOperationOutcome().getIssueFirstRep().getDetailsElement().getValue());
idx++;
response = client.validate().resource(ourCtx.newJsonParser().encodeResourceToString(p)).execute();
assertEquals("http://example.com/fhir/Patient/$validate?_format=json", capt.getAllValues().get(idx).getURI().toASCIIString());
assertEquals("POST", capt.getAllValues().get(idx).getRequestLine().getMethod());
assertEquals("{\"resourceType\":\"Parameters\",\"parameter\":[{\"name\":\"resource\",\"resource\":{\"resourceType\":\"Patient\",\"name\":[{\"given\":[\"GIVEN\"]}]}}]}", extractBody(capt, idx));
assertNotNull(response.getOperationOutcome());
assertEquals("FOOBAR", response.getOperationOutcome().getIssueFirstRep().getDetailsElement().getValue());
idx++;
}
@BeforeClass
public static void beforeClass() {
ourCtx = FhirContext.forDstu2();

View File

@ -89,6 +89,12 @@
or "urn:uuid:" / "urn:oid:" (for DSTU2) this is now correctly passed as
the base in resource.getId()
</action>
<action type="add">
Add fluent client method for validate operation, and support the
new DSTU2 style extended operation for $validate if the client is
in DSTU2 mode. Thanks to Eric from the FHIR Skype Implementers chat for
reporting.
</action>
</release>
<release version="1.0" date="2015-May-8">
<action type="add">

View File

@ -461,6 +461,23 @@
</macro>
</subsection>
<subsection name="Built-In Operations - Validate">
<p>
The $validate operation asks the server to test a given resource
to see if it would be acceptable as a create/update on that server.
The client has built-in support for this operation.
</p>
<p class="doc_info_bubble">
If the client is in DSTU1 mode, the method below will invoke the
DSTU1 validation style instead.
</p>
<macro name="snippet">
<param name="id" value="validate" />
<param name="file" value="examples/src/main/java/example/GenericClientExample.java" />
</macro>
</subsection>
</section>
</body>