View Javadoc
1   package ca.uhn.fhir.jpa.dao.r4;
2   
3   /*
4    * #%L
5    * HAPI FHIR JPA Server
6    * %%
7    * Copyright (C) 2014 - 2018 University Health Network
8    * %%
9    * Licensed under the Apache License, Version 2.0 (the "License");
10   * you may not use this file except in compliance with the License.
11   * You may obtain a copy of the License at
12   * 
13   *      http://www.apache.org/licenses/LICENSE-2.0
14   * 
15   * Unless required by applicable law or agreed to in writing, software
16   * distributed under the License is distributed on an "AS IS" BASIS,
17   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18   * See the License for the specific language governing permissions and
19   * limitations under the License.
20   * #L%
21   */
22  
23  import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
24  import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
25  import ca.uhn.fhir.jpa.entity.ResourceTable;
26  import ca.uhn.fhir.jpa.util.DeleteConflict;
27  import ca.uhn.fhir.rest.api.EncodingEnum;
28  import ca.uhn.fhir.rest.api.MethodOutcome;
29  import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
30  import ca.uhn.fhir.rest.api.ValidationModeEnum;
31  import ca.uhn.fhir.rest.api.server.RequestDetails;
32  import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
33  import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
34  import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
35  import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
36  import ca.uhn.fhir.validation.FhirValidator;
37  import ca.uhn.fhir.validation.IValidationContext;
38  import ca.uhn.fhir.validation.IValidatorModule;
39  import ca.uhn.fhir.validation.ValidationResult;
40  import org.hl7.fhir.exceptions.FHIRException;
41  import org.hl7.fhir.instance.model.api.IAnyResource;
42  import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
43  import org.hl7.fhir.instance.model.api.IBaseResource;
44  import org.hl7.fhir.instance.model.api.IIdType;
45  import org.hl7.fhir.r4.model.IdType;
46  import org.hl7.fhir.r4.model.OperationOutcome;
47  import org.hl7.fhir.r4.model.OperationOutcome.IssueSeverity;
48  import org.hl7.fhir.r4.model.OperationOutcome.OperationOutcomeIssueComponent;
49  import org.springframework.beans.factory.annotation.Autowired;
50  import org.springframework.beans.factory.annotation.Qualifier;
51  
52  import java.util.ArrayList;
53  import java.util.List;
54  
55  import static org.apache.commons.lang3.StringUtils.isNotBlank;
56  
57  public class FhirResourceDaoR4<T extends IAnyResource> extends BaseHapiFhirResourceDao<T> {
58  
59  	private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4.class);
60  
61  	@Autowired()
62  	@Qualifier("myInstanceValidatorR4")
63  	private IValidatorModule myInstanceValidator;
64  
65  	@Override
66  	protected IBaseOperationOutcome createOperationOutcome(String theSeverity, String theMessage, String theCode) {
67  		OperationOutcome oo = new OperationOutcome();
68  		OperationOutcomeIssueComponent issue = oo.addIssue();
69  		issue.getSeverityElement().setValueAsString(theSeverity);
70  		issue.setDiagnostics(theMessage);
71  		try {
72  			issue.setCode(org.hl7.fhir.r4.model.OperationOutcome.IssueType.fromCode(theCode));
73  		} catch (FHIRException e) {
74  			ourLog.error("Unknown code: {}", theCode);
75  		}
76  		return oo;
77  	}
78  
79  
80  	@Override
81  	public MethodOutcome validate(T theResource, IIdType theId, String theRawResource, EncodingEnum theEncoding, ValidationModeEnum theMode, String theProfile, RequestDetails theRequestDetails) {
82  		ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, theResource, null, theId);
83  		notifyInterceptors(RestOperationTypeEnum.VALIDATE, requestDetails);
84  
85  		if (theMode == ValidationModeEnum.DELETE) {
86  			if (theId == null || theId.hasIdPart() == false) {
87  				throw new InvalidRequestException("No ID supplied. ID is required when validating with mode=DELETE");
88  			}
89  			final ResourceTable entity = readEntityLatestVersion(theId);
90  
91  			// Validate that there are no resources pointing to the candidate that
92  			// would prevent deletion
93  			List<DeleteConflict> deleteConflicts = new ArrayList<DeleteConflict>();
94  			if (myDaoConfig.isEnforceReferentialIntegrityOnDelete()) {
95  				validateOkToDelete(deleteConflicts, entity, true);
96  			}
97  			validateDeleteConflictsEmptyOrThrowException(deleteConflicts);
98  
99  			OperationOutcome oo = new OperationOutcome();
100 			oo.addIssue().setSeverity(IssueSeverity.INFORMATION).setDiagnostics("Ok to delete");
101 			return new MethodOutcome(new IdType(theId.getValue()), oo);
102 		}
103 
104 		FhirValidator validator = getContext().newValidator();
105 
106 		validator.registerValidatorModule(myInstanceValidator);
107 
108 		validator.registerValidatorModule(new IdChecker(theMode));
109 
110 		IBaseResource resourceToValidateById = null;
111 		if (theId != null && theId.hasResourceType() && theId.hasIdPart()) {
112 			Class<? extends IBaseResource> type = getContext().getResourceDefinition(theId.getResourceType()).getImplementingClass();
113 			IFhirResourceDao<? extends IBaseResource> dao = getDao(type);
114 			resourceToValidateById = dao.read(theId, theRequestDetails);
115 		}
116 
117 		ValidationResult result;
118 		if (theResource == null) {
119 			if (resourceToValidateById != null) {
120 				result = validator.validateWithResult(resourceToValidateById);
121 			} else {
122 				String msg = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "cantValidateWithNoResource");
123 				throw new InvalidRequestException(msg);
124 			}
125 		} else if (isNotBlank(theRawResource)) {
126 			result = validator.validateWithResult(theRawResource);
127 		} else {
128 			result = validator.validateWithResult(theResource);
129 		}
130 
131 		if (result.isSuccessful()) {
132 			MethodOutcome retVal = new MethodOutcome();
133 			retVal.setOperationOutcome(result.toOperationOutcome());
134 			return retVal;
135 		} else {
136 			throw new PreconditionFailedException("Validation failed", result.toOperationOutcome());
137 		}
138 
139 	}
140 
141 	private class IdChecker implements IValidatorModule {
142 
143 		private ValidationModeEnum myMode;
144 
145 		public IdChecker(ValidationModeEnum theMode) {
146 			myMode = theMode;
147 		}
148 
149 		@Override
150 		public void validateResource(IValidationContext<IBaseResource> theCtx) {
151 			boolean hasId = theCtx.getResource().getIdElement().hasIdPart();
152 			if (myMode == ValidationModeEnum.CREATE) {
153 				if (hasId) {
154 					throw new UnprocessableEntityException("Resource has an ID - ID must not be populated for a FHIR create");
155 				}
156 			} else if (myMode == ValidationModeEnum.UPDATE) {
157 				if (hasId == false) {
158 					throw new UnprocessableEntityException("Resource has no ID - ID must be populated for a FHIR update");
159 				}
160 			}
161 
162 		}
163 
164 	}
165 
166 }