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

File diff suppressed because it is too large Load Diff

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;
@ -854,7 +855,60 @@ public class ResfulServerMethodTest {
assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("Location").getValue()); assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("Location").getValue());
} }
@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>