Add validate method

This commit is contained in:
jamesagnew 2014-04-25 08:23:58 -04:00
parent 15ae763313
commit 6877c0628e
21 changed files with 852 additions and 255 deletions

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>0.3</version> <version>0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>

View File

@ -0,0 +1,53 @@
package ca.uhn.fhir.rest.annotation;
/*
* #%L
* HAPI FHIR Library
* %%
* Copyright (C) 2014 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.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.rest.server.IResourceProvider;
/**
* RESTful method annotation to be used for the FHIR
* <a href="http://hl7.org/implement/standards/fhir/http.html#validate">validate</a> method.
*
* <p>
* Validate is used to accept a resource, and test whether it would be acceptable for
* storing (e.g. using an update or create method)
* </p>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(value=ElementType.METHOD)
public @interface Validate {
/**
* The return type for this method. This generally does not need
* to be populated for a server implementation (using a {@link IResourceProvider},
* since resource providers will return only one resource type per class,
* but generally does need to be populated for client implementations.
*/
// NB: Read, Search (maybe others) share this annotation, so update the javadocs everywhere
Class<? extends IResource> type() default IResource.class;
}

View File

@ -51,6 +51,7 @@ import ca.uhn.fhir.rest.annotation.Metadata;
import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.annotation.Search;
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.client.BaseClientInvocation; import ca.uhn.fhir.rest.client.BaseClientInvocation;
import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException; import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException;
@ -139,8 +140,9 @@ public abstract class BaseMethodBinding {
Update update = theMethod.getAnnotation(Update.class); Update update = theMethod.getAnnotation(Update.class);
Delete delete = theMethod.getAnnotation(Delete.class); Delete delete = theMethod.getAnnotation(Delete.class);
History history = theMethod.getAnnotation(History.class); History history = theMethod.getAnnotation(History.class);
Validate validate = theMethod.getAnnotation(Validate.class);
// ** if you add another annotation above, also add it to the next line: // ** if you add another annotation above, also add it to the next line:
if (!verifyMethodHasZeroOrOneOperationAnnotation(theMethod, read, search, conformance, create, update, delete, history)) { if (!verifyMethodHasZeroOrOneOperationAnnotation(theMethod, read, search, conformance, create, update, delete, history, validate)) {
return null; return null;
} }
@ -188,6 +190,8 @@ public abstract class BaseMethodBinding {
returnTypeFromAnnotation = create.type(); returnTypeFromAnnotation = create.type();
} else if (update != null) { } else if (update != null) {
returnTypeFromAnnotation = update.type(); returnTypeFromAnnotation = update.type();
} else if (validate != null) {
returnTypeFromAnnotation = validate.type();
} }
if (returnTypeFromRp != null) { if (returnTypeFromRp != null) {
@ -232,6 +236,8 @@ public abstract class BaseMethodBinding {
return new DeleteMethodBinding(theMethod, theContext, theProvider); return new DeleteMethodBinding(theMethod, theContext, theProvider);
} else if (history != null) { } else if (history != null) {
return new HistoryMethodBinding(theMethod, theContext, theProvider); return new HistoryMethodBinding(theMethod, theContext, theProvider);
} else if (validate != null) {
return new ValidateMethodBinding(theMethod, theContext, theProvider);
} else { } else {
throw new ConfigurationException("Did not detect any FHIR annotations on method '" + theMethod.getName() + "' on type: " + theMethod.getDeclaringClass().getCanonicalName()); throw new ConfigurationException("Did not detect any FHIR annotations on method '" + theMethod.getName() + "' on type: " + theMethod.getDeclaringClass().getCanonicalName());
} }

View File

@ -24,7 +24,6 @@ import java.io.IOException;
import java.io.PushbackReader; import java.io.PushbackReader;
import java.io.Reader; import java.io.Reader;
import java.io.Writer; import java.io.Writer;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -50,7 +49,6 @@ import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingUtil; import ca.uhn.fhir.rest.server.EncodingUtil;
import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.RestfulServer;
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.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException; import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
@ -220,12 +218,22 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding {
if (!getResourceName().equals(theRequest.getResourceName())) { if (!getResourceName().equals(theRequest.getResourceName())) {
return false; return false;
} }
if (StringUtils.isNotBlank(theRequest.getOperation())) { if (getMatchingOperation() == null && StringUtils.isNotBlank(theRequest.getOperation())) {
return false;
}
if (getMatchingOperation() != null && !getMatchingOperation().equals(theRequest.getOperation())) {
return false; return false;
} }
return true; return true;
} }
/**
* For servers, this method will match only incoming requests
* that match the given operation, or which have no operation in the
* URL if this method returns null.
*/
protected abstract String getMatchingOperation();
/** /**
* Subclasses may override to allow a void method return type, which is allowable for some methods (e.g. delete) * Subclasses may override to allow a void method return type, which is allowable for some methods (e.g. delete)
*/ */

View File

@ -41,7 +41,6 @@ import ca.uhn.fhir.rest.server.EncodingUtil;
import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.RestfulServer;
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.UnprocessableEntityException;
abstract class BaseOutcomeReturningMethodBindingWithResourceParam extends BaseOutcomeReturningMethodBinding { abstract class BaseOutcomeReturningMethodBindingWithResourceParam extends BaseOutcomeReturningMethodBinding {
private int myResourceParameterIndex; private int myResourceParameterIndex;
@ -63,8 +62,7 @@ abstract class BaseOutcomeReturningMethodBindingWithResourceParam extends BaseOu
} }
if (resourceParameter == null) { if (resourceParameter == null) {
throw new ConfigurationException("Method " + theMethod.getName() + " in type " + theMethod.getDeclaringClass().getCanonicalName() + " does not have a parameter annotated with @" throw new ConfigurationException("Method " + theMethod.getName() + " in type " + theMethod.getDeclaringClass().getCanonicalName() + " does not have a parameter annotated with @" + ResourceParam.class.getSimpleName());
+ ResourceParam.class.getSimpleName());
} }
} }
@ -129,14 +127,20 @@ abstract class BaseOutcomeReturningMethodBindingWithResourceParam extends BaseOu
theServer.addHapiHeader(theResponse); theServer.addHapiHeader(theResponse);
theResponse.setContentType(Constants.CT_TEXT); if (response != null && response.getOperationOutcome() != null) {
theResponse.setContentType(encoding.getResourceContentType());
Writer writer = theResponse.getWriter(); Writer writer = theResponse.getWriter();
try { try {
writer.append("Resource has been created"); parser.encodeResourceToWriter(response.getOperationOutcome(), writer);
} finally { } finally {
writer.close();
}
} else {
theResponse.setContentType(Constants.CT_TEXT);
Writer writer = theResponse.getWriter();
writer.close(); writer.close();
} }
// getMethod().in // getMethod().in
} }

