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