-
Notifications
You must be signed in to change notification settings - Fork 1.3k
/
OverridePathBasedReferentialIntegrityForDeletesInterceptor.java
135 lines (121 loc) · 4.64 KB
/
OverridePathBasedReferentialIntegrityForDeletesInterceptor.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/*-
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2024 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http:https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package ca.uhn.fhir.jpa.interceptor;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.fhirpath.IFhirPath;
import ca.uhn.fhir.interceptor.api.Hook;
import ca.uhn.fhir.interceptor.api.Interceptor;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.model.DeleteConflict;
import ca.uhn.fhir.jpa.api.model.DeleteConflictList;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.hl7.fhir.instance.model.api.IBaseReference;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
/**
* This JPA interceptor can be configured with a collection of FHIRPath expressions, and will disable
* referential integrity for target resources at those paths.
* <p>
* For example, suppose this interceptor is configured with a path of <code>AuditEvent.entity.what</code>,
* and an AuditEvent resource exists in the repository that has a reference in that path to resource
* <code>Patient/123</code>. Normally this reference would prevent the Patient resource from being deleted unless
* the AuditEvent was first deleted as well (or a <a href="/hapi-fhir/docs/server_jpa/configuration.html#cascading-deletes">cascading delete</a> was used).
* With this interceptor in place, the Patient resource could be deleted, and the AuditEvent would remain intact.
* </p>
*/
@Interceptor
public class OverridePathBasedReferentialIntegrityForDeletesInterceptor {
private static final Logger ourLog =
LoggerFactory.getLogger(OverridePathBasedReferentialIntegrityForDeletesInterceptor.class);
private final Set<String> myPaths = new HashSet<>();
@Autowired
private FhirContext myFhirContext;
@Autowired
private DaoRegistry myDaoRegistry;
/**
* Constructor
*/
public OverridePathBasedReferentialIntegrityForDeletesInterceptor() {
super();
}
/**
* Adds a FHIRPath expression indicating a resource path that should be ignored when considering referential
* integrity for deletes.
*
* @param thePath The FHIRPath expression, e.g. <code>AuditEvent.agent.who</code>
*/
public void addPath(String thePath) {
getPaths().add(thePath);
}
/**
* Remove all paths registered to this interceptor
*/
public void clearPaths() {
getPaths().clear();
}
/**
* Returns the paths that will be considered by this interceptor
*
* @see #addPath(String)
*/
public Set<String> getPaths() {
return myPaths;
}
/**
* Interceptor hook method. Do not invoke directly.
*/
@Hook(
value = Pointcut.STORAGE_PRESTORAGE_DELETE_CONFLICTS,
order = CascadingDeleteInterceptor.OVERRIDE_PATH_BASED_REF_INTEGRITY_INTERCEPTOR_ORDER)
public void handleDeleteConflicts(DeleteConflictList theDeleteConflictList, RequestDetails requestDetails) {
for (DeleteConflict nextConflict : theDeleteConflictList) {
ourLog.info(
"Ignoring referential integrity deleting {} - Referred to from {} at path {}",
nextConflict.getTargetId(),
nextConflict.getSourceId(),
nextConflict.getSourcePath());
IdDt sourceId = nextConflict.getSourceId();
IdDt targetId = nextConflict.getTargetId();
String targetIdValue = targetId.toVersionless().getValue();
IBaseResource sourceResource =
myDaoRegistry.getResourceDao(sourceId.getResourceType()).read(sourceId, requestDetails);
IFhirPath fhirPath = myFhirContext.newFhirPath();
for (String nextPath : myPaths) {
List<IBaseReference> selections = fhirPath.evaluate(sourceResource, nextPath, IBaseReference.class);
for (IBaseReference nextSelection : selections) {
String selectionTargetValue =
nextSelection.getReferenceElement().toVersionless().getValue();
if (Objects.equals(targetIdValue, selectionTargetValue)) {
theDeleteConflictList.setResourceIdToIgnoreConflict(nextConflict.getTargetId());
break;
}
}
}
}
}
}