View File

@ -64,4 +64,9 @@ public class CreateMethodBinding extends BaseOutcomeReturningMethodBindingWithRe
return new PostClientInvocation(getContext(), resource, urlExtension.toString()); return new PostClientInvocation(getContext(), resource, urlExtension.toString());
} }
@Override
protected String getMatchingOperation() {
return null;
}
} }

View File

@ -143,4 +143,11 @@ public class DeleteMethodBinding extends BaseOutcomeReturningMethodBinding {
theParams[myIdParameterIndex] = new IdDt(id); theParams[myIdParameterIndex] = new IdDt(id);
} }
@Override
protected String getMatchingOperation() {
return null;
}
} }

View File

@ -20,7 +20,6 @@ package ca.uhn.fhir.rest.method;
* #L% * #L%
*/ */
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
@ -36,11 +35,10 @@ import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum; import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum; import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
import ca.uhn.fhir.rest.client.GetClientInvocation; import ca.uhn.fhir.rest.client.GetClientInvocation;
import ca.uhn.fhir.rest.param.IParameter;
import ca.uhn.fhir.rest.param.BaseQueryParameter; import ca.uhn.fhir.rest.param.BaseQueryParameter;
import ca.uhn.fhir.rest.param.IParameter;
import ca.uhn.fhir.rest.param.ParameterUtil; import ca.uhn.fhir.rest.param.ParameterUtil;
import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.Constants;
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.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
@ -134,6 +132,10 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
ourLog.trace("Method {} doesn't match because request type is POST but operation is not _search: {}", theRequest.getId(), theRequest.getOperation()); ourLog.trace("Method {} doesn't match because request type is POST but operation is not _search: {}", theRequest.getId(), theRequest.getOperation());
return false; return false;
} }
if (theRequest.getRequestType() != RequestType.GET && theRequest.getRequestType() != RequestType.POST) {
ourLog.trace("Method {} doesn't match because request type is {}", theRequest.getOperation());
return false;
}
Set<String> methodParamsTemp = new HashSet<String>(); Set<String> methodParamsTemp = new HashSet<String>();
for (int i = 0; i < this.myParameters.size(); i++) { for (int i = 0; i < this.myParameters.size(); i++) {

View File

@ -20,7 +20,7 @@ package ca.uhn.fhir.rest.method;
* #L% * #L%
*/ */
import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.*;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Collections; import java.util.Collections;
@ -41,7 +41,6 @@ import ca.uhn.fhir.rest.client.BaseClientInvocation;
import ca.uhn.fhir.rest.client.PutClientInvocation; import ca.uhn.fhir.rest.client.PutClientInvocation;
import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType; import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceParam { class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceParam {
@ -136,4 +135,10 @@ class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceP
return Collections.singleton(RequestType.PUT); return Collections.singleton(RequestType.PUT);
} }
@Override
protected String getMatchingOperation() {
return null;
}
} }

View File

