View Javadoc
1   package ca.uhn.fhir.jpa.dao.index;
2   
3   /*-
4    * #%L
5    * HAPI FHIR JPA Server
6    * %%
7    * Copyright (C) 2014 - 2019 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.context.FhirContext;
24  import ca.uhn.fhir.context.RuntimeResourceDefinition;
25  import ca.uhn.fhir.context.RuntimeSearchParam;
26  import ca.uhn.fhir.jpa.dao.DaoConfig;
27  import ca.uhn.fhir.jpa.dao.DaoRegistry;
28  import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
29  import ca.uhn.fhir.jpa.model.entity.ResourceTable;
30  import ca.uhn.fhir.jpa.searchparam.extractor.IResourceLinkResolver;
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.ResourceNotFoundException;
34  import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
35  import org.hl7.fhir.instance.model.api.IBaseResource;
36  import org.hl7.fhir.instance.model.api.IIdType;
37  import org.springframework.beans.factory.annotation.Autowired;
38  import org.springframework.stereotype.Service;
39  
40  import javax.persistence.EntityManager;
41  import javax.persistence.PersistenceContext;
42  import javax.persistence.PersistenceContextType;
43  
44  @Service
45  public class DaoResourceLinkResolver implements IResourceLinkResolver {
46  	private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DaoResourceLinkResolver.class);
47  
48  	@Autowired
49  	private DaoConfig myDaoConfig;
50  	@Autowired
51  	private FhirContext myContext;
52  	@Autowired
53  	private IdHelperService myIdHelperService;
54  	@Autowired
55  	private DaoRegistry myDaoRegistry;
56  
57  	@PersistenceContext(type = PersistenceContextType.TRANSACTION)
58  	protected EntityManager myEntityManager;
59  
60  	@Override
61  	public ResourceTable findTargetResource(RuntimeSearchParam theNextSpDef, String theNextPathsUnsplit, IIdType theNextId, String theTypeString, Class<? extends IBaseResource> theType, String theId, RequestDetails theRequest) {
62  		ResourceTable target;
63  		Long valueOf;
64  		try {
65  			valueOf = myIdHelperService.translateForcedIdToPid(theTypeString, theId, theRequest);
66  			ourLog.trace("Translated {}/{} to resource PID {}", theType, theId, valueOf);
67  		} catch (ResourceNotFoundException e) {
68  			if (myDaoConfig.isEnforceReferentialIntegrityOnWrite() == false) {
69  				return null;
70  			}
71  			RuntimeResourceDefinition missingResourceDef = myContext.getResourceDefinition(theType);
72  			String resName = missingResourceDef.getName();
73  
74  			if (myDaoConfig.isAutoCreatePlaceholderReferenceTargets()) {
75  				IBaseResource newResource = missingResourceDef.newInstance();
76  				newResource.setId(resName + "/" + theId);
77  				IFhirResourceDao<IBaseResource> placeholderResourceDao = (IFhirResourceDao<IBaseResource>) myDaoRegistry.getResourceDao(newResource.getClass());
78  				ourLog.debug("Automatically creating empty placeholder resource: {}", newResource.getIdElement().getValue());
79  				valueOf = placeholderResourceDao.update(newResource).getEntity().getId();
80  			} else {
81  				throw new InvalidRequestException("Resource " + resName + "/" + theId + " not found, specified in path: " + theNextPathsUnsplit);
82  			}
83  		}
84  		target = myEntityManager.find(ResourceTable.class, valueOf);
85  		RuntimeResourceDefinition targetResourceDef = myContext.getResourceDefinition(theType);
86  		if (target == null) {
87  			String resName = targetResourceDef.getName();
88  			throw new InvalidRequestException("Resource " + resName + "/" + theId + " not found, specified in path: " + theNextPathsUnsplit);
89  		}
90  
91  		ourLog.trace("Resource PID {} is of type {}", valueOf, target.getResourceType());
92  		if (!theTypeString.equals(target.getResourceType())) {
93  			ourLog.error("Resource {} with PID {} was not of type {}", target.getIdDt().getValue(), target.getId(), theTypeString);
94  			throw new UnprocessableEntityException(
95  				"Resource contains reference to " + theNextId.getValue() + " but resource with ID " + theNextId.getIdPart() + " is actually of type " + target.getResourceType());
96  		}
97  
98  		if (target.getDeleted() != null) {
99  			String resName = targetResourceDef.getName();
100 			throw new InvalidRequestException("Resource " + resName + "/" + theId + " is deleted, specified in path: " + theNextPathsUnsplit);
101 		}
102 
103 		if (theNextSpDef.getTargets() != null && !theNextSpDef.getTargets().contains(theTypeString)) {
104 			return null;
105 		}
106 		return target;
107 	}
108 
109 	@Override
110 	public void validateTypeOrThrowException(Class<? extends IBaseResource> theType) {
111 		myDaoRegistry.getDaoOrThrowException(theType);
112 	}
113 }