Clean up exception handling in server, and add QuestionnaireAnswers validation for JPA server

This commit is contained in:
jamesagnew 2015-07-16 20:16:51 -04:00
parent 3bba0c0425
commit 2fc0d4c7a2
41 changed files with 537 additions and 191 deletions

View File

@ -244,10 +244,10 @@ class ModelScanner {
* annotations if the HL7.org ones are found instead. * annotations if the HL7.org ones are found instead.
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private <T extends Annotation> T pullAnnotation(AnnotatedElement theTarget, Class<T> theAnnotationType) { private <T extends Annotation> T pullAnnotation(Class<?> theContainer, AnnotatedElement theTarget, Class<T> theAnnotationType) {
T retVal = theTarget.getAnnotation(theAnnotationType); T retVal = theTarget.getAnnotation(theAnnotationType);
if (myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU2_HL7ORG) == false) { if (myContext.getVersion().getVersion() != FhirVersionEnum.DSTU2_HL7ORG) {
return retVal; return retVal;
} }
@ -307,7 +307,7 @@ class ModelScanner {
return; return;
} }
ResourceDef resourceDefinition = pullAnnotation(theClass, ResourceDef.class); ResourceDef resourceDefinition = pullAnnotation(theClass, theClass, ResourceDef.class);
if (resourceDefinition != null) { if (resourceDefinition != null) {
if (!IBaseResource.class.isAssignableFrom(theClass)) { if (!IBaseResource.class.isAssignableFrom(theClass)) {
throw new ConfigurationException("Resource type contains a @" + ResourceDef.class.getSimpleName() + " annotation but does not implement " + IResource.class.getCanonicalName() + ": " + theClass.getCanonicalName()); throw new ConfigurationException("Resource type contains a @" + ResourceDef.class.getSimpleName() + " annotation but does not implement " + IResource.class.getCanonicalName() + ": " + theClass.getCanonicalName());
@ -317,7 +317,7 @@ class ModelScanner {
scanResource(resClass, resourceDefinition); scanResource(resClass, resourceDefinition);
} }
DatatypeDef datatypeDefinition = pullAnnotation(theClass, DatatypeDef.class); DatatypeDef datatypeDefinition = pullAnnotation(theClass, theClass, DatatypeDef.class);
if (datatypeDefinition != null) { if (datatypeDefinition != null) {
if (ICompositeType.class.isAssignableFrom(theClass)) { if (ICompositeType.class.isAssignableFrom(theClass)) {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -332,7 +332,7 @@ class ModelScanner {
} }
} }
Block blockDefinition = pullAnnotation(theClass, Block.class); Block blockDefinition = pullAnnotation(theClass, theClass, Block.class);
if (blockDefinition != null) { if (blockDefinition != null) {
if (IResourceBlock.class.isAssignableFrom(theClass) || IBaseBackboneElement.class.isAssignableFrom(theClass) || IBaseDatatypeElement.class.isAssignableFrom(theClass)) { if (IResourceBlock.class.isAssignableFrom(theClass) || IBaseBackboneElement.class.isAssignableFrom(theClass) || IBaseDatatypeElement.class.isAssignableFrom(theClass)) {
@ -440,16 +440,16 @@ class ModelScanner {
continue; continue;
} }
Child childAnnotation = pullAnnotation(next, Child.class); Child childAnnotation = pullAnnotation(theClass, next, Child.class);
if (childAnnotation == null) { if (childAnnotation == null) {
ourLog.trace("Ignoring non @Child field {} on target type {}", next.getName(), theClass); ourLog.trace("Ignoring non @Child field {} on target type {}", next.getName(), theClass);
continue; continue;
} }
Description descriptionAnnotation = pullAnnotation(next, Description.class); Description descriptionAnnotation = pullAnnotation(theClass, next, Description.class);
TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> orderMap = theOrderToElementDef; TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> orderMap = theOrderToElementDef;
Extension extensionAttr = pullAnnotation(next, Extension.class); Extension extensionAttr = pullAnnotation(theClass, next, Extension.class);
if (extensionAttr != null) { if (extensionAttr != null) {
orderMap = theOrderToExtensionDef; orderMap = theOrderToExtensionDef;
} }
@ -649,7 +649,7 @@ class ModelScanner {
} }
} }
CodeableConceptElement concept = pullAnnotation(next, CodeableConceptElement.class); CodeableConceptElement concept = pullAnnotation(theClass, next, CodeableConceptElement.class);
if (concept != null) { if (concept != null) {
if (!ICodedDatatype.class.isAssignableFrom(nextDatatype)) { if (!ICodedDatatype.class.isAssignableFrom(nextDatatype)) {
throw new ConfigurationException("Field '" + elementName + "' in type '" + theClass.getCanonicalName() + "' is marked as @" + CodeableConceptElement.class.getCanonicalName() + " but type is not a subtype of " + ICodedDatatype.class.getName()); throw new ConfigurationException("Field '" + elementName + "' in type '" + theClass.getCanonicalName() + "' is marked as @" + CodeableConceptElement.class.getCanonicalName() + " but type is not a subtype of " + ICodedDatatype.class.getName());
@ -709,7 +709,7 @@ class ModelScanner {
Class<?> parent = theClass.getSuperclass(); Class<?> parent = theClass.getSuperclass();
primaryNameProvider = false; primaryNameProvider = false;
while (parent.equals(Object.class) == false && isBlank(resourceName)) { while (parent.equals(Object.class) == false && isBlank(resourceName)) {
ResourceDef nextDef = pullAnnotation(parent, ResourceDef.class); ResourceDef nextDef = pullAnnotation(theClass, parent, ResourceDef.class);
if (nextDef != null) { if (nextDef != null) {
resourceName = nextDef.name(); resourceName = nextDef.name();
} }
@ -749,7 +749,7 @@ class ModelScanner {
Map<Field, SearchParamDefinition> compositeFields = new LinkedHashMap<Field, SearchParamDefinition>(); Map<Field, SearchParamDefinition> compositeFields = new LinkedHashMap<Field, SearchParamDefinition>();
for (Field nextField : theClass.getFields()) { for (Field nextField : theClass.getFields()) {
SearchParamDefinition searchParam = pullAnnotation(nextField, SearchParamDefinition.class); SearchParamDefinition searchParam = pullAnnotation(theClass, nextField, SearchParamDefinition.class);
if (searchParam != null) { if (searchParam != null) {
RestSearchParameterTypeEnum paramType = RestSearchParameterTypeEnum.valueOf(searchParam.type().toUpperCase()); RestSearchParameterTypeEnum paramType = RestSearchParameterTypeEnum.valueOf(searchParam.type().toUpperCase());
if (paramType == null) { if (paramType == null) {

View File

@ -7,7 +7,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
/* /*
* #%L * #%L
@ -54,7 +54,7 @@ public abstract class BaseServerResponseException extends RuntimeException {
} }
private List<String> myAdditionalMessages = null; private List<String> myAdditionalMessages = null;
private BaseOperationOutcome myBaseOperationOutcome; private IBaseOperationOutcome myBaseOperationOutcome;
private String myResponseBody; private String myResponseBody;
private String myResponseMimeType; private String myResponseMimeType;
private int myStatusCode; private int myStatusCode;
@ -100,7 +100,7 @@ public abstract class BaseServerResponseException extends RuntimeException {
* @param theBaseOperationOutcome * @param theBaseOperationOutcome
* An BaseOperationOutcome resource to return to the calling client (in a server) or the BaseOperationOutcome that was returned from the server (in a client) * An BaseOperationOutcome resource to return to the calling client (in a server) or the BaseOperationOutcome that was returned from the server (in a client)
*/ */
public BaseServerResponseException(int theStatusCode, String theMessage, BaseOperationOutcome theBaseOperationOutcome) { public BaseServerResponseException(int theStatusCode, String theMessage, IBaseOperationOutcome theBaseOperationOutcome) {
super(theMessage); super(theMessage);
myStatusCode = theStatusCode; myStatusCode = theStatusCode;
myBaseOperationOutcome = theBaseOperationOutcome; myBaseOperationOutcome = theBaseOperationOutcome;
@ -134,7 +134,7 @@ public abstract class BaseServerResponseException extends RuntimeException {
* @param theBaseOperationOutcome * @param theBaseOperationOutcome
* An BaseOperationOutcome resource to return to the calling client (in a server) or the BaseOperationOutcome that was returned from the server (in a client) * An BaseOperationOutcome resource to return to the calling client (in a server) or the BaseOperationOutcome that was returned from the server (in a client)
*/ */
public BaseServerResponseException(int theStatusCode, String theMessage, Throwable theCause, BaseOperationOutcome theBaseOperationOutcome) { public BaseServerResponseException(int theStatusCode, String theMessage, Throwable theCause, IBaseOperationOutcome theBaseOperationOutcome) {
super(theMessage, theCause); super(theMessage, theCause);
myStatusCode = theStatusCode; myStatusCode = theStatusCode;
myBaseOperationOutcome = theBaseOperationOutcome; myBaseOperationOutcome = theBaseOperationOutcome;
@ -164,7 +164,7 @@ public abstract class BaseServerResponseException extends RuntimeException {
* @param theBaseOperationOutcome * @param theBaseOperationOutcome
* An BaseOperationOutcome resource to return to the calling client (in a server) or the BaseOperationOutcome that was returned from the server (in a client) * An BaseOperationOutcome resource to return to the calling client (in a server) or the BaseOperationOutcome that was returned from the server (in a client)
*/ */
public BaseServerResponseException(int theStatusCode, Throwable theCause, BaseOperationOutcome theBaseOperationOutcome) { public BaseServerResponseException(int theStatusCode, Throwable theCause, IBaseOperationOutcome theBaseOperationOutcome) {
super(theCause.toString(), theCause); super(theCause.toString(), theCause);
myStatusCode = theStatusCode; myStatusCode = theStatusCode;
myBaseOperationOutcome = theBaseOperationOutcome; myBaseOperationOutcome = theBaseOperationOutcome;
@ -182,9 +182,9 @@ public abstract class BaseServerResponseException extends RuntimeException {
} }
/** /**
* Returns the {@link BaseOperationOutcome} resource if any which was supplied in the response, or <code>null</code> * Returns the {@link IBaseOperationOutcome} resource if any which was supplied in the response, or <code>null</code>
*/ */
public BaseOperationOutcome getOperationOutcome() { public IBaseOperationOutcome getOperationOutcome() {
return myBaseOperationOutcome; return myBaseOperationOutcome;
} }
@ -223,7 +223,7 @@ public abstract class BaseServerResponseException extends RuntimeException {
* The BaseOperationOutcome resource Sets the BaseOperationOutcome resource associated with this exception. In server implementations, this is the OperartionOutcome resource to include * The BaseOperationOutcome resource Sets the BaseOperationOutcome resource associated with this exception. In server implementations, this is the OperartionOutcome resource to include
* with the HTTP response. In client implementations you should not call this method. * with the HTTP response. In client implementations you should not call this method.
*/ */
public void setOperationOutcome(BaseOperationOutcome theBaseOperationOutcome) { public void setOperationOutcome(IBaseOperationOutcome theBaseOperationOutcome) {
myBaseOperationOutcome = theBaseOperationOutcome; myBaseOperationOutcome = theBaseOperationOutcome;
} }

View File

@ -1,7 +1,9 @@
package ca.uhn.fhir.rest.server.exceptions; package ca.uhn.fhir.rest.server.exceptions;
import net.sourceforge.cobertura.CoverageIgnore; import net.sourceforge.cobertura.CoverageIgnore;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.Constants;
/* /*
@ -54,7 +56,7 @@ public class ForbiddenOperationException extends BaseServerResponseException {
* @param theOperationOutcome * @param theOperationOutcome
* The OperationOutcome resource to return to the client * The OperationOutcome resource to return to the client
*/ */
public ForbiddenOperationException(String theMessage, BaseOperationOutcome theOperationOutcome) { public ForbiddenOperationException(String theMessage, IBaseOperationOutcome theOperationOutcome) {
super(STATUS_CODE, theMessage, theOperationOutcome); super(STATUS_CODE, theMessage, theOperationOutcome);
} }

View File

@ -1,7 +1,9 @@
package ca.uhn.fhir.rest.server.exceptions; package ca.uhn.fhir.rest.server.exceptions;
import net.sourceforge.cobertura.CoverageIgnore; import net.sourceforge.cobertura.CoverageIgnore;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.Constants;
/* /*
@ -54,7 +56,7 @@ public class InternalErrorException extends BaseServerResponseException {
* The message * The message
* @param theOperationOutcome The OperationOutcome resource to return to the client * @param theOperationOutcome The OperationOutcome resource to return to the client
*/ */
public InternalErrorException(String theMessage, BaseOperationOutcome theOperationOutcome) { public InternalErrorException(String theMessage, IBaseOperationOutcome theOperationOutcome) {
super(STATUS_CODE, theMessage, theOperationOutcome); super(STATUS_CODE, theMessage, theOperationOutcome);
} }

View File

@ -1,7 +1,9 @@
package ca.uhn.fhir.rest.server.exceptions; package ca.uhn.fhir.rest.server.exceptions;
import net.sourceforge.cobertura.CoverageIgnore; import net.sourceforge.cobertura.CoverageIgnore;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.Constants;
/* /*
@ -54,7 +56,7 @@ public class InvalidRequestException extends BaseServerResponseException {
* The message * The message
* @param theOperationOutcome The OperationOutcome resource to return to the client * @param theOperationOutcome The OperationOutcome resource to return to the client
*/ */
public InvalidRequestException(String theMessage, BaseOperationOutcome theOperationOutcome) { public InvalidRequestException(String theMessage, IBaseOperationOutcome theOperationOutcome) {
super(STATUS_CODE, theMessage, theOperationOutcome); super(STATUS_CODE, theMessage, theOperationOutcome);
} }

View File

@ -5,7 +5,8 @@ import java.util.LinkedHashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import ca.uhn.fhir.rest.api.RequestTypeEnum; import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.Constants;
@ -53,7 +54,7 @@ public class MethodNotAllowedException extends BaseServerResponseException {
* @param theAllowedMethods * @param theAllowedMethods
* A list of allowed methods (see {@link #setAllowedMethods(RequestTypeEnum...)} ) * A list of allowed methods (see {@link #setAllowedMethods(RequestTypeEnum...)} )
*/ */
public MethodNotAllowedException(String theMessage, BaseOperationOutcome theOperationOutcome, RequestTypeEnum... theAllowedMethods) { public MethodNotAllowedException(String theMessage, IBaseOperationOutcome theOperationOutcome, RequestTypeEnum... theAllowedMethods) {
super(STATUS_CODE, theMessage, theOperationOutcome); super(STATUS_CODE, theMessage, theOperationOutcome);
setAllowedMethods(theAllowedMethods); setAllowedMethods(theAllowedMethods);
} }
@ -79,7 +80,7 @@ public class MethodNotAllowedException extends BaseServerResponseException {
* @param theOperationOutcome * @param theOperationOutcome
* The OperationOutcome resource to return to the client * The OperationOutcome resource to return to the client
*/ */
public MethodNotAllowedException(String theMessage, BaseOperationOutcome theOperationOutcome) { public MethodNotAllowedException(String theMessage, IBaseOperationOutcome theOperationOutcome) {
super(STATUS_CODE, theMessage, theOperationOutcome); super(STATUS_CODE, theMessage, theOperationOutcome);
} }

View File

@ -1,7 +1,9 @@
package ca.uhn.fhir.rest.server.exceptions; package ca.uhn.fhir.rest.server.exceptions;
import net.sourceforge.cobertura.CoverageIgnore; import net.sourceforge.cobertura.CoverageIgnore;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.Constants;
/* /*
@ -51,7 +53,7 @@ public class NotImplementedOperationException extends BaseServerResponseExceptio
* @param theOperationOutcome * @param theOperationOutcome
* The OperationOutcome resource to return to the client * The OperationOutcome resource to return to the client
*/ */
public NotImplementedOperationException(String theMessage, BaseOperationOutcome theOperationOutcome) { public NotImplementedOperationException(String theMessage, IBaseOperationOutcome theOperationOutcome) {
super(STATUS_CODE, theMessage, theOperationOutcome); super(STATUS_CODE, theMessage, theOperationOutcome);
} }

View File

@ -1,7 +1,9 @@
package ca.uhn.fhir.rest.server.exceptions; package ca.uhn.fhir.rest.server.exceptions;
import net.sourceforge.cobertura.CoverageIgnore; import net.sourceforge.cobertura.CoverageIgnore;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.Constants;
/* /*
@ -52,7 +54,7 @@ public class NotModifiedException extends BaseServerResponseException {
* @param theOperationOutcome * @param theOperationOutcome
* The OperationOutcome resource to return to the client * The OperationOutcome resource to return to the client
*/ */
public NotModifiedException(String theMessage, BaseOperationOutcome theOperationOutcome) { public NotModifiedException(String theMessage, IBaseOperationOutcome theOperationOutcome) {
super(STATUS_CODE, theMessage, theOperationOutcome); super(STATUS_CODE, theMessage, theOperationOutcome);
} }

View File

@ -21,7 +21,9 @@ package ca.uhn.fhir.rest.server.exceptions;
*/ */
import net.sourceforge.cobertura.CoverageIgnore; import net.sourceforge.cobertura.CoverageIgnore;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import ca.uhn.fhir.rest.annotation.Update; import ca.uhn.fhir.rest.annotation.Update;
import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.Constants;
@ -48,7 +50,7 @@ public class PreconditionFailedException extends ResourceVersionNotSpecifiedExce
* The message * The message
* @param theOperationOutcome The OperationOutcome resource to return to the client * @param theOperationOutcome The OperationOutcome resource to return to the client
*/ */
public PreconditionFailedException(String theMessage, BaseOperationOutcome theOperationOutcome) { public PreconditionFailedException(String theMessage, IBaseOperationOutcome theOperationOutcome) {
super(STATUS_CODE, theMessage, theOperationOutcome); super(STATUS_CODE, theMessage, theOperationOutcome);
} }

View File

@ -21,9 +21,11 @@ package ca.uhn.fhir.rest.server.exceptions;
*/ */
import net.sourceforge.cobertura.CoverageIgnore; import net.sourceforge.cobertura.CoverageIgnore;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.base.composite.BaseIdentifierDt; import ca.uhn.fhir.model.base.composite.BaseIdentifierDt;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.Constants;
@ -55,7 +57,7 @@ public class ResourceGoneException extends BaseServerResponseException {
* The message * The message
* @param theOperationOutcome The OperationOutcome resource to return to the client * @param theOperationOutcome The OperationOutcome resource to return to the client
*/ */
public ResourceGoneException(String theMessage, BaseOperationOutcome theOperationOutcome) { public ResourceGoneException(String theMessage, IBaseOperationOutcome theOperationOutcome) {
super(STATUS_CODE, theMessage, theOperationOutcome); super(STATUS_CODE, theMessage, theOperationOutcome);
} }

View File

@ -22,11 +22,11 @@ package ca.uhn.fhir.rest.server.exceptions;
import net.sourceforge.cobertura.CoverageIgnore; import net.sourceforge.cobertura.CoverageIgnore;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.base.composite.BaseIdentifierDt; import ca.uhn.fhir.model.base.composite.BaseIdentifierDt;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.Constants;
@ -44,7 +44,7 @@ public class ResourceNotFoundException extends BaseServerResponseException {
super(STATUS_CODE, createErrorMessage(theClass, theId)); super(STATUS_CODE, createErrorMessage(theClass, theId));
} }
public ResourceNotFoundException(Class<? extends IResource> theClass, IdDt theId, BaseOperationOutcome theOperationOutcome) { public ResourceNotFoundException(Class<? extends IResource> theClass, IdDt theId, IBaseOperationOutcome theOperationOutcome) {
super(STATUS_CODE, createErrorMessage(theClass, theId), theOperationOutcome); super(STATUS_CODE, createErrorMessage(theClass, theId), theOperationOutcome);
} }
@ -52,7 +52,7 @@ public class ResourceNotFoundException extends BaseServerResponseException {
super(STATUS_CODE, createErrorMessage(theClass, theId)); super(STATUS_CODE, createErrorMessage(theClass, theId));
} }
public ResourceNotFoundException(Class<? extends IResource> theClass, IIdType theId, BaseOperationOutcome theOperationOutcome) { public ResourceNotFoundException(Class<? extends IResource> theClass, IIdType theId, IBaseOperationOutcome theOperationOutcome) {
super(STATUS_CODE, createErrorMessage(theClass, theId), theOperationOutcome); super(STATUS_CODE, createErrorMessage(theClass, theId), theOperationOutcome);
} }
@ -63,7 +63,7 @@ public class ResourceNotFoundException extends BaseServerResponseException {
* The message * The message
* @param theOperationOutcome The OperationOutcome resource to return to the client * @param theOperationOutcome The OperationOutcome resource to return to the client
*/ */
public ResourceNotFoundException(String theMessage, BaseOperationOutcome theOperationOutcome) { public ResourceNotFoundException(String theMessage, IBaseOperationOutcome theOperationOutcome) {
super(STATUS_CODE, theMessage, theOperationOutcome); super(STATUS_CODE, theMessage, theOperationOutcome);
} }
@ -83,7 +83,7 @@ public class ResourceNotFoundException extends BaseServerResponseException {
super(STATUS_CODE, createErrorMessage(theId)); super(STATUS_CODE, createErrorMessage(theId));
} }
public ResourceNotFoundException(IdDt theId, BaseOperationOutcome theOperationOutcome) { public ResourceNotFoundException(IdDt theId, IBaseOperationOutcome theOperationOutcome) {
super(STATUS_CODE, createErrorMessage(theId), theOperationOutcome); super(STATUS_CODE, createErrorMessage(theId), theOperationOutcome);
} }

View File

@ -21,7 +21,9 @@ package ca.uhn.fhir.rest.server.exceptions;
*/ */
import net.sourceforge.cobertura.CoverageIgnore; import net.sourceforge.cobertura.CoverageIgnore;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import ca.uhn.fhir.rest.annotation.Delete; import ca.uhn.fhir.rest.annotation.Delete;
import ca.uhn.fhir.rest.annotation.Update; import ca.uhn.fhir.rest.annotation.Update;
import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.Constants;
@ -47,7 +49,7 @@ public class ResourceVersionConflictException extends BaseServerResponseExceptio
* The message * The message
* @param theOperationOutcome The OperationOutcome resource to return to the client * @param theOperationOutcome The OperationOutcome resource to return to the client
*/ */
public ResourceVersionConflictException(String theMessage, BaseOperationOutcome theOperationOutcome) { public ResourceVersionConflictException(String theMessage, IBaseOperationOutcome theOperationOutcome) {
super(STATUS_CODE, theMessage, theOperationOutcome); super(STATUS_CODE, theMessage, theOperationOutcome);
} }

View File

@ -21,7 +21,9 @@ package ca.uhn.fhir.rest.server.exceptions;
*/ */
import net.sourceforge.cobertura.CoverageIgnore; import net.sourceforge.cobertura.CoverageIgnore;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.Constants;
/** /**
@ -45,7 +47,7 @@ public class ResourceVersionNotSpecifiedException extends BaseServerResponseExce
* The message * The message
* @param theOperationOutcome The OperationOutcome resource to return to the client * @param theOperationOutcome The OperationOutcome resource to return to the client
*/ */
public ResourceVersionNotSpecifiedException(String theMessage, BaseOperationOutcome theOperationOutcome) { public ResourceVersionNotSpecifiedException(String theMessage, IBaseOperationOutcome theOperationOutcome) {
super(STATUS_CODE, theMessage, theOperationOutcome); super(STATUS_CODE, theMessage, theOperationOutcome);
} }
@ -60,7 +62,7 @@ public class ResourceVersionNotSpecifiedException extends BaseServerResponseExce
* The message * The message
* @param theOperationOutcome The OperationOutcome resource to return to the client * @param theOperationOutcome The OperationOutcome resource to return to the client
*/ */
public ResourceVersionNotSpecifiedException(int theStatusCode, String theMessage, BaseOperationOutcome theOperationOutcome) { public ResourceVersionNotSpecifiedException(int theStatusCode, String theMessage, IBaseOperationOutcome theOperationOutcome) {
super(theStatusCode, theMessage, theOperationOutcome); super(theStatusCode, theMessage, theOperationOutcome);
} }

View File

@ -1,7 +1,8 @@
package ca.uhn.fhir.rest.server.exceptions; package ca.uhn.fhir.rest.server.exceptions;
import net.sourceforge.cobertura.CoverageIgnore; import net.sourceforge.cobertura.CoverageIgnore;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
/* /*
* #%L * #%L
@ -51,7 +52,7 @@ public class UnclassifiedServerFailureException extends BaseServerResponseExcept
* The message to add to the status line * The message to add to the status line
* @param theOperationOutcome The OperationOutcome resource to return to the client * @param theOperationOutcome The OperationOutcome resource to return to the client
*/ */
public UnclassifiedServerFailureException(int theStatusCode, String theMessage, BaseOperationOutcome theOperationOutcome) { public UnclassifiedServerFailureException(int theStatusCode, String theMessage, IBaseOperationOutcome theOperationOutcome) {
super(theStatusCode, theMessage, theOperationOutcome); super(theStatusCode, theMessage, theOperationOutcome);
} }

View File

@ -21,14 +21,16 @@ package ca.uhn.fhir.rest.server.exceptions;
*/ */
import net.sourceforge.cobertura.CoverageIgnore; import net.sourceforge.cobertura.CoverageIgnore;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.Constants;
/** /**
* Represents an <b>HTTP 422 Unprocessable Entity</b> response, which means that a resource was rejected by the server because it "violated applicable FHIR profiles or server business rules". * Represents an <b>HTTP 422 Unprocessable Entity</b> response, which means that a resource was rejected by the server because it "violated applicable FHIR profiles or server business rules".
* *
* <p> * <p>
* This exception will generally contain an {@link BaseOperationOutcome} instance which details the failure. * This exception will generally contain an {@link IBaseOperationOutcome} instance which details the failure.
* </p> * </p>
* *
* @see InvalidRequestException Which corresponds to an <b>HTTP 400 Bad Request</b> failure * @see InvalidRequestException Which corresponds to an <b>HTTP 400 Bad Request</b> failure
@ -45,29 +47,29 @@ public class UnprocessableEntityException extends BaseServerResponseException {
* *
* @param theMessage * @param theMessage
* The message to add to the status line * The message to add to the status line
* @param theOperationOutcome The BaseOperationOutcome resource to return to the client * @param theOperationOutcome The {@link IBaseOperationOutcome} resource to return to the client
*/ */
public UnprocessableEntityException(String theMessage, BaseOperationOutcome theOperationOutcome) { public UnprocessableEntityException(String theMessage, IBaseOperationOutcome theOperationOutcome) {
super(STATUS_CODE, theMessage, theOperationOutcome); super(STATUS_CODE, theMessage, theOperationOutcome);
} }
/** /**
* Constructor which accepts an {@link BaseOperationOutcome} resource which will be supplied in the response * Constructor which accepts an {@link IBaseOperationOutcome} resource which will be supplied in the response
*/ */
public UnprocessableEntityException(BaseOperationOutcome theOperationOutcome) { public UnprocessableEntityException(IBaseOperationOutcome theOperationOutcome) {
super(STATUS_CODE, DEFAULT_MESSAGE, theOperationOutcome); super(STATUS_CODE, DEFAULT_MESSAGE, theOperationOutcome);
} }
/** /**
* Constructor which accepts a String describing the issue. This string will be translated into an {@link BaseOperationOutcome} resource which will be supplied in the response. * Constructor which accepts a String describing the issue. This string will be translated into an {@link IBaseOperationOutcome} resource which will be supplied in the response.
*/ */
public UnprocessableEntityException(String theMessage) { public UnprocessableEntityException(String theMessage) {
super(STATUS_CODE, theMessage); super(STATUS_CODE, theMessage);
} }
/** /**
* Constructor which accepts an array of Strings describing the issue. This strings will be translated into an {@link BaseOperationOutcome} resource which will be supplied in the response. * Constructor which accepts an array of Strings describing the issue. This strings will be translated into an {@link IBaseOperationOutcome} resource which will be supplied in the response.
*/ */
public UnprocessableEntityException(String... theMessage) { public UnprocessableEntityException(String... theMessage) {
super(STATUS_CODE, theMessage); super(STATUS_CODE, theMessage);

View File

@ -22,6 +22,7 @@ package ca.uhn.fhir.rest.server.interceptor;
import java.io.IOException; import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@ -33,11 +34,18 @@ import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.method.RequestDetails; import ca.uhn.fhir.rest.method.RequestDetails;
import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.RestfulServer.NarrativeModeEnum;
import ca.uhn.fhir.rest.server.RestfulServerUtils; import ca.uhn.fhir.rest.server.RestfulServerUtils;
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
/**
* This interceptor detects when a request is coming from a browser, and automatically
* returns a response with syntax highlighted (coloured) HTML for the response instead
* of just returning raw XML/JSON.
*
* @since 1.0
*/
public class ResponseHighlighterInterceptor extends InterceptorAdapter { public class ResponseHighlighterInterceptor extends InterceptorAdapter {
private String format(String theResultBody, EncodingEnum theEncodingEnum) { private String format(String theResultBody, EncodingEnum theEncodingEnum) {
@ -178,12 +186,15 @@ public class ResponseHighlighterInterceptor extends InterceptorAdapter {
return super.outgoingResponse(theRequestDetails, theResponseObject, theServletRequest, theServletResponse); return super.outgoingResponse(theRequestDetails, theResponseObject, theServletRequest, theServletResponse);
} }
streamResponse(theRequestDetails, theServletRequest, theServletResponse, theResponseObject);
return false;
}
private void streamResponse(RequestDetails theRequestDetails, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse, IBaseResource resource) {
// Pretty print // Pretty print
boolean prettyPrint = RestfulServerUtils.prettyPrintResponse(theRequestDetails.getServer(), theRequestDetails); boolean prettyPrint = RestfulServerUtils.prettyPrintResponse(theRequestDetails.getServer(), theRequestDetails);
// Narrative mode
NarrativeModeEnum narrativeMode = RestfulServerUtils.determineNarrativeMode(theRequestDetails);
// Determine response encoding // Determine response encoding
EncodingEnum responseEncoding = null; EncodingEnum responseEncoding = null;
if (theRequestDetails.getParameters().containsKey(Constants.PARAM_FORMAT)) { if (theRequestDetails.getParameters().containsKey(Constants.PARAM_FORMAT)) {
@ -198,7 +209,7 @@ public class ResponseHighlighterInterceptor extends InterceptorAdapter {
IParser p = responseEncoding.newParser(theRequestDetails.getServer().getFhirContext()); IParser p = responseEncoding.newParser(theRequestDetails.getServer().getFhirContext());
p.setPrettyPrint(prettyPrint); p.setPrettyPrint(prettyPrint);
String encoded = p.encodeResourceToString(theResponseObject); String encoded = p.encodeResourceToString(resource);
theServletResponse.setContentType(Constants.CT_HTML_WITH_UTF8); theServletResponse.setContentType(Constants.CT_HTML_WITH_UTF8);
@ -239,6 +250,43 @@ public class ResponseHighlighterInterceptor extends InterceptorAdapter {
} catch (IOException e) { } catch (IOException e) {
throw new InternalErrorException(e); throw new InternalErrorException(e);
} }
}
@Override
public boolean handleException(RequestDetails theRequestDetails, Throwable theException, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws ServletException, IOException {
/*
* It's not a browser...
*/
String accept = theServletRequest.getHeader(Constants.HEADER_ACCEPT);
if (accept == null || !accept.toLowerCase().contains("html")) {
return super.handleException(theRequestDetails, theException, theServletRequest, theServletResponse);
}
/*
* It's an AJAX request, so no HTML
*/
String requestedWith = theServletRequest.getHeader("X-Requested-With");
if (requestedWith != null) {
return super.handleException(theRequestDetails, theException, theServletRequest, theServletResponse);
}
/*
* Not a GET
*/
if (theRequestDetails.getRequestType() != RequestTypeEnum.GET) {
return super.handleException(theRequestDetails, theException, theServletRequest, theServletResponse);
}
if (!(theException instanceof BaseServerResponseException)) {
return super.handleException(theRequestDetails, theException, theServletRequest, theServletResponse);
}
BaseServerResponseException bsre = (BaseServerResponseException)theException;
if (bsre.getOperationOutcome() == null) {
}
streamResponse(theRequestDetails, theServletRequest, theServletResponse, bsre.getOperationOutcome());
return false; return false;
} }

View File

@ -54,7 +54,7 @@ public class FhirValidator {
private static volatile Boolean ourPhlocPresentOnClasspath; private static volatile Boolean ourPhlocPresentOnClasspath;
private final FhirContext myContext; private final FhirContext myContext;
private volatile List<IValidator> myValidators = new ArrayList<IValidator>(); private volatile List<IValidatorModule> myValidators = new ArrayList<IValidatorModule>();
/** /**
* Constructor (this should not be called directly, but rather {@link FhirContext#newValidator()} should be called to obtain an instance of {@link FhirValidator}) * Constructor (this should not be called directly, but rather {@link FhirContext#newValidator()} should be called to obtain an instance of {@link FhirValidator})
@ -78,25 +78,25 @@ public class FhirValidator {
} }
private void addOrRemoveValidator(boolean theValidateAgainstStandardSchema, Class<? extends IValidator> type, IValidator theInstance) { private void addOrRemoveValidator(boolean theValidateAgainstStandardSchema, Class<? extends IValidatorModule> type, IValidatorModule theInstance) {
if (theValidateAgainstStandardSchema) { if (theValidateAgainstStandardSchema) {
boolean found = haveValidatorOfType(type); boolean found = haveValidatorOfType(type);
if (!found) { if (!found) {
registerValidator(theInstance); registerValidatorModule(theInstance);
} }
} else { } else {
for (Iterator<IValidator> iter = myValidators.iterator(); iter.hasNext();) { for (Iterator<IValidatorModule> iter = myValidators.iterator(); iter.hasNext();) {
IValidator next = iter.next(); IValidatorModule next = iter.next();
if (next.getClass().equals(type)) { if (next.getClass().equals(type)) {
unregisterValidator(next); unregisterValidatorModule(next);
} }
} }
} }
} }
private boolean haveValidatorOfType(Class<? extends IValidator> type) { private boolean haveValidatorOfType(Class<? extends IValidatorModule> type) {
boolean found = false; boolean found = false;
for (IValidator next : myValidators) { for (IValidatorModule next : myValidators) {
if (next.getClass().equals(type)) { if (next.getClass().equals(type)) {
found = true; found = true;
} }
@ -124,9 +124,9 @@ public class FhirValidator {
* @param theValidator * @param theValidator
* The validator module. Must not be null. * The validator module. Must not be null.
*/ */
public synchronized void registerValidator(IValidator theValidator) { public synchronized void registerValidatorModule(IValidatorModule theValidator) {
Validate.notNull(theValidator, "theValidator must not be null"); Validate.notNull(theValidator, "theValidator must not be null");
ArrayList<IValidator> newValidators = new ArrayList<IValidator>(myValidators.size() + 1); ArrayList<IValidatorModule> newValidators = new ArrayList<IValidatorModule>(myValidators.size() + 1);
newValidators.addAll(myValidators); newValidators.addAll(myValidators);
newValidators.add(theValidator); newValidators.add(theValidator);
@ -162,9 +162,9 @@ public class FhirValidator {
* @param theValidator * @param theValidator
* The validator module. Must not be null. * The validator module. Must not be null.
*/ */
public synchronized void unregisterValidator(IValidator theValidator) { public synchronized void unregisterValidatorModule(IValidatorModule theValidator) {
Validate.notNull(theValidator, "theValidator must not be null"); Validate.notNull(theValidator, "theValidator must not be null");
ArrayList<IValidator> newValidators = new ArrayList<IValidator>(myValidators.size() + 1); ArrayList<IValidatorModule> newValidators = new ArrayList<IValidatorModule>(myValidators.size() + 1);
newValidators.addAll(myValidators); newValidators.addAll(myValidators);
newValidators.remove(theValidator); newValidators.remove(theValidator);
@ -186,7 +186,7 @@ public class FhirValidator {
IValidationContext<Bundle> ctx = ValidationContext.forBundle(myContext, theBundle); IValidationContext<Bundle> ctx = ValidationContext.forBundle(myContext, theBundle);
for (IValidator next : myValidators) { for (IValidatorModule next : myValidators) {
next.validateBundle(ctx); next.validateBundle(ctx);
} }
@ -227,7 +227,7 @@ public class FhirValidator {
IValidationContext<Bundle> ctx = ValidationContext.forBundle(myContext, theBundle); IValidationContext<Bundle> ctx = ValidationContext.forBundle(myContext, theBundle);
for (IValidator next : myValidators) { for (IValidatorModule next : myValidators) {
next.validateBundle(ctx); next.validateBundle(ctx);
} }
@ -247,7 +247,7 @@ public class FhirValidator {
IValidationContext<IBaseResource> ctx = ValidationContext.forResource(myContext, theResource); IValidationContext<IBaseResource> ctx = ValidationContext.forResource(myContext, theResource);
for (IValidator next : myValidators) { for (IValidatorModule next : myValidators) {
next.validateResource(ctx); next.validateResource(ctx);
} }
@ -267,7 +267,7 @@ public class FhirValidator {
IValidationContext<IBaseResource> ctx = ValidationContext.forText(myContext, theResource); IValidationContext<IBaseResource> ctx = ValidationContext.forText(myContext, theResource);
for (IValidator next : myValidators) { for (IValidatorModule next : myValidators) {
next.validateResource(ctx); next.validateResource(ctx);
} }

View File

@ -28,7 +28,7 @@ import ca.uhn.fhir.model.api.Bundle;
/** /**
* Registers * Registers
*/ */
public interface IValidator { public interface IValidatorModule {
void validateResource(IValidationContext<IBaseResource> theCtx); void validateResource(IValidationContext<IBaseResource> theCtx);

View File

@ -52,7 +52,7 @@ import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
class SchemaBaseValidator implements IValidator { class SchemaBaseValidator implements IValidatorModule {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SchemaBaseValidator.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SchemaBaseValidator.class);
private static final Set<String> SCHEMA_NAMES; private static final Set<String> SCHEMA_NAMES;

View File

@ -43,7 +43,7 @@ import com.phloc.schematron.ISchematronResource;
import com.phloc.schematron.SchematronHelper; import com.phloc.schematron.SchematronHelper;
import com.phloc.schematron.xslt.SchematronResourceSCH; import com.phloc.schematron.xslt.SchematronResourceSCH;
public class SchematronBaseValidator implements IValidator { public class SchematronBaseValidator implements IValidatorModule {
private Map<Class<? extends IBaseResource>, ISchematronResource> myClassToSchematron = new HashMap<Class<? extends IBaseResource>, ISchematronResource>(); private Map<Class<? extends IBaseResource>, ISchematronResource> myClassToSchematron = new HashMap<Class<? extends IBaseResource>, ISchematronResource>();
private FhirContext myCtx; private FhirContext myCtx;

View File

@ -80,7 +80,9 @@ public class ValidationResult {
} }
/** /**
* @deprecated Use {@link #toOperationOutcome()} instead since this method returns a view * @deprecated Use {@link #toOperationOutcome()} instead since this method returns a view.
* {@link #toOperationOutcome()} is identical to this method, but has a more suitable name so this method
* will be removed at some point.
*/ */
@Deprecated @Deprecated
public IBaseOperationOutcome getOperationOutcome() { public IBaseOperationOutcome getOperationOutcome() {

View File

@ -276,7 +276,7 @@ public abstract class BaseHapiFhirDao implements IDao {
values.addAll(t.getValues(theResource, nextPathTrimmed)); values.addAll(t.getValues(theResource, nextPathTrimmed));
} catch (Exception e) { } catch (Exception e) {
RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource); RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource);
ourLog.warn("Failed to index values from path[{}] in resource type[{}]: ", new Object[] { nextPathTrimmed, def.getName(), e.toString() } ); ourLog.warn("Failed to index values from path[{}] in resource type[{}]: ", new Object[] { nextPathTrimmed, def.getName(), e.toString() });
} }
} }
return values; return values;
@ -325,7 +325,8 @@ public abstract class BaseHapiFhirDao implements IDao {
} }
} }
protected IFhirResourceDao<? extends IResource> getDao(Class<? extends IBaseResource> theType) { @SuppressWarnings("unchecked")
protected <T extends IBaseResource> IFhirResourceDao<T> getDao(Class<T> theType) {
if (myResourceTypeToDao == null) { if (myResourceTypeToDao == null) {
myResourceTypeToDao = new HashMap<Class<? extends IBaseResource>, IFhirResourceDao<?>>(); myResourceTypeToDao = new HashMap<Class<? extends IBaseResource>, IFhirResourceDao<?>>();
for (IFhirResourceDao<?> next : myResourceDaos) { for (IFhirResourceDao<?> next : myResourceDaos) {
@ -339,7 +340,7 @@ public abstract class BaseHapiFhirDao implements IDao {
} }
return myResourceTypeToDao.get(theType); return (IFhirResourceDao<T>) myResourceTypeToDao.get(theType);
} }
protected TagDefinition getTag(TagTypeEnum theTagType, String theScheme, String theTerm, String theLabel) { protected TagDefinition getTag(TagTypeEnum theTagType, String theScheme, String theTerm, String theLabel) {
@ -625,12 +626,12 @@ public abstract class BaseHapiFhirDao implements IDao {
} }
protected Set<Long> processMatchUrl(String theMatchUrl, Class<? extends IBaseResource> theResourceType) { protected <T extends IResource> Set<Long> processMatchUrl(String theMatchUrl, Class<T> theResourceType) {
RuntimeResourceDefinition resourceDef = getContext().getResourceDefinition(theResourceType); RuntimeResourceDefinition resourceDef = getContext().getResourceDefinition(theResourceType);
SearchParameterMap paramMap = translateMatchUrl(theMatchUrl, resourceDef); SearchParameterMap paramMap = translateMatchUrl(theMatchUrl, resourceDef);
IFhirResourceDao<? extends IResource> dao = getDao(theResourceType); IFhirResourceDao<T> dao = getDao(theResourceType);
Set<Long> ids = dao.searchForIdsWithAndOr(paramMap); Set<Long> ids = dao.searchForIdsWithAndOr(paramMap);
return ids; return ids;
@ -1019,8 +1020,10 @@ public abstract class BaseHapiFhirDao implements IDao {
return updateEntity(theResource, entity, theUpdateHistory, theDeletedTimestampOrNull, true, true); return updateEntity(theResource, entity, theUpdateHistory, theDeletedTimestampOrNull, true, true);
} }
protected ResourceTable updateEntity(final IResource theResource, ResourceTable entity, boolean theUpdateHistory, Date theDeletedTimestampOrNull, boolean thePerformIndexing, protected ResourceTable updateEntity(final IResource theResource, ResourceTable entity, boolean theUpdateHistory, Date theDeletedTimestampOrNull, boolean thePerformIndexing, boolean theUpdateVersion) {
boolean theUpdateVersion) {
validateResourceForStorage(theResource);
if (entity.getPublished() == null) { if (entity.getPublished() == null) {
entity.setPublished(new Date()); entity.setPublished(new Date());
} }
@ -1028,8 +1031,7 @@ public abstract class BaseHapiFhirDao implements IDao {
if (theResource != null) { if (theResource != null) {
String resourceType = myContext.getResourceDefinition(theResource).getName(); String resourceType = myContext.getResourceDefinition(theResource).getName();
if (isNotBlank(entity.getResourceType()) && !entity.getResourceType().equals(resourceType)) { if (isNotBlank(entity.getResourceType()) && !entity.getResourceType().equals(resourceType)) {
throw new UnprocessableEntityException("Existing resource ID[" + entity.getIdDt().toUnqualifiedVersionless() + "] is of type[" + entity.getResourceType() + "] - Cannot update with [" throw new UnprocessableEntityException("Existing resource ID[" + entity.getIdDt().toUnqualifiedVersionless() + "] is of type[" + entity.getResourceType() + "] - Cannot update with [" + resourceType + "]");
+ resourceType + "]");
} }
} }
@ -1195,6 +1197,16 @@ public abstract class BaseHapiFhirDao implements IDao {
return entity; return entity;
} }
/**
* Subclasses may override to provide specific behaviour
*
* @param theResource
* The resource that is about to be persisted
*/
protected void validateResourceForStorage(IResource theResource) {
// nothing
}
protected static String normalizeString(String theString) { protected static String normalizeString(String theString) {
char[] out = new char[theString.length()]; char[] out = new char[theString.length()];
theString = Normalizer.normalize(theString, Normalizer.Form.NFD); theString = Normalizer.normalize(theString, Normalizer.Form.NFD);

View File

@ -56,8 +56,10 @@ import javax.persistence.criteria.Subquery;
import org.apache.commons.lang3.NotImplementedException; import org.apache.commons.lang3.NotImplementedException;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.omg.PortableInterceptor.InterceptorOperations;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required; import org.springframework.beans.factory.annotation.Required;
import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.PlatformTransactionManager;
@ -857,16 +859,24 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
if (isNotBlank(theResource.getId().getIdPart())) { if (isNotBlank(theResource.getId().getIdPart())) {
if (getContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU1)) { if (getContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU1)) {
if (theResource.getId().isIdPartValidLong()) { if (theResource.getId().isIdPartValidLong()) {
throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "failedToCreateWithClientAssignedNumericId", theResource.getId().getIdPart())); String message = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "failedToCreateWithClientAssignedNumericId", theResource.getId().getIdPart());
throw new InvalidRequestException(message, createErrorOperationOutcome(message));
} }
} else { } else {
throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "failedToCreateWithClientAssignedId", theResource.getId().getIdPart())); String message = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "failedToCreateWithClientAssignedId", theResource.getId().getIdPart());
throw new InvalidRequestException(message, createErrorOperationOutcome(message));
} }
} }
return doCreate(theResource, theIfNoneExist, thePerformIndexing); return doCreate(theResource, theIfNoneExist, thePerformIndexing);
} }
private IBaseOperationOutcome createErrorOperationOutcome(String theMessage) {
return createOperationOutcome("error", theMessage);
}
protected abstract IBaseOperationOutcome createOperationOutcome(String theSeverity, String theMessage);
private Predicate createCompositeParamPart(CriteriaBuilder builder, Root<ResourceTable> from, RuntimeSearchParam left, IQueryParameterType leftValue) { private Predicate createCompositeParamPart(CriteriaBuilder builder, Root<ResourceTable> from, RuntimeSearchParam left, IQueryParameterType leftValue) {
Predicate retVal = null; Predicate retVal = null;
switch (left.getParamType()) { switch (left.getParamType()) {

View File

@ -25,7 +25,7 @@ import ca.uhn.fhir.model.dstu2.valueset.BundleTypeEnum;
import ca.uhn.fhir.model.primitive.UriDt; import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
public class FhirBundleResourceDaoDstu2 extends FhirResourceDaoDstu2<Bundle> { public class FhirResourceDaoBundleDstu2 extends FhirResourceDaoDstu2<Bundle> {
@Override @Override
protected void preProcessResourceForStorage(Bundle theResource) { protected void preProcessResourceForStorage(Bundle theResource) {

View File

@ -24,12 +24,18 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt; import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.ValidationModeEnum;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.util.FhirTerser; import ca.uhn.fhir.util.FhirTerser;
public class FhirResourceDaoDstu1<T extends IResource> extends BaseHapiFhirResourceDao<T> { public class FhirResourceDaoDstu1<T extends IResource> extends BaseHapiFhirResourceDao<T> {
@ -47,5 +53,18 @@ public class FhirResourceDaoDstu1<T extends IResource> extends BaseHapiFhirResou
return values; return values;
} }
@Override
public MethodOutcome validate(T theResource, IdDt theId, String theRawResource, EncodingEnum theEncoding, ValidationModeEnum theMode, String theProfile) {
throw new UnsupportedOperationException();
}
@Override
protected IBaseOperationOutcome createOperationOutcome(String theSeverity, String theMessage) {
OperationOutcome oo = new OperationOutcome();
oo.getIssueFirstRep().getSeverityElement().setValue(theSeverity);
oo.getIssueFirstRep().getDetailsElement().setValue(theMessage);
return oo;
}
} }

View File

@ -20,10 +20,11 @@ package ca.uhn.fhir.jpa.dao;
* #L% * #L%
*/ */
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.ArrayList;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeResourceDefinition;
@ -31,7 +32,20 @@ import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt; import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome.BaseIssue;
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome;
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome.Issue;
import ca.uhn.fhir.model.dstu2.valueset.IssueSeverityEnum;
import ca.uhn.fhir.model.dstu2.valueset.IssueTypeEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.parser.IParserErrorHandler;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.ValidationModeEnum;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.util.FhirTerser; import ca.uhn.fhir.util.FhirTerser;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.ValidationResult;
public class FhirResourceDaoDstu2<T extends IResource> extends BaseHapiFhirResourceDao<T> { public class FhirResourceDaoDstu2<T extends IResource> extends BaseHapiFhirResourceDao<T> {
@ -52,5 +66,48 @@ public class FhirResourceDaoDstu2<T extends IResource> extends BaseHapiFhirResou
return values; return values;
} }
@Override
protected IBaseOperationOutcome createOperationOutcome(String theSeverity, String theMessage) {
OperationOutcome oo = new OperationOutcome();
oo.getIssueFirstRep().getSeverityElement().setValue(theSeverity);
oo.getIssueFirstRep().getDetailsElement().setValue(theMessage);
return oo;
}
@Override
public MethodOutcome validate(T theResource, IdDt theId, String theRawResource, EncodingEnum theEncoding, ValidationModeEnum theMode, String theProfile) {
final OperationOutcome oo = new OperationOutcome();
IParser parser = theEncoding.newParser(getContext());
parser.setParserErrorHandler(new IParserErrorHandler() {
@Override
public void unknownAttribute(IParseLocation theLocation, String theAttributeName) {
oo.addIssue().setSeverity(IssueSeverityEnum.ERROR).setCode(IssueTypeEnum.INVALID_CONTENT).setDetails("Unknown attribute found: " + theAttributeName);
}
@Override
public void unknownElement(IParseLocation theLocation, String theElementName) {
oo.addIssue().setSeverity(IssueSeverityEnum.ERROR).setCode(IssueTypeEnum.INVALID_CONTENT).setDetails("Unknown element found: " + theElementName);
}
});
FhirValidator validator = getContext().newValidator();
validator.setValidateAgainstStandardSchema(true);
validator.setValidateAgainstStandardSchematron(true);
ValidationResult result = validator.validateWithResult(theResource);
OperationOutcome operationOutcome = (OperationOutcome) result.toOperationOutcome();
for (BaseIssue next : operationOutcome.getIssue()) {
oo.getIssue().add((Issue) next);
}
// This method returns a MethodOutcome object
MethodOutcome retVal = new MethodOutcome();
oo.addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDetails("Validation succeeded");
retVal.setOperationOutcome(oo);
return retVal;
}
} }

View File

@ -0,0 +1,74 @@
package ca.uhn.fhir.jpa.dao;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome;
import ca.uhn.fhir.model.dstu2.resource.Questionnaire;
import ca.uhn.fhir.model.dstu2.resource.QuestionnaireAnswers;
import ca.uhn.fhir.model.dstu2.resource.ValueSet;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.validation.FhirQuestionnaireAnswersValidator;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.IResourceLoader;
import ca.uhn.fhir.validation.ValidationResult;
public class FhirResourceDaoQuestionnaireAnswersDstu2 extends FhirResourceDaoDstu2<QuestionnaireAnswers> {
private FhirContext myRefImplCtx = FhirContext.forDstu2Hl7Org();
@Override
protected void validateResourceForStorage(IResource theResource) {
super.validateResourceForStorage(theResource);
QuestionnaireAnswers qa = (QuestionnaireAnswers) theResource;
if (qa.getQuestionnaire().getReference().isEmpty()) {
return;
}
FhirValidator val = myRefImplCtx.newValidator();
val.setValidateAgainstStandardSchema(false);
val.setValidateAgainstStandardSchematron(false);
FhirQuestionnaireAnswersValidator module = new FhirQuestionnaireAnswersValidator();
module.setResourceLoader(new JpaResourceLoader());
val.registerValidatorModule(module);
ValidationResult result = val.validateWithResult(myRefImplCtx.newJsonParser().parseResource(getContext().newJsonParser().encodeResourceToString(qa)));
if (!result.isSuccessful()) {
IBaseOperationOutcome oo = getContext().newJsonParser().parseResource(OperationOutcome.class, myRefImplCtx.newJsonParser().encodeResourceToString(result.toOperationOutcome()));
throw new UnprocessableEntityException(oo);
}
}
public class JpaResourceLoader implements IResourceLoader {
@Override
public <T extends IBaseResource> T load(Class<T> theType, IIdType theId) throws ResourceNotFoundException {
/*
* The QuestionnaireAnswers validator uses RI structures, so for now we need
* to convert between that and HAPI structures. This is a bit hackish, but
* hopefully it will go away at some point.
*/
if ("ValueSet".equals(theType.getSimpleName())) {
IFhirResourceDao<ValueSet> dao = getDao(ValueSet.class);
ValueSet vs = dao.read(theId);
return myRefImplCtx.newJsonParser().parseResource(theType, getContext().newJsonParser().encodeResourceToString(vs));
} else if ("Questionnaire".equals(theType.getSimpleName())) {
IFhirResourceDao<Questionnaire> dao = getDao(Questionnaire.class);
Questionnaire vs = dao.read(theId);
return myRefImplCtx.newJsonParser().parseResource(theType, getContext().newJsonParser().encodeResourceToString(vs));
} else {
// Should not happen, validator will only ask for these two
throw new IllegalStateException("Unexpected request to load resource of type " + theType);
}
}
}
}

View File

@ -132,7 +132,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionInvalidUrl", theAction, theUrl); String msg = getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionInvalidUrl", theAction, theUrl);
throw new InvalidRequestException(msg); throw new InvalidRequestException(msg);
} }
IFhirResourceDao<? extends IResource> dao = null; IFhirResourceDao<? extends IBaseResource> dao = null;
if (resType != null) { if (resType != null) {
dao = getDao(resType.getImplementingClass()); dao = getDao(resType.getImplementingClass());
} }
@ -268,9 +268,9 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
if (isNotBlank(ifNoneMatch)) { if (isNotBlank(ifNoneMatch)) {
throw new InvalidRequestException("Unable to perform vread on '" + url + "' with ifNoneMatch also set. Do not include a version in the URL to perform a conditional read."); throw new InvalidRequestException("Unable to perform vread on '" + url + "' with ifNoneMatch also set. Do not include a version in the URL to perform a conditional read.");
} }
found = resourceDao.read(new IdDt(parts.getResourceType(), parts.getResourceId(), parts.getVersionId())); found = (IResource) resourceDao.read(new IdDt(parts.getResourceType(), parts.getResourceId(), parts.getVersionId()));
} else { } else {
found = resourceDao.read(new IdDt(parts.getResourceType(), parts.getResourceId())); found = (IResource) resourceDao.read(new IdDt(parts.getResourceType(), parts.getResourceId()));
if (isNotBlank(ifNoneMatch) && ifNoneMatch.equals(found.getId().getVersionIdPart())) { if (isNotBlank(ifNoneMatch) && ifNoneMatch.equals(found.getId().getVersionIdPart())) {
notChanged = true; notChanged = true;
} }
@ -392,13 +392,13 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
} }
private static class UrlParts { private static class UrlParts {
private IFhirResourceDao<? extends IResource> myDao; private IFhirResourceDao<? extends IBaseResource> myDao;
private String myParams; private String myParams;
private String myResourceId; private String myResourceId;
private String myResourceType; private String myResourceType;
private String myVersionId; private String myVersionId;
public IFhirResourceDao<? extends IResource> getDao() { public IFhirResourceDao<? extends IBaseResource> getDao() {
return myDao; return myDao;
} }
@ -418,7 +418,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
return myVersionId; return myVersionId;
} }
public void setDao(IFhirResourceDao<? extends IResource> theDao) { public void setDao(IFhirResourceDao<? extends IBaseResource> theDao) {
myDao = theDao; myDao = theDao;
} }

View File

@ -24,18 +24,22 @@ import java.util.Date;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import ca.uhn.fhir.jpa.entity.BaseHasResource; import ca.uhn.fhir.jpa.entity.BaseHasResource;
import ca.uhn.fhir.jpa.entity.TagTypeEnum; import ca.uhn.fhir.jpa.entity.TagTypeEnum;
import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.TagList; import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.dstu2.composite.MetaDt; import ca.uhn.fhir.model.dstu2.composite.MetaDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.ValidationModeEnum;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.IBundleProvider; import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
public interface IFhirResourceDao<T extends IResource> extends IDao { public interface IFhirResourceDao<T extends IBaseResource> extends IDao {
void addTag(IIdType theId, TagTypeEnum theTagType, String theScheme, String theTerm, String theLabel); void addTag(IIdType theId, TagTypeEnum theTagType, String theScheme, String theTerm, String theLabel);
@ -44,8 +48,9 @@ public interface IFhirResourceDao<T extends IResource> extends IDao {
DaoMethodOutcome create(T theResource, String theIfNoneExist); DaoMethodOutcome create(T theResource, String theIfNoneExist);
/** /**
* @param thePerformIndexing Use with caution! If you set this to false, you need to manually perform indexing or your resources * @param thePerformIndexing
* won't be indexed and searches won't work. * Use with caution! If you set this to false, you need to manually perform indexing or your resources
* won't be indexed and searches won't work.
*/ */
DaoMethodOutcome create(T theResource, String theIfNoneExist, boolean thePerformIndexing); DaoMethodOutcome create(T theResource, String theIfNoneExist, boolean thePerformIndexing);
@ -65,20 +70,41 @@ public interface IFhirResourceDao<T extends IResource> extends IDao {
IBundleProvider history(Long theId, Date theSince); IBundleProvider history(Long theId, Date theSince);
/**
* Not supported in DSTU1!
*/
MetaDt metaAddOperation(IIdType theId1, MetaDt theMetaAdd);
/**
* Not supported in DSTU1!
*/
MetaDt metaDeleteOperation(IIdType theId1, MetaDt theMetaDel);
/**
* Not supported in DSTU1!
*/
MetaDt metaGetOperation();
/**
* Not supported in DSTU1!
*/
MetaDt metaGetOperation(IIdType theId);
/** /**
* *
* @param theId * @param theId
* @return * @return
* @throws ResourceNotFoundException * @throws ResourceNotFoundException
* If the ID is not known to the server * If the ID is not known to the server
*/ */
T read(IIdType theId); T read(IIdType theId);
BaseHasResource readEntity(IIdType theId); BaseHasResource readEntity(IIdType theId);
/** /**
* @param theCheckForForcedId If true, this method should fail if the requested ID contains a numeric PID which exists, but is * @param theCheckForForcedId
* obscured by a "forced ID" so should not exist as far as the outside world is concerned. * If true, this method should fail if the requested ID contains a numeric PID which exists, but is
* obscured by a "forced ID" so should not exist as far as the outside world is concerned.
*/ */
BaseHasResource readEntity(IIdType theId, boolean theCheckForForcedId); BaseHasResource readEntity(IIdType theId, boolean theCheckForForcedId);
@ -102,28 +128,14 @@ public interface IFhirResourceDao<T extends IResource> extends IDao {
/** /**
* @param thePerformIndexing * @param thePerformIndexing
* Use with caution! If you set this to false, you need to manually perform indexing or your resources * Use with caution! If you set this to false, you need to manually perform indexing or your resources
* won't be indexed and searches won't work. * won't be indexed and searches won't work.
*/ */
DaoMethodOutcome update(T theResource, String theMatchUrl, boolean thePerformIndexing); DaoMethodOutcome update(T theResource, String theMatchUrl, boolean thePerformIndexing);
/** /**
* Not supported in DSTU1! * Not supported in DSTU1!
*/ */
MetaDt metaGetOperation(); MethodOutcome validate(T theResource, IdDt theId, String theRawResource, EncodingEnum theEncoding, ValidationModeEnum theMode, String theProfile);
/**
* Not supported in DSTU1!
*/
MetaDt metaGetOperation(IIdType theId);
/**
* Not supported in DSTU1!
*/
MetaDt metaDeleteOperation(IIdType theId1, MetaDt theMetaDel);
/**
* Not supported in DSTU1!
*/
MetaDt metaAddOperation(IIdType theId1, MetaDt theMetaAdd);
} }

View File

@ -165,38 +165,7 @@ public class JpaResourceProviderDstu2<T extends IResource> extends BaseJpaResour
@Validate @Validate
public MethodOutcome validate(@ResourceParam T theResource, @IdParam IdDt theId, @ResourceParam String theRawResource, @ResourceParam EncodingEnum theEncoding, @Validate.Mode ValidationModeEnum theMode, public MethodOutcome validate(@ResourceParam T theResource, @IdParam IdDt theId, @ResourceParam String theRawResource, @ResourceParam EncodingEnum theEncoding, @Validate.Mode ValidationModeEnum theMode,
@Validate.Profile String theProfile) { @Validate.Profile String theProfile) {
return getDao().validate(theResource, theId, theRawResource, theEncoding, theMode, theProfile);
final OperationOutcome oo = new OperationOutcome();
IParser parser = theEncoding.newParser(getContext());
parser.setParserErrorHandler(new IParserErrorHandler() {
@Override
public void unknownAttribute(IParseLocation theLocation, String theAttributeName) {
oo.addIssue().setSeverity(IssueSeverityEnum.ERROR).setCode(IssueTypeEnum.INVALID_CONTENT).setDetails("Unknown attribute found: " + theAttributeName);
}
@Override
public void unknownElement(IParseLocation theLocation, String theElementName) {
oo.addIssue().setSeverity(IssueSeverityEnum.ERROR).setCode(IssueTypeEnum.INVALID_CONTENT).setDetails("Unknown element found: " + theElementName);
}
});
FhirValidator validator = getContext().newValidator();
validator.setValidateAgainstStandardSchema(true);
validator.setValidateAgainstStandardSchematron(true);
ValidationResult result = validator.validateWithResult(theResource);
OperationOutcome operationOutcome = (OperationOutcome) result.getOperationOutcome();
for (BaseIssue next : operationOutcome.getIssue()) {
oo.getIssue().add((Issue) next);
}
// This method returns a MethodOutcome object
MethodOutcome retVal = new MethodOutcome();
oo.addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDetails("Validation succeeded");
retVal.setOperationOutcome(oo);
return retVal;
} }
} }

View File

@ -50,6 +50,7 @@ import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum; import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import ca.uhn.fhir.model.dstu.resource.Device; import ca.uhn.fhir.model.dstu.resource.Device;
import ca.uhn.fhir.model.dstu.resource.Practitioner; import ca.uhn.fhir.model.dstu.resource.Practitioner;
import ca.uhn.fhir.model.dstu2.composite.CodingDt;
import ca.uhn.fhir.model.dstu2.composite.MetaDt; import ca.uhn.fhir.model.dstu2.composite.MetaDt;
import ca.uhn.fhir.model.dstu2.composite.PeriodDt; import ca.uhn.fhir.model.dstu2.composite.PeriodDt;
import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt; import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt;
@ -65,7 +66,10 @@ import ca.uhn.fhir.model.dstu2.resource.Observation;
import ca.uhn.fhir.model.dstu2.resource.Organization; import ca.uhn.fhir.model.dstu2.resource.Organization;
import ca.uhn.fhir.model.dstu2.resource.Parameters; import ca.uhn.fhir.model.dstu2.resource.Parameters;
import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.dstu2.resource.Questionnaire;
import ca.uhn.fhir.model.dstu2.resource.QuestionnaireAnswers;
import ca.uhn.fhir.model.dstu2.resource.ValueSet; import ca.uhn.fhir.model.dstu2.resource.ValueSet;
import ca.uhn.fhir.model.dstu2.valueset.AnswerFormatEnum;
import ca.uhn.fhir.model.dstu2.valueset.EncounterClassEnum; import ca.uhn.fhir.model.dstu2.valueset.EncounterClassEnum;
import ca.uhn.fhir.model.dstu2.valueset.EncounterStateEnum; import ca.uhn.fhir.model.dstu2.valueset.EncounterStateEnum;
import ca.uhn.fhir.model.dstu2.valueset.HTTPVerbEnum; import ca.uhn.fhir.model.dstu2.valueset.HTTPVerbEnum;
@ -188,27 +192,26 @@ public class ResourceProviderDstu2Test extends BaseJpaTest {
@Test @Test
public void testMetaOperations() throws Exception { public void testMetaOperations() throws Exception {
String methodName = "testMetaOperations"; String methodName = "testMetaOperations";
Patient pt = new Patient(); Patient pt = new Patient();
pt.addName().addFamily(methodName); pt.addName().addFamily(methodName);
IIdType id = ourClient.create().resource(pt).execute().getId().toUnqualifiedVersionless(); IIdType id = ourClient.create().resource(pt).execute().getId().toUnqualifiedVersionless();
MetaDt meta = ourClient.meta().get(MetaDt.class).fromResource(id).execute(); MetaDt meta = ourClient.meta().get(MetaDt.class).fromResource(id).execute();
assertEquals(0, meta.getTag().size()); assertEquals(0, meta.getTag().size());
MetaDt inMeta = new MetaDt(); MetaDt inMeta = new MetaDt();
inMeta.addTag().setSystem("urn:system1").setCode("urn:code1"); inMeta.addTag().setSystem("urn:system1").setCode("urn:code1");
meta = ourClient.meta().add().onResource(id).meta(inMeta).execute(); meta = ourClient.meta().add().onResource(id).meta(inMeta).execute();
assertEquals(1, meta.getTag().size()); assertEquals(1, meta.getTag().size());
inMeta = new MetaDt(); inMeta = new MetaDt();
inMeta.addTag().setSystem("urn:system1").setCode("urn:code1"); inMeta.addTag().setSystem("urn:system1").setCode("urn:code1");
meta = ourClient.meta().delete().onResource(id).meta(inMeta).execute(); meta = ourClient.meta().delete().onResource(id).meta(inMeta).execute();
assertEquals(0, meta.getTag().size()); assertEquals(0, meta.getTag().size());
} }
@Test @Test
public void testCreateResourceConditional() throws IOException { public void testCreateResourceConditional() throws IOException {
String methodName = "testCreateResourceConditional"; String methodName = "testCreateResourceConditional";
@ -245,6 +248,40 @@ public class ResourceProviderDstu2Test extends BaseJpaTest {
} }
@Test
public void testCreateQuestionnaireAnswersWithValidation() throws IOException {
String methodName = "testCreateQuestionnaireAnswersWithValidation";
ValueSet options = new ValueSet();
options.getDefine().setSystem("urn:system").addConcept().setCode("code0");
IIdType optId = ourClient.create().resource(options).execute().getId();
Questionnaire q = new Questionnaire();
q.getGroup().addQuestion().setLinkId("link0").setRequired(false).setType(AnswerFormatEnum.CHOICE).setOptions(new ResourceReferenceDt(optId));
IIdType qId = ourClient.create().resource(q).execute().getId();
QuestionnaireAnswers qa;
// Good code
qa = new QuestionnaireAnswers();
qa.getQuestionnaire().setReference(qId.toUnqualifiedVersionless().getValue());
qa.getGroup().addQuestion().setLinkId("link0").addAnswer().setValue(new CodingDt().setSystem("urn:system").setCode("code0"));
ourClient.create().resource(qa).execute();
// Bad code
qa = new QuestionnaireAnswers();
qa.getQuestionnaire().setReference(qId.toUnqualifiedVersionless().getValue());
qa.getGroup().addQuestion().setLinkId("link0").addAnswer().setValue(new CodingDt().setSystem("urn:system").setCode("code1"));
try {
ourClient.create().resource(qa).execute();
fail();
} catch (UnprocessableEntityException e) {
assertThat(e.getMessage(), containsString("Question with linkId[link0]"));
}
}
@Test @Test
public void testCreateResourceWithNumericId() throws IOException { public void testCreateResourceWithNumericId() throws IOException {
String resource = "<Patient xmlns=\"http://hl7.org/fhir\"></Patient>"; String resource = "<Patient xmlns=\"http://hl7.org/fhir\"></Patient>";
@ -255,7 +292,10 @@ public class ResourceProviderDstu2Test extends BaseJpaTest {
CloseableHttpResponse response = ourHttpClient.execute(post); CloseableHttpResponse response = ourHttpClient.execute(post);
try { try {
assertEquals(400, response.getStatusLine().getStatusCode()); assertEquals(400, response.getStatusLine().getStatusCode());
String respString = IOUtils.toString(response.getEntity().getContent());
ourLog.info(respString);
} finally { } finally {
response.getEntity().getContent().close();
response.close(); response.close();
} }
} }
@ -365,7 +405,8 @@ public class ResourceProviderDstu2Test extends BaseJpaTest {
} }
/* /*
* Try it with a raw socket call. The Apache client won't let us use the unescaped "|" in the URL but we want to make sure that works too.. * Try it with a raw socket call. The Apache client won't let us use the unescaped "|" in the URL but we want to
* make sure that works too..
*/ */
Socket sock = new Socket(); Socket sock = new Socket();
sock.setSoTimeout(3000); sock.setSoTimeout(3000);
@ -739,8 +780,7 @@ public class ResourceProviderDstu2Test extends BaseJpaTest {
p1.addIdentifier().setValue("testSearchByIdentifierWithoutSystem01"); p1.addIdentifier().setValue("testSearchByIdentifierWithoutSystem01");
IdDt p1Id = (IdDt) ourClient.create().resource(p1).execute().getId(); IdDt p1Id = (IdDt) ourClient.create().resource(p1).execute().getId();
Bundle actual = ourClient.search().forResource(Patient.class).where(Patient.IDENTIFIER.exactly().systemAndCode(null, "testSearchByIdentifierWithoutSystem01")).encodedJson().prettyPrint() Bundle actual = ourClient.search().forResource(Patient.class).where(Patient.IDENTIFIER.exactly().systemAndCode(null, "testSearchByIdentifierWithoutSystem01")).encodedJson().prettyPrint().execute();
.execute();
assertEquals(1, actual.size()); assertEquals(1, actual.size());
assertEquals(p1Id.getIdPart(), actual.getEntries().get(0).getResource().getId().getIdPart()); assertEquals(p1Id.getIdPart(), actual.getEntries().get(0).getResource().getId().getIdPart());
@ -1166,8 +1206,7 @@ public class ResourceProviderDstu2Test extends BaseJpaTest {
assertThat(p1Id.getValue(), containsString("Patient/testUpdateWithClientSuppliedIdWhichDoesntExistRpDstu2/_history")); assertThat(p1Id.getValue(), containsString("Patient/testUpdateWithClientSuppliedIdWhichDoesntExistRpDstu2/_history"));
Bundle actual = ourClient.search().forResource(Patient.class).where(Patient.IDENTIFIER.exactly().systemAndCode("urn:system", "testUpdateWithClientSuppliedIdWhichDoesntExistRpDstu2")) Bundle actual = ourClient.search().forResource(Patient.class).where(Patient.IDENTIFIER.exactly().systemAndCode("urn:system", "testUpdateWithClientSuppliedIdWhichDoesntExistRpDstu2")).encodedJson().prettyPrint().execute();
.encodedJson().prettyPrint().execute();
assertEquals(1, actual.size()); assertEquals(1, actual.size());
assertEquals(p1Id.getIdPart(), actual.getEntries().get(0).getResource().getId().getIdPart()); assertEquals(p1Id.getIdPart(), actual.getEntries().get(0).getResource().getId().getIdPart());

View File

@ -21,6 +21,7 @@ import org.mockito.ArgumentCaptor;
import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs; import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import ca.uhn.fhir.model.dstu.resource.OperationOutcome; import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
import ca.uhn.fhir.model.dstu.resource.Patient; import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
@ -122,7 +123,6 @@ public class ExceptionHandlingTest {
assertThat(e.getResponseBody(), StringContains.containsString("value=\"foo\"")); assertThat(e.getResponseBody(), StringContains.containsString("value=\"foo\""));
} }
} }
@Test @Test
@ -146,7 +146,7 @@ public class ExceptionHandlingTest {
assertThat(e.getMessage(), StringContains.containsString("HTTP 500 Internal Error")); assertThat(e.getMessage(), StringContains.containsString("HTTP 500 Internal Error"));
assertThat(e.getMessage(), StringContains.containsString("Help I'm a bug")); assertThat(e.getMessage(), StringContains.containsString("Help I'm a bug"));
assertNotNull(e.getOperationOutcome()); assertNotNull(e.getOperationOutcome());
assertEquals("Help I'm a bug",e.getOperationOutcome().getIssueFirstRep().getDetailsElement().getValue()); assertEquals("Help I'm a bug", ((BaseOperationOutcome) e.getOperationOutcome()).getIssueFirstRep().getDetailsElement().getValue());
} }
} }
@ -164,7 +164,7 @@ public class ExceptionHandlingTest {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", contentType + "; charset=UTF-8")); when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", contentType + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"))); when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
IMyClient client = ourCtx.newRestfulClient(IMyClient.class,"http://example.com/fhir"); IMyClient client = ourCtx.newRestfulClient(IMyClient.class, "http://example.com/fhir");
try { try {
client.read(new IdDt("Patient/1234")); client.read(new IdDt("Patient/1234"));
fail(); fail();
@ -172,15 +172,14 @@ public class ExceptionHandlingTest {
assertThat(e.getMessage(), StringContains.containsString("HTTP 500 Internal Error")); assertThat(e.getMessage(), StringContains.containsString("HTTP 500 Internal Error"));
assertThat(e.getMessage(), StringContains.containsString("Help I'm a bug")); assertThat(e.getMessage(), StringContains.containsString("Help I'm a bug"));
assertNotNull(e.getOperationOutcome()); assertNotNull(e.getOperationOutcome());
assertEquals("Help I'm a bug",e.getOperationOutcome().getIssueFirstRep().getDetailsElement().getValue()); assertEquals("Help I'm a bug", ((BaseOperationOutcome) e.getOperationOutcome()).getIssueFirstRep().getDetailsElement().getValue());
} }
} }
public interface IMyClient extends IRestfulClient public interface IMyClient extends IRestfulClient {
{
@Read @Read
Patient read(@IdParam IdDt theId); Patient read(@IdParam IdDt theId);
} }
} }

View File

@ -1,13 +1,10 @@
package ca.uhn.fhir.rest.server.exceptions; package ca.uhn.fhir.rest.server.exceptions;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.*;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.junit.Test; import org.junit.Test;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.reflect.ClassPath; import com.google.common.reflect.ClassPath;
import com.google.common.reflect.ClassPath.ClassInfo; import com.google.common.reflect.ClassPath.ClassInfo;
@ -48,9 +45,9 @@ public class ExceptionPropertiesTest {
} }
try { try {
next.getConstructor(String.class, BaseOperationOutcome.class); next.getConstructor(String.class, IBaseOperationOutcome.class);
} catch (NoSuchMethodException e) { } catch (NoSuchMethodException e) {
fail(classInfo.getName() + " has no constructor with params: (String, OperationOutcome)"); fail(classInfo.getName() + " has no constructor with params: (String, IBaseOperationOutcome)");
} }
} }

View File

@ -90,7 +90,7 @@ public class FhirDstu2 implements IFhirVersion {
str = FhirDstu2.class.getResourceAsStream("ca/uhn/fhir/model/dstu2/fhirversion.properties"); str = FhirDstu2.class.getResourceAsStream("ca/uhn/fhir/model/dstu2/fhirversion.properties");
} }
if (str == null) { if (str == null) {
throw new ConfigurationException("Can not find model property file on classpath: " + "/ca/uhn/fhir/model/dstu2/model.properties"); throw new ConfigurationException("Can not find model property file on classpath: " + "/ca/uhn/fhir/model/dstu2/fhirversion.properties");
} }
return str; return str;
} }

View File

@ -1,12 +1,87 @@
package ca.uhn.fhir.rest.server.interceptor; package ca.uhn.fhir.rest.server.interceptor;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Test; import org.junit.Test;
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome;
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome.Issue;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.method.RequestDetails;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
public class ResponseHighlightingInterceptorTest { public class ResponseHighlightingInterceptorTest {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResponseHighlightingInterceptorTest.class);
@Test @Test
public void testHighlight() { public void testHighlightNormalResponse() throws Exception {
ResponseHighlighterInterceptor ic = new ResponseHighlighterInterceptor(); ResponseHighlighterInterceptor ic = new ResponseHighlighterInterceptor();
HttpServletRequest req = mock(HttpServletRequest.class);
when(req.getHeader(Constants.HEADER_ACCEPT)).thenReturn("text/html");
HttpServletResponse resp = mock(HttpServletResponse.class);
StringWriter sw = new StringWriter();
when(resp.getWriter()).thenReturn(new PrintWriter(sw));
Patient resource = new Patient();
resource.addName().addFamily("FAMILY");
RequestDetails reqDetails = new RequestDetails();
reqDetails.setRequestType(RequestTypeEnum.GET);
reqDetails.setParameters(new HashMap<String, String[]>());
reqDetails.setServer(new RestfulServer());
reqDetails.setServletRequest(req);
assertFalse(ic.outgoingResponse(reqDetails, resource, req, resp));
String output = sw.getBuffer().toString();
ourLog.info(output);
assertThat(output, containsString("<span class='hlTagName'>Patient</span>"));
} }
@Test
public void testHighlightException() throws Exception {
ResponseHighlighterInterceptor ic = new ResponseHighlighterInterceptor();
HttpServletRequest req = mock(HttpServletRequest.class);
when(req.getHeader(Constants.HEADER_ACCEPT)).thenReturn("text/html");
HttpServletResponse resp = mock(HttpServletResponse.class);
StringWriter sw = new StringWriter();
when(resp.getWriter()).thenReturn(new PrintWriter(sw));
Patient resource = new Patient();
resource.addName().addFamily("FAMILY");
RequestDetails reqDetails = new RequestDetails();
reqDetails.setRequestType(RequestTypeEnum.GET);
reqDetails.setParameters(new HashMap<String, String[]>());
reqDetails.setServer(new RestfulServer());
reqDetails.setServletRequest(req);
ResourceNotFoundException exception = new ResourceNotFoundException("Not found");
exception.setOperationOutcome(new OperationOutcome().addIssue(new Issue().setDetails("Hello")));
assertFalse(ic.handleException(reqDetails, exception, req, resp));
String output = sw.getBuffer().toString();
ourLog.info(output);
assertThat(output, containsString("<span class='hlTagName'>OperationOutcome</span>"));
}
} }

View File

@ -10,7 +10,7 @@ import ca.uhn.fhir.model.api.Bundle;
/** /**
* Base class for a bridge between the RI validation tools and HAPI * Base class for a bridge between the RI validation tools and HAPI
*/ */
abstract class BaseValidatorBridge implements IValidator { abstract class BaseValidatorBridge implements IValidatorModule {
public BaseValidatorBridge() { public BaseValidatorBridge() {
super(); super();

View File

@ -30,7 +30,7 @@ import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
public class FhirInstanceValidator extends BaseValidatorBridge implements IValidator { public class FhirInstanceValidator extends BaseValidatorBridge implements IValidatorModule {
static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirInstanceValidator.class); static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirInstanceValidator.class);

View File

@ -19,7 +19,7 @@ public class FhirInstanceValidatorTest {
val = ourCtx.newValidator(); val = ourCtx.newValidator();
val.setValidateAgainstStandardSchema(false); val.setValidateAgainstStandardSchema(false);
val.setValidateAgainstStandardSchematron(false); val.setValidateAgainstStandardSchematron(false);
val.registerValidator(new FhirInstanceValidator()); val.registerValidatorModule(new FhirInstanceValidator());
} }
@Test @Test

View File

@ -47,7 +47,7 @@ public class QuestionnaireAnswersValidatorIntegrationTest {
myVal = ourCtx.newValidator(); myVal = ourCtx.newValidator();
myVal.setValidateAgainstStandardSchema(false); myVal.setValidateAgainstStandardSchema(false);
myVal.setValidateAgainstStandardSchematron(false); myVal.setValidateAgainstStandardSchematron(false);
myVal.registerValidator(qaVal); myVal.registerValidatorModule(qaVal);
} }
@Test @Test
@ -73,7 +73,7 @@ public class QuestionnaireAnswersValidatorIntegrationTest {
Questionnaire q = new Questionnaire(); Questionnaire q = new Questionnaire();
q.getGroup().addQuestion().setLinkId("link0").setRequired(false).setType(AnswerFormat.CHOICE).setOptions(new Reference("http://somevalueset/ValueSet/123")); q.getGroup().addQuestion().setLinkId("link0").setRequired(false).setType(AnswerFormat.CHOICE).setOptions(new Reference("http://somevalueset/ValueSet/123"));
when(myResourceLoaderMock.load(Mockito.eq(Questionnaire.class), Mockito.eq(new IdType("http://example.com/Questionnaire/q1")))).thenReturn(q); when(myResourceLoaderMock.load(Mockito.eq(Questionnaire.class), Mockito.eq(new IdType("http://example.com/Questionnaire/q1")))).thenReturn(q);
ValueSet options = new ValueSet(); ValueSet options = new ValueSet();
options.getDefine().setSystem("urn:system").addConcept().setCode("code0"); options.getDefine().setSystem("urn:system").addConcept().setCode("code0");
when(myResourceLoaderMock.load(Mockito.eq(ValueSet.class), Mockito.eq(new IdType("http://somevalueset/ValueSet/123")))).thenReturn(options); when(myResourceLoaderMock.load(Mockito.eq(ValueSet.class), Mockito.eq(new IdType("http://somevalueset/ValueSet/123")))).thenReturn(options);
@ -97,6 +97,8 @@ public class QuestionnaireAnswersValidatorIntegrationTest {
ourLog.info(result.getMessages().toString()); ourLog.info(result.getMessages().toString());
assertThat(result.getMessages().toString(), containsString("myLocationString=QuestionnaireAnswers.group(0).question(0).answer(0)")); assertThat(result.getMessages().toString(), containsString("myLocationString=QuestionnaireAnswers.group(0).question(0).answer(0)"));
assertThat(result.getMessages().toString(), containsString("myMessage=Question with linkId[link0] has answer with system[urn:system] and code[code1] but this is not a valid answer for ValueSet[http://somevalueset/ValueSet/123]")); assertThat(result.getMessages().toString(), containsString("myMessage=Question with linkId[link0] has answer with system[urn:system] and code[code1] but this is not a valid answer for ValueSet[http://somevalueset/ValueSet/123]"));
result.toOperationOutcome();
} }

View File

@ -28,8 +28,8 @@
#foreach ( $res in $resources ) #foreach ( $res in $resources )
<bean id="my${res.name}Dao${versionCapitalized}" <bean id="my${res.name}Dao${versionCapitalized}"
#if ( ${res.name} == 'Bundle' ) #if ( ${versionCapitalized} == 'Dstu2' && ( ${res.name} == 'Bundle' || ${res.name} == 'QuestionnaireAnswers' ))
class="ca.uhn.fhir.jpa.dao.Fhir${res.name}ResourceDao${versionCapitalized}"> class="ca.uhn.fhir.jpa.dao.FhirResourceDao${res.name}${versionCapitalized}">
#else #else
class="ca.uhn.fhir.jpa.dao.FhirResourceDao${versionCapitalized}"> class="ca.uhn.fhir.jpa.dao.FhirResourceDao${versionCapitalized}">
#end #end

View File

@ -6,6 +6,15 @@
<title>HAPI FHIR Changelog</title> <title>HAPI FHIR Changelog</title>
</properties> </properties>
<body> <body>
<release version="1.2" date="2015-07-13">
<action type="add">
JPA server now validates QuestionnaireAnswers for conformance to their respective Questionnaire
if one is declared.
</action>
<action type="add">
SyntaxHighlightingInterceptor now also highlights OperationOutcome responses for errors/exceptions.
</action>
</release>
<release version="1.1" date="2015-07-13"> <release version="1.1" date="2015-07-13">
<action type="add"> <action type="add">
Add support for reference implementation structures. Add support for reference implementation structures.