@ -0,0 +1,102 @@
package ca.uhn.fhir.rest.method;
/*
* #%L
* HAPI FHIR Library
* %%
* Copyright (C) 2014 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.Collections;
import java.util.Set;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.annotation.Validate;
import ca.uhn.fhir.rest.client.BaseClientInvocation;
import ca.uhn.fhir.rest.client.PutClientInvocation;
import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
import ca.uhn.fhir.rest.server.Constants;
class ValidateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceParam {
private Integer myIdParameterIndex;
public ValidateMethodBinding(Method theMethod, FhirContext theContext, Object theProvider) {
super(theMethod, theContext, Validate.class, theProvider);
myIdParameterIndex = Util.findIdParameterIndex(theMethod);
}
@Override
public RestfulOperationTypeEnum getResourceOperationType() {
return RestfulOperationTypeEnum.VALIDATE;
}
@Override
public RestfulOperationSystemEnum getSystemOperationType() {
return null;
}
@Override
protected void addParametersForServerRequest(Request theRequest, Object[] theParams) {
if (myIdParameterIndex != null) {
theParams[myIdParameterIndex] = theRequest.getId();
}
}
@Override
protected BaseClientInvocation createClientInvocation(Object[] theArgs, IResource resource, String resourceName) {
StringBuilder urlExtension = new StringBuilder();
urlExtension.append(resourceName);
urlExtension.append(Constants.PARAM_VALIDATE);
if (myIdParameterIndex != null) {
IdDt idDt = (IdDt) theArgs[myIdParameterIndex];
if (idDt != null && idDt.isEmpty() == false) {
String id = idDt.getValue();
urlExtension.append('/');
urlExtension.append(id);
}
}
PutClientInvocation retVal = new PutClientInvocation(getContext(), resource, urlExtension.toString());
return retVal;
}
@Override
protected boolean allowVoidReturnType() {
return true;
}
@Override
protected Set<RequestType> provideAllowableRequestTypes() {
return Collections.singleton(RequestType.POST);
}
@Override
protected String getMatchingOperation() {
return Constants.PARAM_VALIDATE;
}
}

View File

@ -59,6 +59,7 @@ public class Constants {
public static final String PARAM_SEARCH = "_search"; public static final String PARAM_SEARCH = "_search";
public static final String HEADER_LAST_MODIFIED = "Last-Modified"; public static final String HEADER_LAST_MODIFIED = "Last-Modified";
public static final String HEADER_LAST_MODIFIED_LOWERCASE = HEADER_LAST_MODIFIED.toLowerCase(); public static final String HEADER_LAST_MODIFIED_LOWERCASE = HEADER_LAST_MODIFIED.toLowerCase();
public static final String PARAM_VALIDATE = "_validate";
static { static {
Map<String, EncodingUtil> valToEncoding = new HashMap<String, EncodingUtil>(); Map<String, EncodingUtil> valToEncoding = new HashMap<String, EncodingUtil>();

View File

@ -20,6 +20,14 @@ package ca.uhn.fhir.rest.server.exceptions;
* #L% * #L%
*/ */
/**
* RESTful method exception which corresponds to the <b>HTTP 400 Bad Request</b> status.
* This status indicates that the client's message was invalid (e.g. not a valid FHIR Resource
* per the specifications), as opposed to the {@link InvalidRequestException} which indicates
* that data does not pass business rule validation on the server.
*
* @see UnprocessableEntityException Which should be used for business level validation failures
*/
public class InvalidRequestException extends BaseServerResponseException { public class InvalidRequestException extends BaseServerResponseException {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;

View File

@ -28,6 +28,8 @@ import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
* <p> * <p>
* This exception will generally contain an {@link OperationOutcome} instance which details the failure. * This exception will generally contain an {@link OperationOutcome} instance which details the failure.
* </p> * </p>
*
* @see InvalidRequestException Which corresponds to an <b>HTTP 400 Bad Request</b> failure
*/ */
public class UnprocessableEntityException extends BaseServerResponseException { public class UnprocessableEntityException extends BaseServerResponseException {

View File

@ -23,6 +23,7 @@ import ca.uhn.fhir.model.dstu.resource.Observation;
import ca.uhn.fhir.model.dstu.resource.OperationOutcome; import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
import ca.uhn.fhir.model.dstu.resource.Organization; import ca.uhn.fhir.model.dstu.resource.Organization;
import ca.uhn.fhir.model.dstu.resource.Patient; import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.dstu.valueset.IssueSeverityEnum;
import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum; import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.StringDt;
@ -38,6 +39,7 @@ import ca.uhn.fhir.rest.annotation.RequiredParam;
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;
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.annotation.VersionIdParam; import ca.uhn.fhir.rest.annotation.VersionIdParam;
import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.client.ITestClient; import ca.uhn.fhir.rest.client.ITestClient;
@ -307,6 +309,7 @@ public MethodOutcome createPatient(@ResourceParam Patient thePatient) {
} }
//END SNIPPET: create //END SNIPPET: create
//START SNIPPET: createClient //START SNIPPET: createClient
@Create @Create
public abstract MethodOutcome createNewPatient(@ResourceParam Patient thePatient); public abstract MethodOutcome createNewPatient(@ResourceParam Patient thePatient);
@ -369,6 +372,39 @@ return retVal;
//START SNIPPET: validate
@Validate
public MethodOutcome validatePatient(@ResourceParam Patient thePatient) {
/*
* Actually do our validation: The UnprocessableEntityException
* results in an HTTP 422, which is appropriate for business rule failure
*/
if (thePatient.getIdentifierFirstRep().isEmpty()) {
/* 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("No identifier supplied");
}
// This method returns a MethodOutcome object
MethodOutcome retVal = new MethodOutcome();
// You may also add an OperationOutcome resource to return
// This part is optional though:
OperationOutcome outcome = new OperationOutcome();
outcome.addIssue().setSeverity(IssueSeverityEnum.WARNING).setDetails("One minor issue detected");
retVal.setOperationOutcome(outcome);
return retVal;
}
//END SNIPPET: validate
public static void main(String[] args) throws DataFormatException, IOException { public static void main(String[] args) throws DataFormatException, IOException {
//nothing //nothing
} }

View File

@ -15,12 +15,14 @@
method pattern, with one key difference: A client method pattern, with one key difference: A client
is defined using annotated methods on an interface is defined using annotated methods on an interface
which are used to retrieve resources, which are used to retrieve resources,
whereas a server requires concrete method implementations whereas a server requires concrete method
implementations
to actually provide those resources. to actually provide those resources.
</p> </p>
<p> <p>
Unless otherwise specified, the examples below show <b>server</b> Unless otherwise specified, the examples below show
<b>server</b>
implementations, but client methods will follow the same patterns. implementations, but client methods will follow the same patterns.
</p> </p>
@ -89,8 +91,8 @@
<td> <td>
<a href="#instance_update">Type - Search</a> <a href="#instance_update">Type - Search</a>
<macro name="toc"> <macro name="toc">
<param name="section" value="7"/> <param name="section" value="7" />
<param name="fromDepth" value="2"/> <param name="fromDepth" value="2" />
</macro> </macro>
</td> </td>
<td> <td>
@ -157,7 +159,7 @@
</tbody> </tbody>
</table> </table>
<a name="instance_read"/> <a name="instance_read" />
</section> </section>
<!-- ****************************************************************** --> <!-- ****************************************************************** -->
@ -168,7 +170,9 @@
<p> <p>
The The
<a href="http://hl7.org/implement/standards/fhir/http.html#read"><b>read</b></a> <a href="http://hl7.org/implement/standards/fhir/http.html#read">
<b>read</b>
</a>
operation retrieves a resource by ID. It is annotated with the operation retrieves a resource by ID. It is annotated with the
<a href="./apidocs/ca/uhn/fhir/rest/annotation/Read.html">@Read</a> <a href="./apidocs/ca/uhn/fhir/rest/annotation/Read.html">@Read</a>
annotation, and has a single parameter annotated with the annotation, and has a single parameter annotated with the
@ -182,11 +186,12 @@
</macro> </macro>
<p> <p>
Example URL to invoke this method:<br/> Example URL to invoke this method:
<br />
<code>http://fhir.example.com/Patient/111</code> <code>http://fhir.example.com/Patient/111</code>
</p> </p>
<a name="instance_vread"/> <a name="instance_vread" />
</section> </section>
<!-- ****************************************************************** --> <!-- ****************************************************************** -->
@ -197,7 +202,9 @@
<p> <p>
The The
<a href="http://hl7.org/implement/standards/fhir/http.html#vread"><b>vread</b></a> <a href="http://hl7.org/implement/standards/fhir/http.html#vread">
<b>vread</b>
</a>
operation retrieves a specific version of a resource with a given ID. It looks exactly operation retrieves a specific version of a resource with a given ID. It looks exactly
like a "read" operation, but with a second like a "read" operation, but with a second
parameter annotated with the parameter annotated with the
@ -211,11 +218,12 @@
</macro> </macro>
<p> <p>
Example URL to invoke this method:<br/> Example URL to invoke this method:
<br />
<code>http://fhir.example.com/Patient/111/_history/2</code> <code>http://fhir.example.com/Patient/111/_history/2</code>
</p> </p>
<a name="instance_update"/> <a name="instance_update" />
</section> </section>
<!-- ****************************************************************** --> <!-- ****************************************************************** -->
@ -226,7 +234,9 @@
<p> <p>
The The
<a href="http://hl7.org/implement/standards/fhir/http.html#update"><b>update</b></a> <a href="http://hl7.org/implement/standards/fhir/http.html#update">
<b>update</b>
</a>
operation updates a specific resource instance (using its ID), and optionally operation updates a specific resource instance (using its ID), and optionally
accepts a version ID as well (which can be used to detect version conflicts). accepts a version ID as well (which can be used to detect version conflicts).
</p> </p>
@ -245,7 +255,8 @@
</p> </p>
<p> <p>
Update methods must return an object of type Update methods must return an object of type
<a href="./apidocs/ca/uhn/fhir/rest/api/MethodOutcome.html">MethodOutcome</a>. This <a href="./apidocs/ca/uhn/fhir/rest/api/MethodOutcome.html">MethodOutcome</a>
. This
object contains the identity of the created resource. object contains the identity of the created resource.
</p> </p>
<p> <p>
@ -259,7 +270,8 @@
<p> <p>
Example URL to invoke this method (this would be invoked using an HTTP PUT, Example URL to invoke this method (this would be invoked using an HTTP PUT,
with the resource in the PUT body):<br/> with the resource in the PUT body):
<br />
<code>http://fhir.example.com/Patient</code> <code>http://fhir.example.com/Patient</code>
</p> </p>
@ -283,7 +295,7 @@
<param name="file" value="src/site/example/java/example/RestfulPatientResourceProviderMore.java" /> <param name="file" value="src/site/example/java/example/RestfulPatientResourceProviderMore.java" />
</macro> </macro>
<a name="instance_delete"/> <a name="instance_delete" />
</section> </section>
<!-- ****************************************************************** --> <!-- ****************************************************************** -->
@ -294,7 +306,9 @@
<p> <p>
The The
<a href="http://hl7.org/implement/standards/fhir/http.html#delete"><b>delete</b></a> <a href="http://hl7.org/implement/standards/fhir/http.html#delete">
<b>delete</b>
</a>
operation retrieves a specific version of a resource with a given ID. It takes a single operation retrieves a specific version of a resource with a given ID. It takes a single
ID parameter annotated with an ID parameter annotated with an
<a href="./apidocs/ca/uhn/fhir/rest/annotation/IdParam.html">@IdParam</a> <a href="./apidocs/ca/uhn/fhir/rest/annotation/IdParam.html">@IdParam</a>
@ -311,25 +325,34 @@
</p> </p>
<ul> <ul>
<li> <li>
<b>void</b>: This method may return <code>void</code>, in which case <b>void</b>
: This method may return
<code>void</code>
, in which case
the server will return an empty response and the client will ignore the server will return an empty response and the client will ignore
any successful response from the server (failure responses will still throw any successful response from the server (failure responses will still throw
an exception) an exception)
</li> </li>
<li> <li>
<b><a href="./apidocs/ca/uhn/fhir/rest/api/MethodOutcome.html">MethodOutcome</a></b>: <b>
This method may return <code>MethodOutcome</code>, <a href="./apidocs/ca/uhn/fhir/rest/api/MethodOutcome.html">MethodOutcome</a>
</b>
:
This method may return
<code>MethodOutcome</code>
,
which is a wrapper for the FHIR OperationOutcome resource, which may optionally be returned which is a wrapper for the FHIR OperationOutcome resource, which may optionally be returned
by the server according to the FHIR specification. by the server according to the FHIR specification.
</li> </li>
</ul> </ul>
<p> <p>
Example URL to invoke this method (HTTP DELETE):<br/> Example URL to invoke this method (HTTP DELETE):
<br />
<code>http://fhir.example.com/Patient/111</code> <code>http://fhir.example.com/Patient/111</code>
</p> </p>
<a name="type_create"/> <a name="type_create" />
</section> </section>
<!-- ****************************************************************** --> <!-- ****************************************************************** -->
@ -340,7 +363,9 @@
<p> <p>
The The
<a href="http://hl7.org/implement/standards/fhir/http.html#create"><b>create</b></a> <a href="http://hl7.org/implement/standards/fhir/http.html#create">
<b>create</b>
</a>
operation saves a new resource to the server, allowing the server to operation saves a new resource to the server, allowing the server to
give that resource an ID and version ID. give that resource an ID and version ID.
</p> </p>
@ -353,7 +378,8 @@
</p> </p>
<p> <p>
Create methods must return an object of type Create methods must return an object of type
<a href="./apidocs/ca/uhn/fhir/rest/api/MethodOutcome.html">MethodOutcome</a>. This <a href="./apidocs/ca/uhn/fhir/rest/api/MethodOutcome.html">MethodOutcome</a>
. This
object contains the identity of the created resource. object contains the identity of the created resource.
</p> </p>
<p> <p>
@ -367,7 +393,8 @@
<p> <p>
Example URL to invoke this method (this would be invoked using an HTTP POST, Example URL to invoke this method (this would be invoked using an HTTP POST,
with the resource in the POST body):<br/> with the resource in the POST body):
<br />
<code>http://fhir.example.com/Patient</code> <code>http://fhir.example.com/Patient</code>
</p> </p>
@ -381,7 +408,7 @@
<param name="file" value="src/site/example/java/example/RestfulPatientResourceProviderMore.java" /> <param name="file" value="src/site/example/java/example/RestfulPatientResourceProviderMore.java" />
</macro> </macro>
<a name="type_search"/> <a name="type_search" />
</section> </section>
<!-- ****************************************************************** --> <!-- ****************************************************************** -->
@ -392,7 +419,10 @@
<p> <p>
The The
<a href="http://hl7.org/implement/standards/fhir/http.html#search"><b>search</b></a> operation returns a bundle <a href="http://hl7.org/implement/standards/fhir/http.html#search">
<b>search</b>
</a>
operation returns a bundle
with zero-to-many resources of a given type, matching a given set of parameters. with zero-to-many resources of a given type, matching a given set of parameters.
</p> </p>
@ -401,7 +431,8 @@
<p> <p>
The following example shows a search with no parameters. This operation The following example shows a search with no parameters. This operation
should return all resources of a given type (this obviously doesn't make should return all resources of a given type (this obviously doesn't make
sense in all contexts, but does for some resource types). sense in all contexts, but
does for some resource types).
</p> </p>
<macro name="snippet"> <macro name="snippet">
@ -410,7 +441,8 @@
</macro> </macro>
<p> <p>
Example URL to invoke this method:<br/> Example URL to invoke this method:
<br />
<code>http://fhir.example.com/Patient</code> <code>http://fhir.example.com/Patient</code>
</p> </p>
@ -422,14 +454,16 @@
to your search method and tag these parameters as either to your search method and tag these parameters as either
<a href="./apidocs/ca/uhn/fhir/rest/server/parameters/RequiredParam.html">@RequiredParam</a> <a href="./apidocs/ca/uhn/fhir/rest/server/parameters/RequiredParam.html">@RequiredParam</a>
or or
<a href="./apidocs/ca/uhn/fhir/rest/server/parameters/OptionalParam.html">@OptionalParam</a>. <a href="./apidocs/ca/uhn/fhir/rest/server/parameters/OptionalParam.html">@OptionalParam</a>
.
</p> </p>
<p> <p>
This annotation takes a "name" parameter which specifies the parameter's This annotation takes a "name" parameter which specifies the parameter's
name (as it will appear in the search URL). FHIR defines standardized parameter name (as it will appear in the search URL). FHIR defines standardized parameter
names for each resource, and these are available as constants on the names for each resource, and these are available as constants on the
individual HAPI resource classes. individual HAPI resource
classes.
</p> </p>
<macro name="snippet"> <macro name="snippet">
@ -438,7 +472,8 @@
</macro> </macro>
<p> <p>
Example URL to invoke this method:<br/> Example URL to invoke this method:
<br />
<code>http://fhir.example.com/Patient?family=SMITH</code> <code>http://fhir.example.com/Patient?family=SMITH</code>
</p> </p>
@ -458,7 +493,8 @@
</macro> </macro>
<p> <p>
Example URL to invoke this method:<br/> Example URL to invoke this method:
<br />
<code>http://fhir.example.com/Patient?identifier=urn:foo|7000135</code> <code>http://fhir.example.com/Patient?identifier=urn:foo|7000135</code>
</p> </p>
@ -469,15 +505,20 @@
Search methods may take multiple parameters, and these parameters Search methods may take multiple parameters, and these parameters
may be optional. To add a second required parameter, annotate the may be optional. To add a second required parameter, annotate the
parameter with parameter with
<a href="./apidocs/ca/uhn/fhir/rest/server/parameters/RequiredParam.html">@RequiredParam</a>. <a href="./apidocs/ca/uhn/fhir/rest/server/parameters/RequiredParam.html">@RequiredParam</a>
To add an optional parameter (which will be passed in as <code>null</code> if no value .
To add an optional parameter (which will be passed in as
<code>null</code>
if no value
is supplied), annotate the method with is supplied), annotate the method with
<a href="./apidocs/ca/uhn/fhir/rest/server/parameters/OptionalParam.html">@OptionalParam</a>. <a href="./apidocs/ca/uhn/fhir/rest/server/parameters/OptionalParam.html">@OptionalParam</a>
.
</p> </p>
<p> <p>
You may annotate a method with any combination of as many @RequiredParam and as many @OptionalParam You may annotate a method with any combination of as many @RequiredParam and as many @OptionalParam
parameters as you want. It is valid to have only @RequiredParam parameters, or parameters as you want. It is valid to have only @RequiredParam
parameters, or
only @OptionalParam parameters, or any combination of the two. only @OptionalParam parameters, or any combination of the two.
</p> </p>
@ -487,8 +528,10 @@
</macro> </macro>
<p> <p>
Example URLs to invoke this method:<br/> Example URLs to invoke this method:
<code>http://fhir.example.com/Patient?family=SMITH</code><br/> <br />
<code>http://fhir.example.com/Patient?family=SMITH</code>
<br />
<code>http://fhir.example.com/Patient?family=SMITH&amp;given=JOHN</code> <code>http://fhir.example.com/Patient?family=SMITH&amp;given=JOHN</code>
</p> </p>
@ -499,8 +542,10 @@
<p> <p>
It is possible to accept multiple values of a single parameter It is possible to accept multiple values of a single parameter
as well. This is useful in cases when you want to return a list as well. This is useful in cases when you want to return a list
of resources with criteria matching a list of possible values. of resources with criteria matching a list of
See the <a href="http://www.hl7.org/implement/standards/fhir/search.html#combining">FHIR Specification</a> possible values.
See the
<a href="http://www.hl7.org/implement/standards/fhir/search.html#combining">FHIR Specification</a>
for more information. for more information.
</p> </p>
@ -510,13 +555,21 @@
<ul> <ul>
<li> <li>
Where a parameter may accept multiple comma separated values within a single value string Where a parameter may accept multiple comma separated values within a single value string
(e.g. <code>?language=FR,NL</code>) this is treated as an <b>OR</b> relationship, and (e.g.
<code>?language=FR,NL</code>
) this is treated as an
<b>OR</b>
relationship, and
the search should return elements matching either one or the other. the search should return elements matching either one or the other.
</li> </li>
<li> <li>
Where a parameter may accept multiple value strings for the same parameter name Where a parameter may accept multiple value strings for the same parameter name
(e.g. <code>?language=FR&amp;language=NL</code>) this is treated as an <b>AND</b> relationship, (e.g.
and the search should return only elements matching both. <code>?language=FR&amp;language=NL</code>
) this is treated as an
<b>AND</b>
relationship,
and the search should return only elements matching both.
</li> </li>
</ul> </ul>
@ -535,7 +588,8 @@
</macro> </macro>
<p> <p>
Example URL to invoke this method:<br/> Example URL to invoke this method:
<br />
<code>http://fhir.example.com/Observation?name=urn:fakenames|123,urn:fakenames|456</code> <code>http://fhir.example.com/Observation?name=urn:fakenames|123,urn:fakenames|456</code>
</p> </p>
@ -548,7 +602,9 @@
</p> </p>
<p> <p>
See the section below on <a href="#DATE_RANGES">date ranges</a> for See the section below on
<a href="#DATE_RANGES">date ranges</a>
for
one example of an AND parameter. one example of an AND parameter.
</p> </p>
@ -564,7 +620,9 @@
<p> <p>
Dates may be optionally prefixed with a qualifier. For example, the Dates may be optionally prefixed with a qualifier. For example, the
string <code>&gt;=2011-01-02</code> means any date on or after 2011-01-02. string
<code>&gt;=2011-01-02</code>
means any date on or after 2011-01-02.
</p> </p>
<p> <p>
@ -579,7 +637,8 @@
</macro> </macro>
<p> <p>
Example URL to invoke this method:<br/> Example URL to invoke this method:
<br />
<code>http://fhir.example.com/Observation?birthdate=&gt;=2011-01-02</code> <code>http://fhir.example.com/Observation?birthdate=&gt;=2011-01-02</code>
</p> </p>
@ -594,7 +653,7 @@
</subsection> </subsection>
<a name="DATE_RANGES"/> <a name="DATE_RANGES" />
<subsection name="Date Parameters - Ranges"> <subsection name="Date Parameters - Ranges">
<p> <p>
@ -604,15 +663,20 @@
<p> <p>
FHIR allows for multiple parameters with the same key, and interprets FHIR allows for multiple parameters with the same key, and interprets
these as being an "AND" set. So, for example, a range of<br/> these as being an "AND" set. So, for example, a range of
<code>DATE &gt;= 2011-01-01</code> and <code>DATE &lt; 2011-02-01</code><br/> <br />
<code>DATE &gt;= 2011-01-01</code>
and
<code>DATE &lt; 2011-02-01</code>
<br />
can be interpreted as any date within January 2011. can be interpreted as any date within January 2011.
</p> </p>
<p> <p>
The following snippet shows how to accept such a range, and combines it The following snippet shows how to accept such a range, and combines it
with a specific identifier, which is a common scenario. (i.e. Give me a list with a specific identifier, which is a common scenario. (i.e. Give me a list
of observations for a specific patient within a given date range) of observations for a
specific patient within a given date range)
</p> </p>
<macro name="snippet"> <macro name="snippet">
<param name="id" value="dateRange" /> <param name="id" value="dateRange" />
@ -620,7 +684,8 @@
</macro> </macro>
<p> <p>
Example URL to invoke this method:<br/> Example URL to invoke this method:
<br />
<code>http://fhir.example.com/Observation?subject.identifier=7000135&amp;date=&gt;=2011-01-01&amp;date=&lt;2011-02-01</code> <code>http://fhir.example.com/Observation?subject.identifier=7000135&amp;date=&gt;=2011-01-01&amp;date=&lt;2011-02-01</code>
</p> </p>
@ -640,7 +705,8 @@
<p> <p>
FHIR allows clients to request that specific linked resources be included FHIR allows clients to request that specific linked resources be included
as contained resources, which means that they will be "embedded" in a special as contained resources, which means that they will be "embedded" in a special
container called "contained" within the parent resource. container called
"contained" within the parent resource.
</p> </p>
<p> <p>
@ -654,7 +720,8 @@
</macro> </macro>
<p> <p>
Example URL to invoke this method:<br/> Example URL to invoke this method:
<br />
<code>http://fhir.example.com/DiagnosticReport?subject.identifier=7000135&amp;_include=DiagnosticReport.subject</code> <code>http://fhir.example.com/DiagnosticReport?subject.identifier=7000135&amp;_include=DiagnosticReport.subject</code>
</p> </p>
@ -663,7 +730,9 @@
<subsection name="Named Queries (_query)"> <subsection name="Named Queries (_query)">
<p> <p>
FHIR supports <a href="http://www.hl7.org/implement/standards/fhir/search.html#advanced">named queries</a>, FHIR supports
<a href="http://www.hl7.org/implement/standards/fhir/search.html#advanced">named queries</a>
,
which may have specific behaviour defined. The following example shows how to create a Search which may have specific behaviour defined. The following example shows how to create a Search
operation with a name. operation with a name.
</p> </p>
@ -679,13 +748,14 @@
</macro> </macro>
<p> <p>
Example URL to invoke this method:<br/> Example URL to invoke this method:
<br />
<code>http://fhir.example.com/Patient?_query=namedQuery1&amp;someparam=value</code> <code>http://fhir.example.com/Patient?_query=namedQuery1&amp;someparam=value</code>
</p> </p>
</subsection> </subsection>
<a name="type_validate"/> <a name="type_validate" />
</section> </section>
<!-- ****************************************************************** --> <!-- ****************************************************************** -->
@ -695,10 +765,68 @@
<section name="Type Level - Validate"> <section name="Type Level - Validate">
<p> <p>
Not yet implemented The
<a href="http://hl7.org/implement/standards/fhir/http.html#validate">
<b>validate</b>
</a>
tests whether a resource passes business validation, and would be
acceptable for saving to a server (e.g. by a create or update method).
</p>
<p>
Validate methods must be annotated with the
<a href="./apidocs/ca/uhn/fhir/rest/annotation/Validate.html">@Validate</a>
annotation, and have a parameter annotated with the
<a href="./apidocs/ca/uhn/fhir/rest/annotation/ResourceParam.html">@Resource</a>
annotation. This parameter contains the resource instance to be created.
</p>
<p>
Validate methods may optionally also have a parameter
oftype IdDt annotated with the
<a href="./apidocs/ca/uhn/fhir/rest/annotation/IdParam.html">@IdParam</a>
annotation. This parameter contains the resource ID (see the
<a href="http://hl7.org/implement/standards/fhir/http.html#validation">FHIR specification</a>
for details on how this is used)
</p>
<p>
Validate methods must return normally if the resource validates successfully,
or throw an
<a href="./apidocs/ca/uhn/fhir/rest/server/exceptions/UnprocessableEntityException.html">UnprocessableEntityException</a>
or
<a href="./apidocs/ca/uhn/fhir/rest/server/exceptions/InvalidRequestException.html">InvalidRequestException</a>
if the validation fails.
</p>
<p>
Validate methods must return either:
</p>
<ul>
<li>
<b>void</b>
- The method should throw an exception for a validation failure, or return normally.
</li>
<li>
An object of type
<a href="./apidocs/ca/uhn/fhir/rest/api/MethodOutcome.html">MethodOutcome</a>. The
MethodOutcome may optionally be populated with an OperationOutcome resource, which
will be returned to the client if it exists.
</li>
</ul>
<p>
The following snippet shows how to define a server validate method:
</p> </p>
<a name="system_conformance"/> <macro name="snippet">
<param name="id" value="validate" />
<param name="file" value="src/site/example/java/example/RestfulPatientResourceProviderMore.java" />
</macro>
<p>
Example URL to invoke this method (this would be invoked using an HTTP POST,
with the resource in the POST body):
<br />
<code>http://fhir.example.com/Patient/_validate</code>
</p>
<a name="system_conformance" />
</section> </section>
<!-- ****************************************************************** --> <!-- ****************************************************************** -->
@ -745,7 +873,8 @@
<p> <p>
Then use the standard Then use the standard
<a href="doc_rest_client.html">RESTful Client</a> mechanism for instantiating <a href="doc_rest_client.html">RESTful Client</a>
mechanism for instantiating
a client: a client:
</p> </p>
<macro name="snippet"> <macro name="snippet">
@ -753,7 +882,7 @@
<param name="file" value="src/site/example/java/example/RestfulPatientResourceProviderMore.java" /> <param name="file" value="src/site/example/java/example/RestfulPatientResourceProviderMore.java" />
</macro> </macro>
<a name="system_transaction"/> <a name="system_transaction" />
</section> </section>
<!-- ****************************************************************** --> <!-- ****************************************************************** -->
@ -766,7 +895,7 @@
Not yet implemented Not yet implemented
</p> </p>
<a name="system_search"/> <a name="system_search" />
</section> </section>
<!-- ****************************************************************** --> <!-- ****************************************************************** -->
@ -779,7 +908,7 @@
Not yet implemented Not yet implemented
</p> </p>
<a name="history"/> <a name="history" />
</section> </section>
<!-- ****************************************************************** --> <!-- ****************************************************************** -->
@ -790,10 +919,17 @@
<p> <p>
The The
<a href="http://hl7.org/implement/standards/fhir/http.html#history"><b>history</b></a> <a href="http://hl7.org/implement/standards/fhir/http.html#history">
<b>history</b>
</a>
operation retrieves a historical collection of all versions of a single resource operation retrieves a historical collection of all versions of a single resource
<i>(instance history)</i>, all resources of a given type <i>(type history)</i>, <i>(instance history)</i>
or all resources of any type on a server <i>(server history)</i>. , all resources of a given type
<i>(type history)</i>
,
or all resources of any type on a server
<i>(server history)</i>
.
</p> </p>
<p> <p>
History methods must be annotated with the History methods must be annotated with the
@ -803,16 +939,22 @@
</p> </p>
<ul> <ul>
<li> <li>
For an <b>Instance History</b> method, the method must have a parameter For an
<b>Instance History</b>
method, the method must have a parameter
annotated with the annotated with the
<a href="./apidocs/ca/uhn/fhir/rest/annotation/IdParam.html">@IdParam</a> <a href="./apidocs/ca/uhn/fhir/rest/annotation/IdParam.html">@IdParam</a>
annotation, indicating the ID of the resource for which to return history. annotation, indicating the ID of the resource for which to return history.
</li> </li>
<li> <li>
For an <b>Type History</b> method, the method must not have any @IdParam parameter. For an
<b>Type History</b>
method, the method must not have any @IdParam parameter.
</li> </li>
<li> <li>
For an <b>Server History</b> method, the method must not have any @IdParam parameter For an
<b>Server History</b>
method, the method must not have any @IdParam parameter
and must not be found in a ResourceProvider definition. and must not be found in a ResourceProvider definition.
<!-- TODO: make ResourceProvider a link to a defintion of these on the RESTFul server page --> <!-- TODO: make ResourceProvider a link to a defintion of these on the RESTFul server page -->
</li> </li>

View File

@ -69,6 +69,7 @@ import ca.uhn.fhir.rest.annotation.RequiredParam;
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;
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.annotation.VersionIdParam; import ca.uhn.fhir.rest.annotation.VersionIdParam;
import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.param.CodingListParam; import ca.uhn.fhir.rest.param.CodingListParam;
@ -855,6 +856,59 @@ public class ResfulServerMethodTest {
} }
@Test
public void testValidate() throws Exception {
Patient patient = new Patient();
patient.addName().addFamily("FOO");
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/_validate");
httpPost.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
OperationOutcome oo = new FhirContext().newXmlParser().parseResource(OperationOutcome.class, responseContent);
assertEquals("it passed", oo.getIssueFirstRep().getDetails().getValue());
// Now should fail
patient = new Patient();
patient.addName().addFamily("BAR");
httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/_validate");
httpPost.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
status = ourClient.execute(httpPost);
responseContent = IOUtils.toString(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(422, status.getStatusLine().getStatusCode());
oo = new FhirContext().newXmlParser().parseResource(OperationOutcome.class, responseContent);
assertEquals("it failed", oo.getIssueFirstRep().getDetails().getValue());
// Should fail with outcome
patient = new Patient();
patient.addName().addFamily("BAZ");
httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/_validate");
httpPost.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
status = ourClient.execute(httpPost);
responseContent = IOUtils.toString(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals("", responseContent);
}
@Test @Test
public void testUpdateWithVersion() throws Exception { public void testUpdateWithVersion() throws Exception {
@ -973,6 +1027,22 @@ public class ResfulServerMethodTest {
return new MethodOutcome(true, id, version); return new MethodOutcome(true, id, version);
} }
@Validate()
public MethodOutcome validatePatient(@ResourceParam Patient thePatient) {
if (thePatient.getNameFirstRep().getFamilyFirstRep().getValueNotNull().equals("FOO")) {
MethodOutcome methodOutcome = new MethodOutcome();
OperationOutcome oo = new OperationOutcome();
oo.addIssue().setDetails("it passed");
methodOutcome.setOperationOutcome(oo);
return methodOutcome;
}
if (thePatient.getNameFirstRep().getFamilyFirstRep().getValueNotNull().equals("BAR")) {
throw new UnprocessableEntityException("it failed");
}
return new MethodOutcome();
}
private Patient createPatient1() { private Patient createPatient1() {
Patient patient = new Patient(); Patient patient = new Patient();
patient.addIdentifier(); patient.addIdentifier();

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>0.3</version> <version>0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
@ -19,7 +19,7 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId> <artifactId>hapi-fhir-base</artifactId>
<version>0.3</version> <version>0.3-SNAPSHOT</version>
</dependency> </dependency>
</dependencies> </dependencies>
@ -28,7 +28,7 @@
<plugin> <plugin>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-tinder-plugin</artifactId> <artifactId>hapi-tinder-plugin</artifactId>
<version>0.3</version> <version>0.3-SNAPSHOT</version>
<executions> <executions>
<execution> <execution>
<goals> <goals>

View File

@ -0,0 +1,146 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>0.3</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu</artifactId>
<packaging>jar</packaging>
<name>HAPI FHIR Structures - DSTU (FHIR 0.80)</name>
<dependencies>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
<version>0.3</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-tinder-plugin</artifactId>
<version>0.3</version>
<executions>
<execution>
<goals>
<goal>generate-structures</goal>
</goals>
</execution>
</executions>
<configuration>
<package>ca.uhn.fhir.model.dstu</package>
<baseResourceNames>
<!-- <baseResourceName>account</baseResourceName> -->
<!-- <baseResourceName>activitydefinition-extensions</baseResourceName> -->
<baseResourceName>adversereaction</baseResourceName>
<baseResourceName>alert</baseResourceName>
<baseResourceName>allergyintolerance</baseResourceName>
<baseResourceName>appointmentresponse</baseResourceName>
<baseResourceName>appointment</baseResourceName>
<baseResourceName>availability</baseResourceName>
<baseResourceName>careplan</baseResourceName>
<baseResourceName>claim</baseResourceName>
<baseResourceName>composition</baseResourceName>
<baseResourceName>conceptmap</baseResourceName>
<baseResourceName>condition</baseResourceName>
<baseResourceName>conformance</baseResourceName>
<baseResourceName>coverage</baseResourceName>
<baseResourceName>deviceobservationreport</baseResourceName>
<baseResourceName>device</baseResourceName>
<baseResourceName>diagnosticorder</baseResourceName>
<baseResourceName>diagnosticreport</baseResourceName>
<baseResourceName>documentmanifest</baseResourceName>
<baseResourceName>documentreference</baseResourceName>
<baseResourceName>encounter</baseResourceName>
<!-- <baseResourceName>familyhistory-genetics-profile</baseResourceName> -->
<baseResourceName>familyhistory</baseResourceName>
<baseResourceName>geneexpression</baseResourceName>
<baseResourceName>geneticanalysis</baseResourceName>
<baseResourceName>group</baseResourceName>
<baseResourceName>gvfmeta</baseResourceName>
<baseResourceName>gvfvariant</baseResourceName>
<baseResourceName>imagingstudy</baseResourceName>
<baseResourceName>immunizationrecommendation</baseResourceName>
<baseResourceName>immunization</baseResourceName>
<baseResourceName>list</baseResourceName>
<baseResourceName>location</baseResourceName>
<baseResourceName>media</baseResourceName>
<baseResourceName>medicationadministration</baseResourceName>
<baseResourceName>medicationdispense</baseResourceName>
<baseResourceName>medicationprescription</baseResourceName>
<baseResourceName>medication</baseResourceName>
<baseResourceName>medicationstatement</baseResourceName>
<baseResourceName>messageheader</baseResourceName>
<baseResourceName>microarray</baseResourceName>
<!-- <baseResourceName>namespace</baseResourceName> -->
<baseResourceName>observation</baseResourceName>
<baseResourceName>operationoutcome</baseResourceName>
<baseResourceName>orderresponse</baseResourceName>
<baseResourceName>order</baseResourceName>
<baseResourceName>organization</baseResourceName>
<baseResourceName>other</baseResourceName>
<baseResourceName>patient</baseResourceName>
<!--<baseResourceName>person</baseResourceName>-->
<baseResourceName>practitioner</baseResourceName>
<baseResourceName>procedure</baseResourceName>
<baseResourceName>profile</baseResourceName>
<!-- <baseResourceName>protocol</baseResourceName> -->
<!-- <baseResourceName>provenance-extensions</baseResourceName> -->
<baseResourceName>provenance</baseResourceName>
<baseResourceName>query</baseResourceName>
<!-- <baseResourceName>questionnaire-extensions</baseResourceName> -->
<baseResourceName>questionnaire</baseResourceName>
<baseResourceName>relatedperson</baseResourceName>
<baseResourceName>remittance</baseResourceName>
<!-- <baseResourceName>resource</baseResourceName> -->
<baseResourceName>securityevent</baseResourceName>
<!--<baseResourceName>sequence</baseResourceName>-->
<baseResourceName>sequencinganalysis</baseResourceName>
<baseResourceName>sequencinglab</baseResourceName>
<baseResourceName>slot</baseResourceName>
<baseResourceName>specimen</baseResourceName>
<baseResourceName>substance</baseResourceName>
<baseResourceName>supply</baseResourceName>
<!--<baseResourceName>template</baseResourceName>-->
<baseResourceName>test</baseResourceName>
<baseResourceName>user</baseResourceName>
<!-- <baseResourceName>valueset-extensions</baseResourceName> -->
<baseResourceName>valueset</baseResourceName>
<!--<baseResourceName>vcfmeta</baseResourceName>-->
<!--<baseResourceName>vcfvariant</baseResourceName>-->
</baseResourceNames>
<buildDatatypes>true</buildDatatypes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>${maven_javadoc_plugin_version}</version>
<configuration>
</configuration>
</plugin>
</plugins>
</reporting>
</project>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>0.3</version> <version>0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
@ -20,7 +20,7 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId> <artifactId>hapi-fhir-base</artifactId>
<version>0.3</version> <version>0.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>0.3</version> <version>0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>

View File

@ -12,7 +12,7 @@
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<packaging>pom</packaging> <packaging>pom</packaging>
<version>0.3</version> <version>0.3-SNAPSHOT</version>
<name>HAPI</name> <name>HAPI</name>
<url>http://hl7api.sourceforge.net/hapi-fhir/</url> <url>http://hl7api.sourceforge.net/hapi-fhir/</url>